OpenGLES-02 繪制基本圖元(點(diǎn)开仰、線拟枚、三角形)

在繪制之前,我們需要了解下面的知識(shí):

一众弓、渲染管線
下圖中展示整個(gè)OpenGL ES 2.0可編程渲染管線

渲染管線.png

圖中Vertex Shader和Fragment Shader 是可編程管線恩溅;

1).Vertex Array/Buffer objects
頂點(diǎn)數(shù)據(jù)來源,這時(shí)渲染管線的頂點(diǎn)輸入田轧,通常使用 Buffer objects效率更好暴匠。

2).Vertex Shader
頂點(diǎn)著色器通過矩陣變換位置、計(jì)算照明公式來生成逐頂點(diǎn)顏色已經(jīng)生成或變換紋理坐標(biāo)等基于頂點(diǎn)的操作傻粘。

3).Primitive Assembly
圖元裝配經(jīng)過著色器處理之后的頂點(diǎn)在圖片裝配階段被裝配為基本圖元每窖。OpenGL ES 支持三種基本圖元:點(diǎn),線和三角形弦悉,它們是可被 OpenGL ES 渲染的窒典。接著對(duì)裝配好的圖元進(jìn)行裁剪(clip):保留完全在視錐體中的圖元,丟棄完全不在視錐體中的圖元稽莉,對(duì)一半在一半不在的圖元進(jìn)行裁剪瀑志;接著再對(duì)在視錐體中的圖元進(jìn)行剔除處理(cull):這個(gè)過程可編碼來決定是剔除正面,背面還是全部剔除污秆。

4).Rasterization
光柵化劈猪。在光柵化階段,基本圖元被轉(zhuǎn)換為二維的片元(fragment)良拼,fragment 表示可以被渲染到屏幕上的像素战得,它包含位置,顏色庸推,紋理坐標(biāo)等信息常侦,這些值是由圖元的頂點(diǎn)信息進(jìn)行插值計(jì)算得到的浇冰。這些片元接著被送到片元著色器中處理。這是從頂點(diǎn)數(shù)據(jù)到可渲染在顯示設(shè)備上的像素的質(zhì)變過程聋亡。

5).Fragment Shader
片元著色器通過可編程的方式實(shí)現(xiàn)對(duì)每個(gè)片元的操作肘习。在這一階段它接受光柵化處理之后的fragment,color坡倔,深度值漂佩,模版值作為輸入,片元著色器可以拋棄片元致讥,也可以生成一個(gè)或多個(gè)顏色值作為輸出仅仆。

6).逐片段操作

逐片段操作.png

1.像素歸屬測(cè)試(Pixel Ownership Test):這一步驟由OpenGL ES內(nèi)部進(jìn)行,不由開發(fā)人員控制垢袱;測(cè)試確定幀緩沖區(qū)的位置的像素是否歸屬當(dāng)前OpenGL ES所有墓拜,如不屬于或被另一個(gè)窗口遮擋,從而完全不顯示這些像素请契。

2.裁剪測(cè)試(Scissor Test):判斷像素是否在由 glScissor 定義的剪裁矩形內(nèi)咳榜,不在該剪裁區(qū)域內(nèi)的像素就會(huì)被剪裁掉;

3.模板和深度測(cè)試(Stencil And Depth Test):測(cè)試輸入片段的模板和深度值上進(jìn)行爽锥,以確定片段是否應(yīng)該被拒絕涌韩;深度測(cè)試比較下一個(gè)片段與幀緩沖區(qū)中的片段的深度,從而決定哪一個(gè)像素在前面氯夷,哪一個(gè)像素被遮擋臣樱;

4.混合(Blending):是將片段的顏色和幀緩沖區(qū)中已有的顏色值進(jìn)行混合,并將混合所得的新值寫入幀緩沖腮考;

5.抖動(dòng)(Dithering):可用于最小化因?yàn)槭褂糜邢蘧仍趲彌_區(qū)中保存顏色值而產(chǎn)生的偽像雇毫。

6.To Framebuffer:這是流水線的最后一個(gè)階段,F(xiàn)ramebuffer 中存儲(chǔ)這可以用于渲染到屏幕或紋理中的像素值踩蔚,也可以從Framebuffer 中讀回像素值棚放,但不能讀取其他值(如深度值,模版值等)馅闽。

注:以上渲染管線資料來自http://www.cnblogs.com/edisongz/p/6918428.html

二飘蚯、頂點(diǎn)著色器 Vertex Shader
下面來仔細(xì)看看頂點(diǎn)著色器:

頂點(diǎn)著色器.png

頂點(diǎn)著色器接收的輸入:

Attributes:由 vertext array 提供的頂點(diǎn)數(shù)據(jù),如空間位置福也,法向量局骤,紋理坐標(biāo)以及頂點(diǎn)顏色,它是針對(duì)每一個(gè)頂點(diǎn)的數(shù)據(jù)暴凑。屬性只在頂點(diǎn)著色器中才有峦甩,片元著色器中沒有屬性。屬性可以理解為針對(duì)每一個(gè)頂點(diǎn)的輸入數(shù)據(jù)搬设。OpenGL ES 2.0 規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大屬性個(gè)數(shù)不能少于 8 個(gè)穴店。

Uniforms:uniforms保存由應(yīng)用程序傳遞給著色器的只讀常量數(shù)據(jù)。在頂點(diǎn)著色器中拿穴,這些數(shù)據(jù)通常是變換矩陣泣洞,光照參數(shù),顏色等默色。由 uniform 修飾符修飾的變量屬于全局變量球凰,該全局性對(duì)頂點(diǎn)著色器與片元著色器均可見,也就是說腿宰,這兩個(gè)著色器如果被連接到同一個(gè)應(yīng)用程序中呕诉,它們共享同一份 uniform 全局變量集。因此如果在這兩個(gè)著色器中都聲明了同名的 uniform 變量吃度,要保證這對(duì)同名變量完全相同:同名+同類型甩挫,因?yàn)樗鼈儗?shí)際是同一個(gè)變量。此外椿每,uniform 變量存儲(chǔ)在常量存儲(chǔ)區(qū)伊者,因此限制了 uniform 變量的個(gè)數(shù),OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大頂點(diǎn)著色器 uniform 變量個(gè)數(shù)不能少于 128 個(gè)间护,最大的片元著色器 uniform 變量個(gè)數(shù)不能少于 16 個(gè)亦渗。

Samplers:一種特殊的 uniform,用于呈現(xiàn)紋理汁尺。sampler 可用于頂點(diǎn)著色器和片元著色器法精。

Shader program:由 main 申明的一段程序源碼,描述在頂點(diǎn)上執(zhí)行的操作:如坐標(biāo)變換痴突,計(jì)算光照公式來產(chǎn)生 per-vertex 顏色或計(jì)算紋理坐標(biāo)搂蜓。

頂點(diǎn)著色器的輸出:

Varying:varying 變量用于存儲(chǔ)頂點(diǎn)著色器的輸出數(shù)據(jù),當(dāng)然也存儲(chǔ)片元著色器的輸入數(shù)據(jù)苞也,varying 變量最終會(huì)在光柵化處理階段被線性插值洛勉。頂點(diǎn)著色器如果聲明了 varying 變量,它必須被傳遞到片元著色器中才能進(jìn)一步傳遞到下一階段如迟,因此頂點(diǎn)著色器中聲明的 varying 變量都應(yīng)在片元著色器中重新聲明同名同類型的 varying 變量收毫。OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大 varying 變量個(gè)數(shù)不能少于 8 個(gè)。

在頂點(diǎn)著色器階段至少應(yīng)輸出位置信息-即內(nèi)建變量:gl_Position殷勘,其它兩個(gè)可選的變量為:gl_FrontFacing 和 gl_PointSize此再。

三、片元著色器 Fragment Shader

接下來仔細(xì)看看片元著色器:

片元著色器.png

片元管理器接受如下輸入:

Varyings:這個(gè)在前面已經(jīng)講過了玲销,頂點(diǎn)著色器階段輸出的 varying 變量在光柵化階段被線性插值計(jì)算之后輸出到片元著色器中作為它的輸入输拇,即上圖中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord贤斜。OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大 varying 變量個(gè)數(shù)不能少于 8 個(gè)策吠。

Uniforms:前面也已經(jīng)講過逛裤,這里是用于片元著色器的常量,如霧化參數(shù)猴抹,紋理參數(shù)等带族;OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大的片元著色器 uniform 變量個(gè)數(shù)不能少于 16 個(gè)。

Samples:一種特殊的 uniform蟀给,用于呈現(xiàn)紋理蝙砌。

Shader program:由 main 申明的一段程序源碼,描述在片元上執(zhí)行的操作跋理。

在頂點(diǎn)著色器階段只有唯一的 varying 輸出變量-即內(nèi)建變量:gl_FragColor择克。

四,頂點(diǎn)著色與片元著色在編程上的差異

1前普,精度上的差異

著色語言定了三種級(jí)別的精度:lowp, mediump, highp肚邢。我們可以在 glsl 腳本文件的開頭定義默認(rèn)的精度。如下代碼定義在 float 類型默認(rèn)使用 highp 級(jí)別的精度

precision highp float;
在頂點(diǎn)著色階段拭卿,如果沒有用戶自定義的默認(rèn)精度道偷,那么 int 和 float 都默認(rèn)為 highp 級(jí)別;而在片元著色階段记劈,如果沒有用戶自定義的默認(rèn)精度勺鸦,那么就真的沒有默認(rèn)精度了,我們必須在每個(gè)變量前放置精度描述符目木。此外换途,OpenGL ES 2.0 標(biāo)準(zhǔn)也沒有強(qiáng)制要求所有實(shí)現(xiàn)在片元階段都支持 highp 精度的。我們可以通過查看是否定義 GL_FRAGMENT_PRECISION_HIGH 來判斷具體實(shí)現(xiàn)是否在片元著色器階段支持 highp 精度刽射,從而編寫出可移植的代碼军拟。當(dāng)然,通常我們不需要在片元著色器階段使用 highp 級(jí)別的精度誓禁,推薦的做法是先使用 mediump 級(jí)別的精度懈息,只有在效果不夠好的情況下再考慮 highp 精度。

2摹恰,attribute 修飾符只可用于頂點(diǎn)著色辫继。這個(gè)前面已經(jīng)說過了。

3俗慈,或由于精度的不同姑宽,或因?yàn)榫幾g優(yōu)化的原因,在頂點(diǎn)著色和片元著色階段同樣的計(jì)算可能會(huì)得到不同的結(jié)果闺阱,這會(huì)導(dǎo)致一些問題(z-fighting)炮车。因此 glsl 引入了 invariant 修飾符來修飾在兩個(gè)著色階段的同一變量,確保同樣的計(jì)算會(huì)得到相同的值。

注:以上關(guān)于頂點(diǎn)著色器和片元著色器資料來自 http://www.cnblogs.com/kesalin/archive/2012/11/25/opengl_es_tutorial_02.html

五瘦穆,使用頂點(diǎn)著色器與片元著色器

好了纪隙,理論知識(shí)講得足夠多了,下面我們來看看如何在代碼中添加頂點(diǎn)著色器與片元著色器扛或。我們?cè)谇耙黄恼隆禣penGLES-01 渲染第一步》代碼的基礎(chǔ)上進(jìn)行編碼瘫拣。在前面提到可編程管線通過用 shader 語言編寫腳本文件實(shí)現(xiàn)的,這些腳本文件相當(dāng)于 C 源碼告喊,有源碼就需要編譯鏈接,因此需要對(duì)應(yīng)的編譯器與鏈接器派昧,shader 對(duì)象與 program 對(duì)象就相當(dāng)于編譯器與鏈接器黔姜。shader 對(duì)象載入源碼,然后編譯成 object 形式(就像C源碼編譯成 .obj文件)蒂萎。經(jīng)過編譯的 shader 就可以裝配到 program 對(duì)象中秆吵,每個(gè) program對(duì)象必須裝配兩個(gè) shader 對(duì)象:一個(gè)頂點(diǎn) shader,一個(gè)片元 shader五慈,然后 program 對(duì)象被連接成“可執(zhí)行文件”纳寂,這樣就可以在 render 中是由該“可執(zhí)行文件”了。

1.首先泻拦,我們創(chuàng)建頂點(diǎn)著色器腳本文件

創(chuàng)建VertexShader腳本文件.png

然后命名為:VertexShader.glsl ,(glsl:gl shader language)話說這樣命名才能有代碼提示和校驗(yàn)毙芜,然而我沒體驗(yàn)到提示和校驗(yàn)。


命名.png

編輯文件內(nèi)容如下:

attribute vec4 vPosition; 
 
void main(void)
{
    gl_Position = vPosition;
}

頂點(diǎn)著色腳本的源碼很簡(jiǎn)單争拐,如果你仔細(xì)閱讀了前面的介紹腋粥,就一目了然。 attribute 屬性 vPosition 表示從應(yīng)用程序輸入的類型為 vec4 的位置信息架曹,輸出內(nèi)建 vary 變量 vPosition隘冲。留意:這里使用了默認(rèn)的精度(highp)。

2.創(chuàng)建片元著色器腳本文件
創(chuàng)建方式如1绑雄,命名為FragmentShader.glsl,然后編輯其內(nèi)容如下:

precision mediump float;

void main()
{
    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}

片元著色腳本源碼也很簡(jiǎn)單,前面說過片元著色要么自己定義默認(rèn)精度,要么在每個(gè)變量前添加精度描述符崔涂,在這里自定義 float 的精度為 mediump邢享。然后為內(nèi)建輸出變量 gl_FragColor 指定為綠色。 (故意亮瞎你的眼脚粟,請(qǐng)自行修改靡砌,此外,提下人對(duì)顏色的敏感度珊楼,人對(duì)綠色的敏感度比紅和藍(lán)都要高通殃,所以16位的顏色數(shù)據(jù)里,紅綠藍(lán)占比為5:6:5)。

3.編寫工具類GLESUtils文件來使用shader腳本文件
首先創(chuàng)建一個(gè)GLESUtils類集成NSObject画舌,修改.h為:

#import <Foundation/Foundation.h>
#include <OpenGLES/ES3/gl.h>

@interface GLESUtils : NSObject

// Create a shader object, load the shader source string, and compile the shader.
//
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString;

+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath;

@end

然后在.m中添加如下函數(shù):

+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath
{
    NSError* error;
    NSString* shaderString = [NSString stringWithContentsOfFile:shaderFilepath
                                                       encoding:NSUTF8StringEncoding
                                                          error:&error];
    if (!shaderString) {
        NSLog(@"Error: loading shader file: %@ %@", shaderFilepath, error.localizedDescription);
        return 0;
    }
    
    return [self loadShader:type withString:shaderString];
}

+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString
{
    // Create the shader object
    GLuint shader = glCreateShader(type);
    if (shader == 0) {
        NSLog(@"Error: failed to create shader.");
        return 0;
    }
    
    // Load the shader source
    const char * shaderStringUTF8 = [shaderString UTF8String];
    glShaderSource(shader, 1, &shaderStringUTF8, NULL);
    
    // Compile the shader
    glCompileShader(shader);
    
    // Check the compile status
    GLint compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    
    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
        
        if (infoLen > 1) {
            char * infoLog = malloc(sizeof(char) * infoLen);
            glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
            NSLog(@"Error compiling shader:\n%s\n", infoLog );
            
            free(infoLog);
        }
        
        glDeleteShader(shader);
        return 0;
    }
    
    return shader;
}

工具類GLESUtils中用兩個(gè)類方法實(shí)現(xiàn)對(duì)shader腳本文件的創(chuàng)建堕担、裝載和編譯,接下來詳細(xì)介紹每個(gè)步驟:
1)曲聂,創(chuàng)建/刪除 shader

函數(shù) glCreateShader 用來創(chuàng)建 shader霹购,參數(shù) GLenum type 表示我們要處理的 shader 類型,它可以是 GL_VERTEX_SHADER 或 GL_FRAGMENT_SHADER朋腋,分別表示頂點(diǎn) shader 或 片元 shader齐疙。它返回一個(gè)句柄指向創(chuàng)建好的 shader 對(duì)象。

函數(shù) glDeleteShader 用來銷毀 shader旭咽,參數(shù)為 glCreateShader 返回的 shader 對(duì)象句柄贞奋。

2),裝載 shader

函數(shù) glShaderSource 用來給指定 shader 提供 shader 源碼穷绵。第一個(gè)參數(shù)是 shader 對(duì)象的句柄轿塔;第二個(gè)參數(shù)表示 shader 源碼字符串的個(gè)數(shù);第三個(gè)參數(shù)是 shader 源碼字符串?dāng)?shù)組仲墨;第四個(gè)參數(shù)一個(gè) int 數(shù)組勾缭,表示每個(gè)源碼字符串應(yīng)該取用的長(zhǎng)度,如果該參數(shù)為 NULL目养,表示假定源碼字符串是 \0 結(jié)尾的俩由,讀取該字符串的內(nèi)容指定 \0 為止作為源碼,如果該參數(shù)不是 NULL癌蚁,則讀取每個(gè)源碼字符串中前 length(與每個(gè)字符串對(duì)應(yīng)的 length)長(zhǎng)度個(gè)字符作為源碼采驻。

3),編譯 shader

函數(shù) glCompileShader 用來編譯指定的 shader 對(duì)象匈勋,這將編譯存儲(chǔ)在 shader 對(duì)象中的源碼礼旅。我們可以通過函數(shù) glGetShaderiv 來查詢 shader 對(duì)象的信息,如本例中查詢編譯情況洽洁,此外還可以查詢 GL_DELETE_STATUS痘系,GL_INFO_LOG_STATUS,GL_SHADER_SOURCE_LENGTH 和 GL_SHADER_TYPE饿自。在這里我們查詢編譯情況汰翠,如果返回 0,表示編譯出錯(cuò)了昭雌,錯(cuò)誤信息會(huì)寫入 info 日志中复唤,我們可以查詢?cè)?info 日志,從而獲得錯(cuò)誤信息烛卧。

六佛纫、準(zhǔn)備繪制
回到我們的MyGLView,添加下面兩個(gè)成員變量:

GLuint _programHandle;
GLuint _positionSlot;

再添加如下函數(shù)配置program

- (void)setupProgram
{
    // Load shaders
    //
    NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
                                                                  ofType:@"glsl"];
    NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
                                                                    ofType:@"glsl"];
    GLuint vertexShader = [GLESUtils loadShader:GL_VERTEX_SHADER
                                   withFilepath:vertexShaderPath]; 
    GLuint fragmentShader = [GLESUtils loadShader:GL_FRAGMENT_SHADER
                                     withFilepath:fragmentShaderPath];

    // Create program, attach shaders.
    _programHandle = glCreateProgram();
    if (!_programHandle) {
        NSLog(@"Failed to create program.");
        return;
    }
    
    glAttachShader(_programHandle, vertexShader);
    glAttachShader(_programHandle, fragmentShader);
    
    // Link program
    //
    glLinkProgram(_programHandle);
    
    // Check the link status
    GLint linked;
    glGetProgramiv(_programHandle, GL_LINK_STATUS, &linked );
    if (!linked) 
    {
        GLint infoLen = 0;
        glGetProgramiv (_programHandle, GL_INFO_LOG_LENGTH, &infoLen );
        
        if (infoLen > 1)
        {
            char * infoLog = malloc(sizeof(char) * infoLen);
            glGetProgramInfoLog (_programHandle, infoLen, NULL, infoLog );
            NSLog(@"Error linking program:\n%s\n", infoLog );            
            
            free (infoLog );
        }
        
        glDeleteProgram(_programHandle);
        _programHandle = 0;
        return;
    }
    
    glUseProgram(_programHandle);
    
    // Get attribute slot from program
    //
    _positionSlot = glGetAttribLocation(_programHandle, "vPosition");
}

有了前面的介紹,上面的代碼很容易理解。首先我們是由 GLESUtils 提供的輔助方法從前面創(chuàng)建的腳本中創(chuàng)建呈宇,裝載和編譯頂點(diǎn) shader 和片元 shader好爬;然后我們創(chuàng)建 program,將頂點(diǎn) shader 和片元 shader 裝配到 program 對(duì)象中甥啄,再使用 glLinkProgram 將裝配的 shader 鏈接起來存炮,這樣兩個(gè) shader 就可以合作干活了。注意:鏈接過程會(huì)對(duì) shader 進(jìn)行可鏈接性檢查蜈漓,也就是前面說到同名變量必須同名同型以及變量個(gè)數(shù)不能超出范圍等檢查穆桂。我們?nèi)绾螜z查 shader 編譯情況一樣融虽,對(duì) program 的鏈接情況進(jìn)行檢查享完。如果一切正確,那我們就可以調(diào)用 glUseProgram 激活 program 對(duì)象從而在 render 中使用它句狼。通過調(diào)用 glGetAttribLocation 我們獲取到 shader 中定義的變量 vPosition 在 program 的槽位,通過該槽位我們就可以對(duì) vPosition 進(jìn)行操作秘遏。

接下來我們?cè)趇nitWithFrame方法里調(diào)用此方法(在render方法前):

-(instancetype)initWithFrame:(CGRect)frame{
    if (self==[super initWithFrame:frame]) {
        [self setupLayer];
        [self setupContext];
        [self setupRenderBuffer];
        [self setupFrameBuffer];
        [self setupProgram];  //配置program
        [self render];
    }
    
    return self;
}

以上都是繪制前的準(zhǔn)備工作丘薛,接下來開始繪制:

七、開始繪制
修改render方法里的代碼:

-(void)render
{
    //設(shè)置清屏顏色,默認(rèn)是黑色邦危,如果你的運(yùn)行結(jié)果是黑色洋侨,問題就可能在這兒
    glClearColor(0.3, 0.5, 0.8, 1.0);
    /*
    glClear指定清除的buffer
    共可設(shè)置三個(gè)選項(xiàng)GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT
    也可組合如:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    這里我們只用了color buffer倦蚪,所以只需清除GL_COLOR_BUFFER_BIT
     */
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Setup viewport
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    //Draw Point
    GLfloat pointVertices[] = {
        0.0f,  0.8f, 0.0f,
        0.1f, 0.8f, 0.0f,
        0.2f,  0.8f, 0.0f,
        0.2f,  0.7f, 0.0f
    };
    //loadData
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, pointVertices );
    glEnableVertexAttribArray(_positionSlot);
    glDrawArrays(GL_POINTS, 0, 4); //draw
    
    
    //Draw Line
    GLfloat lineVertices[] = {
        0.0f,  0.6f, 0.0f,
        -0.1f, 0.6f, 0.0f,
        0.2f,  0.6f, 0.0f,
        0.2f,  0.5f, 0.0f
    };
    //loadData
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );
    glEnableVertexAttribArray(_positionSlot);
    glDrawArrays(GL_LINES, 0, 4); //draw
//    glDrawArrays(GL_LINE_LOOP, 0, 4);
//    glDrawArrays(GL_LINE_STRIP, 0, 4);
    
    

    
      // Draw triangle
    GLfloat triangleVertices[] = {
        -0.5f,  0.4f, 0.0f,
         0.5f,  0.4f, 0.0f,
        -0.5f, -0.4f, 0.0f,
         0.5f, -0.4f, 0.0f
    };
    //loadData
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, triangleVertices );
    glEnableVertexAttribArray(_positionSlot);
 //   glDrawArrays(GL_TRIANGLES, 0, 4);  //這里若想畫出2個(gè)三角形希坚,還得添加2個(gè)頂點(diǎn),請(qǐng)自行添加(012陵且,345)裁僧。
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    
    
    [_context presentRenderbuffer:_renderBuffer];
}

⊙⊙⊙先來介紹下相關(guān)函數(shù):

void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr);

參數(shù) index :為頂點(diǎn)數(shù)據(jù)(如頂點(diǎn),顏色,法線锅知,紋理或點(diǎn)精靈大胁ゼ薄)在著色器程序中的槽位;
參數(shù) size :指定每一種數(shù)據(jù)的組成大小售睹,比如頂點(diǎn)由 x, y, z 3個(gè)組成部分桩警,紋理由 u, v 2個(gè)組成部分;
參數(shù) type :表示每一個(gè)組成部分的數(shù)據(jù)格式昌妹;
參數(shù) normalized : 表示當(dāng)數(shù)據(jù)為法線數(shù)據(jù)時(shí)捶枢,是否需要將法線規(guī)范化為單位長(zhǎng)度,對(duì)于其他頂點(diǎn)數(shù)據(jù)設(shè)置為 GL_FALSE 即可飞崖。如果法線向量已經(jīng)為單位長(zhǎng)度設(shè)置為 GL_FALSE 即可烂叔,這樣可免去不必要的計(jì)算,提升效率固歪;
stride : 表示上一個(gè)數(shù)據(jù)到下一個(gè)數(shù)據(jù)之間的間隔(同樣是以字節(jié)為單位)蒜鸡,OpenGL ES根據(jù)該間隔來從由多個(gè)頂點(diǎn)數(shù)據(jù)混合而成的數(shù)據(jù)塊中跳躍地讀取相應(yīng)的頂點(diǎn)數(shù)據(jù);
ptr :值得注意牢裳,這個(gè)參數(shù)是個(gè)多面手(后面再提)逢防。這里它指向 CPU 內(nèi)存中的頂點(diǎn)數(shù)據(jù)數(shù)組。

glEnableVertexAttribArray(); 允許使用頂點(diǎn)數(shù)據(jù)

1.glViewport 表示渲染 surface 將在屏幕上的哪個(gè)區(qū)域呈現(xiàn)出來,請(qǐng)自己修改其參數(shù)運(yùn)行以便理解蒲讯。
2.我們構(gòu)造了點(diǎn)忘朝、線、三角形的頂點(diǎn)數(shù)據(jù)(vertices)判帮,然后繪制出來局嘁。
3.關(guān)于繪制點(diǎn),若就以上圖代碼晦墙,繪制出來的點(diǎn)會(huì)很小悦昵,可能你會(huì)看不見,這時(shí)晌畅,我們?cè)陧旤c(diǎn)著色器中添加:

gl_PointSize = 10.0;   //只能是float

就會(huì)讓點(diǎn)變大旱捧。
4.關(guān)于繪制線,繪制線有3種選項(xiàng)踩麦,分別為GL_LINES枚赡、GL_LINE_LOOP、GL_LINE_STRIP谓谦。

Line Strip , 指首尾相接的線段贫橙,第一條線和最后一條線沒有連接在一起;
Line Loops, 指首尾相接的線段反粥,第一條線和最后一條線連接在一起卢肃,即閉合的曲線疲迂;

線元.png

5.關(guān)于繪制三角形,繪制三角形也有三種選項(xiàng)莫湘,分別為GL_TRIANGLES尤蒿、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN幅垮。

Triangle Strip, 指條帶腰池,相互連接的三角形
Triangle Fan, 指扇面,相互連接的三角形

三角圖元.png

最后運(yùn)行結(jié)果如下:

運(yùn)行結(jié)果.png

所有教程代碼在此 : https://github.com/qingmomo/iOS-OpenGLES-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忙芒,一起剝皮案震驚了整個(gè)濱河市示弓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呵萨,老刑警劉巖奏属,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異潮峦,居然都是意外死亡囱皿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門忱嘹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘱腥,“玉大人,你說我怎么就攤上這事德谅〉鳎” “怎么了萨螺?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵窄做,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我慰技,道長(zhǎng)椭盏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任吻商,我火速辦了婚禮掏颊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艾帐。我一直安慰自己乌叶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布柒爸。 她就那樣靜靜地躺著准浴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捎稚。 梳的紋絲不亂的頭發(fā)上乐横,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天求橄,我揣著相機(jī)與錄音,去河邊找鬼葡公。 笑死罐农,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的催什。 我是一名探鬼主播涵亏,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蛆楞!你這毒婦竟也來了溯乒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤豹爹,失蹤者是張志新(化名)和其女友劉穎裆悄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體臂聋,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡光稼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了孩等。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艾君。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肄方,靈堂內(nèi)的尸體忽然破棺而出冰垄,到底是詐尸還是另有隱情,我是刑警寧澤权她,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布虹茶,位于F島的核電站,受9級(jí)特大地震影響隅要,放射性物質(zhì)發(fā)生泄漏蝴罪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一步清、第九天 我趴在偏房一處隱蔽的房頂上張望要门。 院中可真熱鬧,春花似錦廓啊、人聲如沸欢搜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炒瘟。三九已至,卻和暖如春书聚,著一層夾襖步出監(jiān)牢的瞬間唧领,已是汗流浹背藻雌。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斩个,地道東北人胯杭。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像受啥,于是被迫代替她去往敵國(guó)和親做个。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344