OpenGL ES《一》钻注,你好蚂且,三角形

在前一節(jié),我們對OpenGL進行概述和其中一些專有名詞幅恋⌒铀溃或許有些人剛開始對于接觸這些名詞,會有一頭霧水的感覺捆交,這是正常淑翼。
現(xiàn)在我們通過創(chuàng)建第一個項目,在屏幕中繪制出一個三角形品追,來理解這些名詞玄括。

我使用的iOS框架中的GLKViewController來繪制三角形,這個Controller已經(jīng)將OpenGL所需要的環(huán)境和變量進行了一次封裝肉瓦,可以方便開發(fā)者使用遭京。如果需要用復雜的并且可以控制的,也可以選擇用普通的View來轉(zhuǎn)化為OpenGL需要使用的環(huán)境泞莉,自己控制每一過程哪雕。

構(gòu)建OpenGL環(huán)境

OpenGL es有和我們使用圖像繪制一樣有個管理上下文的類,叫:EAGLContext鲫趁。創(chuàng)建時候會讓我們選擇OpenGL的Api版本斯嚎,我們這里選擇OpenGLES2

self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    if (!self.context) {
        NSLog(@"初始化失敗");
        exit(1);
    }

設(shè)置視圖環(huán)境和編碼

每個視圖在處理不同的圖片的時候都有著不同的視圖編碼,先進行設(shè)置挨厚,避免后面會有錯誤

    GLKView *view = (GLKView *)self.view;
    [view setContext:self.context]; 
    view.drawableDepthFormat = GLKViewDrawableColorFormatRGBA8888;
    //檢查當前上下文是否是我們穿件的 Context
    if (![EAGLContext setCurrentContext:self.context]) {
        NSLog(@"Failed to set current OpenGL context");
        exit(1);
    }

這里我們就完成了對于OpenGL的環(huán)境的搭建和設(shè)置堡僻,接下來就要開始看看如何創(chuàng)建三角形,創(chuàng)建三角形都需要什么疫剃?

頂點輸入

開始繪制圖形之前苦始,我們必須給OpenGL輸入一些頂點數(shù)據(jù),也就是這個三角形需要在屏幕的什么位置慌申。
OpenGL是一個3D圖形庫陌选,所以我們在OpenGL中指定的所有坐標都是3D坐標(x、y蹄溉、z)咨油。OpenGL不是簡單地把所有的3D坐標變換為屏幕上的2D像素;OpenGL僅當3D坐標在3個軸(x柒爵、y和z)上都為-1.0到1.0的范圍內(nèi)時才處理它役电。
由于我們要創(chuàng)建一個2D的三角形,我們要給三個頂點坐標棉胀,每個頂點都表示一個在3D空間的位置法瑟。我們會將它們以標準化設(shè)備坐標的形式(OpenGL的可見區(qū)域)定義為一個float數(shù)組冀膝。

//頂點數(shù)據(jù)
const GLfloat vertices[] = {
      0.5, -0.5, 0.0f,    1.0f, 0.0f, 0.0f, //右下(x,y,z坐標 + rgb顏色)
     -0.5,  0.5, 0.0f,    0.0f, 1.0f, 0.0f, //左上
     -0.5, -0.5, 0.0f,    0.0f, 0.0f, 1.0f, //左下
};

因為我們要創(chuàng)建的是一個2D的三角形,所以這個三角形的深度都是一樣的霎挟,我們都給0.0f窝剖。

通常深度可以理解為z坐標,它代表一個像素在空間中和你的距離酥夭,如果離你遠就可能被別的像素遮擋赐纱,你就看不到它了,它會被丟棄熬北,以節(jié)省資源疙描。

頂點坐標.png

另外我們可以創(chuàng)建一個索引坐標,用來控制三角形繪制順序讶隐。也可以不需要起胰,不需要的話,在最后的繪制方法需要有所變化巫延。這里我們使用索引來控制待错。和頂點坐標一樣需要創(chuàng)建一個數(shù)組:

const GLuint indices[] = {
    0,1,2
};

定義這樣的頂點數(shù)據(jù)以后,我們會把它作為輸入發(fā)送給圖形渲染管線的第一個處理階段:頂點著色器烈评。它會在GPU上創(chuàng)建內(nèi)存用于儲存我們的頂點數(shù)據(jù)火俄,還要配置OpenGL如何解釋這些內(nèi)存,并且指定其如何發(fā)送給顯卡讲冠。
我們通過頂點緩沖對象 (Vertex Buffer Objects, VBO)管理這個內(nèi)存瓜客,它會在GPU內(nèi)存(通常被稱為顯存)中儲存大量頂點。使用這些緩沖對象的好處是我們可以一次性的發(fā)送一大批數(shù)據(jù)到顯卡上竿开,而不是每個頂點發(fā)送一次谱仪。從CPU把數(shù)據(jù)發(fā)送到顯卡相對較慢,所以只要可能我們都要嘗試盡量一次性發(fā)送盡可能多的數(shù)據(jù)否彩。當數(shù)據(jù)發(fā)送至顯卡的內(nèi)存中后疯攒,頂點著色器幾乎能立即訪問頂點,這是個非沉欣螅快的過程敬尺。

綁定頂點數(shù)據(jù)

頂點緩沖對象是中第一個出現(xiàn)的OpenGL對象。就像OpenGL中的其它對象一樣贴浙,這個緩沖有一個獨一無二的ID:

   //用于跟蹤每個頂點信息的
    GLuint verticesBuffer;
    glGenBuffers(1, &verticesBuffer);//創(chuàng)建一個緩存對象

OpenGL有很多緩沖對象類型砂吞,頂點緩沖對象的緩沖類型是GL_ARRAY_BUFFER。OpenGL允許我們同時綁定多個緩沖崎溃,只要它們是不同的緩沖類型蜻直。我們可以使用glBindBuffer函數(shù)把新創(chuàng)建的緩沖綁定到GL_ARRAY_BUFFER目標上:

//激活緩沖對象. OpenGL有很多緩沖對象類型,頂點緩沖對象的緩沖類型
glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer);

從這一刻起,我們使用的任何(在GL_ARRAY_BUFFER目標上的)緩沖調(diào)用都會用來配置當前綁定的緩沖(頂點數(shù)據(jù)VBO)概而。然后我們可以調(diào)用glBufferData函數(shù)呼巷,它會把之前定義的頂點數(shù)據(jù)復制到緩沖的內(nèi)存中:

    /**
     GL_STATIC_DRAW :數(shù)據(jù)不會或幾乎不會改變。
     GL_DYNAMIC_DRAW:數(shù)據(jù)會被改變很多赎瑰。
     GL_STREAM_DRAW :數(shù)據(jù)每次繪制時都會改變王悍。
     */
    //調(diào)用glBufferData函數(shù),它會把之前定義的頂點數(shù)據(jù)復制到緩沖的內(nèi)存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBufferData是一個專門用來把用戶定義的數(shù)據(jù)復制到當前綁定緩沖的函數(shù)乡范。

  1. 第一個參數(shù)是目標緩沖的類型:頂點緩沖對象當前綁定到GL_ARRAY_BUFFER目標上。
  2. 第二個參數(shù)指定傳輸數(shù)據(jù)的大小(以字節(jié)為單位)啤咽;用一個簡單的sizeof計算出頂點數(shù)據(jù)大小就行晋辆。
  3. 第三個參數(shù)是我們希望發(fā)送的實際數(shù)據(jù)。
  4. 第四個參數(shù)指定了我們希望顯卡如何管理給定的數(shù)據(jù)宇整。它有三種形式:
    • GL_STATIC_DRAW :數(shù)據(jù)不會或幾乎不會改變瓶佳。
    • GL_DYNAMIC_DRAW:數(shù)據(jù)會被改變很多。
    • GL_STREAM_DRAW :數(shù)據(jù)每次繪制時都會改變鳞青。

三角形的位置數(shù)據(jù)不會改變霸饲,每次渲染調(diào)用時都保持原樣,所以它的使用類型最好是GL_STATIC_DRAW臂拓。

索引的使用流程和頂點數(shù)據(jù)的流程一樣厚脉,同樣需要綁定,當是索引綁定的類型不同

//用于跟蹤組成每個三角形的索引信息
    GLuint indicesBuffer;
    glGenBuffers(1, &indicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

鏈接頂點屬性(啟動頂點數(shù)據(jù))

頂點著色器允許我們指定任何以頂點屬性為形式的輸入胶惰。這使其具有很強的靈活性的同時傻工,它還的確意味著我們必須手動指定輸入數(shù)據(jù)的哪一個部分對應(yīng)頂點著色器的哪一個頂點屬性。所以孵滞,我們必須在渲染前指定OpenGL該如何解釋頂點數(shù)據(jù)中捆。
我們的頂點緩沖數(shù)據(jù)會被解析為下面這樣子:


image.png
  • 位置數(shù)據(jù)被儲存為32位(4字節(jié))浮點值。
  • 每個位置包含3個這樣的值坊饶。
  • 在這3個值之間沒有空隙(或其他值)泄伪。這幾個值在數(shù)組中緊密排列(Tightly Packed)。
  • 數(shù)據(jù)中第一個值在緩沖開始的位置匿级。

有了這些信息我們就可以使用glVertexAttribPointer函數(shù)告訴OpenGL該如何解析頂點數(shù)據(jù)(應(yīng)用到逐個頂點屬性上)了:

  //開啟對應(yīng)的頂點屬性(坐標蟋滴,紋理坐標,顏色等)
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribColor);
/**
     為頂點屬性(坐標痘绎,紋理坐標脓杉,顏色等)配置合適的值

     參數(shù)1:GLuint indx 聲明這個屬性的名稱
     參數(shù)2:GLint size定義這個屬性由多少個值組成。譬如說position是由3個GLfloat組成
     參數(shù)3: GLenum type聲明每一個值是什么類型简逮。我們都用了GL_FLOAT
     參數(shù)4:GLboolean normalized , GL_FALSE就好了
     參數(shù)5:GLsizei stride stride的大小球散,描述每個vertex數(shù)據(jù)的大小
     參數(shù)6:const GLvoid* ptr , 數(shù)據(jù)結(jié)構(gòu)的偏移量。從這個結(jié)構(gòu)中哪里開始獲取值散庶。
     
     Position的值在前面蕉堰,所以傳(GLfloat *)NULL + 0進去就可以了凌净。
     而TexCoord是緊接著位置的數(shù)據(jù),而position的大小是3個float的大小屋讶,所以是從(GLfloat *)NULL + 3開始的
     */
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (GLfloat *)NULL + 0);
    glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (GLfloat *)NULL + 3);

這過程的完整代碼

- (void)setupVBOs {
    //用于跟蹤每個頂點信息的
    GLuint verticesBuffer;
    glGenBuffers(1, &verticesBuffer);//創(chuàng)建一個緩存對象
    glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer);//激活緩沖對象. OpenGL有很多緩沖對象類型冰寻,頂點緩沖對象的緩沖類型是GL_ARRAY_BUFFER
    /**
     GL_STATIC_DRAW :數(shù)據(jù)不會或幾乎不會改變。
     GL_DYNAMIC_DRAW:數(shù)據(jù)會被改變很多皿渗。
     GL_STREAM_DRAW :數(shù)據(jù)每次繪制時都會改變斩芭。
     */
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//調(diào)用glBufferData函數(shù),它會把之前定義的頂點數(shù)據(jù)復制到緩沖的內(nèi)存中
    
    //用于跟蹤組成每個三角形的索引信息
    GLuint indicesBuffer;
    glGenBuffers(1, &indicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    //開啟對應(yīng)的頂點屬性(坐標乐疆,紋理坐標划乖,顏色等)
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribColor);
    
    
    /**
     為頂點屬性(坐標,紋理坐標挤土,顏色等)配置合適的值

     參數(shù)1:GLuint indx 聲明這個屬性的名稱
     參數(shù)2:GLint size定義這個屬性由多少個值組成琴庵。譬如說position是由3個GLfloat組成
     參數(shù)3: GLenum type聲明每一個值是什么類型。我們都用了GL_FLOAT
     參數(shù)4:GLboolean normalized , GL_FALSE就好了
     參數(shù)5:GLsizei stride stride的大小仰美,描述每個vertex數(shù)據(jù)的大小
     參數(shù)6:const GLvoid* ptr , 數(shù)據(jù)結(jié)構(gòu)的偏移量迷殿。從這個結(jié)構(gòu)中哪里開始獲取值。
     
     Position的值在前面咖杂,所以傳(GLfloat *)NULL + 0進去就可以了庆寺。
     而TexCoord是緊接著位置的數(shù)據(jù),而position的大小是3個float的大小诉字,所以是從(GLfloat *)NULL + 3開始的
     */
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (GLfloat *)NULL + 0);
    glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (GLfloat *)NULL + 3);   
}

頂點著色器

頂點著色器(Vertex Shader)是幾個可編程著色器中的一個止邮。如果我們打算做渲染的話,現(xiàn)代OpenGL需要我們至少設(shè)置一個頂點和一個片段著色器奏窑。

片段著色器

片段著色器(Fragment Shader)是第二個也是最后一個我們打算創(chuàng)建的用于渲染三角形的著色器导披。片段著色器所做的是計算像素最后的顏色輸出。

這里我們使用iOS系統(tǒng)框架中已經(jīng)構(gòu)建好的編譯類來完成
- (void)setupBaseEffect {
    self.mEffect = [[GLKBaseEffect alloc] init];
//啟動著色器
    [self.mEffect prepareToDraw];
}

在初始化這個類的時候埃唯,系統(tǒng)內(nèi)部就進行了對著色器的編譯撩匕。我們只要對它進行啟用就好。

在iOS的GLKit中有多種基礎(chǔ)的著色器類

  • GLKNamedEffect

    • 提供基于著色器的OpenGL渲染效果的對象的標準界面墨叛。
  • GLKBaseEffect

    • 用于基于著色器的OpenGL渲染中的一個簡單的照明和著色系統(tǒng)止毕。
  • GLKReflectionMapEffect

    • 用于基于著色器的OpenGL渲染的支持反射映射的照明和著色系統(tǒng)。
  • GLKSkyboxEffect

    • 用于基于著色器的OpenGL渲染的一個簡單的天空盒視覺效果漠趁。

繪制三角形

終于到最后一步開始繪制了扁凛。
因為這類是繼承于GLKViewController,它的會自動執(zhí)行一個代理函數(shù)GLKViewDelegate闯传,我們在那進行繪制三角形谨朝。
官方文檔對于這個方法的描述是該方法的語義與drawRect:方法相同,用來繪制view的內(nèi)容的。

#pragma mark - GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
     
    glClearColor(0.3f, 0.6f, 1.0f, 1.0f);//設(shè)置清除顏色
    //把窗口清除為當前顏色 和 清除深度緩沖區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); 

}

重點說下glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

  • 第一個參數(shù):表示繪制的基本圖元類型 GL_POINTS, GL_LINE_STRIP等字币。
  • 第二個參數(shù):參數(shù)count表示使用的EBO(索引緩沖區(qū)對象)中索引元素的個數(shù)
  • 第三個參數(shù):參數(shù)type 表示索引數(shù)據(jù)的數(shù)據(jù)類型则披。必須取 GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, 或者 GL_UNSIGNED_INT 三者之一。
  • 第四個參數(shù):indices 表示EBO中索引的偏移量洗出。

還有需要注意的是千萬不要在繪圖方法中去調(diào)用display方法士复,什么是繪圖方法,比如說UIView的drawRect:(CGRect)rect方法還有glkView:(GLKView *)view drawInRect:(CGRect)rect翩活。因為會循環(huán)調(diào)用阱洪,循環(huán)調(diào)用導致崩潰

完成

三角形.png

如果不使用索引來構(gòu)建三角形?提示:glDrawArrays(GL_TRIANGLES, 0, 3);
如果把三角形變成一個矩形呢菠镇?提示:兩個三角形組成一個矩形
不用索引來構(gòu)建一個矩形冗荸?

四邊形.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市辟犀,隨后出現(xiàn)的幾起案子俏竞,更是在濱河造成了極大的恐慌绸硕,老刑警劉巖堂竟,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異玻佩,居然都是意外死亡出嘹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門咬崔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來税稼,“玉大人,你說我怎么就攤上這事垮斯±善停” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵兜蠕,是天一觀的道長扰肌。 經(jīng)常有香客問我,道長熊杨,這世上最難降的妖魔是什么曙旭? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮晶府,結(jié)果婚禮上桂躏,老公的妹妹穿的比我還像新娘。我一直安慰自己川陆,他們只是感情好剂习,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般进倍。 火紅的嫁衣襯著肌膚如雪土至。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天猾昆,我揣著相機與錄音陶因,去河邊找鬼。 笑死垂蜗,一個胖子當著我的面吹牛楷扬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贴见,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼烘苹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了片部?” 一聲冷哼從身側(cè)響起镣衡,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎档悠,沒想到半個月后廊鸥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡辖所,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年惰说,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缘回。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡吆视,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酥宴,到底是詐尸還是另有隱情啦吧,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布拙寡,位于F島的核電站授滓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏倒庵。R本人自食惡果不足惜褒墨,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擎宝。 院中可真熱鬧郁妈,春花似錦、人聲如沸绍申。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胃碾,卻和暖如春涨享,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仆百。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工厕隧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俄周。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓吁讨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親峦朗。 傳聞我的和親對象是個殘疾皇子建丧,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 什么是實的翎朱,什么是虛的?實的是指搞學術(shù)搞科研好創(chuàng)新尺铣,虛的指講話做人搞人際拴曲,人這一生,總得有一門玩到精迄埃,才是真正精...
    向慢慢閱讀 226評論 0 0
  • 考研報名完畢疗韵,還有五天回濟南兑障。天總有一天會亮侄非!
    大兔子的小妞子閱讀 90評論 0 0
  • 今天是我29歲的生日逞怨,是最后一年可以用2開頭的日子。都說30而立——立家福澡、立業(yè)叠赦。可站在30歲的檔口革砸,你是否也如我一...
    雁紫87閱讀 901評論 15 36
  • ( 一 ) 隆冬冷風算利,憶上心頭 相伴而行册踩,少年害羞 滿腔愛意,壓在...
    流水疊影slx閱讀 234評論 6 1