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

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

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

渲染管線.png

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

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

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

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

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

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

6).逐片段操作

逐片段操作.png

1.像素歸屬測(cè)試(Pixel Ownership Test):這一步驟由OpenGL ES內(nèi)部進(jìn)行,不由開(kāi)發(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)行混合,并將混合所得的新值寫(xiě)入幀緩沖;

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

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

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

二、頂點(diǎn)著色器 Vertex Shader
下面來(lái)仔細(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)著色器中才有,片元著色器中沒(méi)有屬性。屬性可以理解為針對(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)著色器與片元著色器均可見(jiàn),也就是說(shuō),這兩個(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ì)算光照公式來(lái)產(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

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

片元著色器.png

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

Varyings:這個(gè)在前面已經(jīng)講過(guò)了,頂點(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)講過(guò),這里是用于片元著色器的常量,如霧化參數(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,精度上的差異

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

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

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

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

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

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

好了,理論知識(shí)講得足夠多了,下面我們來(lái)看看如何在代碼中添加頂點(diǎn)著色器與片元著色器。我們?cè)谇耙黄恼隆禣penGLES-01 渲染第一步》代碼的基礎(chǔ)上進(jìn)行編碼。在前面提到可編程管線通過(guò)用 shader 語(yǔ)言編寫(xiě)腳本文件實(shí)現(xiàn)的,這些腳本文件相當(dāng)于 C 源碼,有源碼就需要編譯鏈接,因此需要對(duì)應(yīng)的編譯器與鏈接器,shader 對(duì)象與 program 對(duì)象就相當(dāng)于編譯器與鏈接器。shader 對(duì)象載入源碼,然后編譯成 object 形式(就像C源碼編譯成 .obj文件)。經(jīng)過(guò)編譯的 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)話說(shuō)這樣命名才能有代碼提示和校驗(yàn),然而我沒(méi)體驗(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)單,前面說(shuō)過(guò)片元著色要么自己定義默認(rèn)精度,要么在每個(gè)變量前添加精度描述符,在這里自定義 float 的精度為 mediump。然后為內(nèi)建輸出變量 gl_FragColor 指定為綠色。 (故意亮瞎你的眼,請(qǐng)自行修改,此外,提下人對(duì)顏色的敏感度,人對(duì)綠色的敏感度比紅和藍(lán)都要高,所以16位的顏色數(shù)據(jù)里,紅綠藍(lán)占比為5:6:5)。

3.編寫(xiě)工具類GLESUtils文件來(lái)使用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)建、裝載和編譯,接下來(lái)詳細(xì)介紹每個(gè)步驟:
1),創(chuàng)建/刪除 shader

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

函數(shù) glDeleteShader 用來(lái)銷毀 shader,參數(shù)為 glCreateShader 返回的 shader 對(duì)象句柄。

2),裝載 shader

函數(shù) glShaderSource 用來(lái)給指定 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 用來(lái)編譯指定的 shader 對(duì)象,這將編譯存儲(chǔ)在 shader 對(duì)象中的源碼。我們可以通過(guò)函數(shù) glGetShaderiv 來(lái)查詢 shader 對(duì)象的信息,如本例中查詢編譯情況,此外還可以查詢 GL_DELETE_STATUS,GL_INFO_LOG_STATUS,GL_SHADER_SOURCE_LENGTH 和 GL_SHADER_TYPE。在這里我們查詢編譯情況,如果返回 0,表示編譯出錯(cuò)了,錯(cuò)誤信息會(huì)寫(xiě)入 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 鏈接起來(lái),這樣兩個(gè) shader 就可以合作干活了。注意:鏈接過(guò)程會(huì)對(duì) shader 進(jìn)行可鏈接性檢查,也就是前面說(shuō)到同名變量必須同名同型以及變量個(gè)數(shù)不能超出范圍等檢查。我們?nèi)绾螜z查 shader 編譯情況一樣,對(duì) program 的鏈接情況進(jìn)行檢查。如果一切正確,那我們就可以調(diào)用 glUseProgram 激活 program 對(duì)象從而在 render 中使用它。通過(guò)調(diào)用 glGetAttribLocation 我們獲取到 shader 中定義的變量 vPosition 在 program 的槽位,通過(guò)該槽位我們就可以對(duì) vPosition 進(jìn)行操作。

接下來(lái)我們?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)備工作,接下來(lái)開(kāi)始繪制:

七、開(kāi)始繪制
修改render方法里的代碼:

-(void)render
{
    //設(shè)置清屏顏色,默認(rèn)是黑色,如果你的運(yùn)行結(jié)果是黑色,問(wèn)題就可能在這兒
    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);  //這里若想畫(huà)出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];
}

⊙⊙⊙先來(lái)介紹下相關(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ù)該間隔來(lái)從由多個(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)出來(lái),請(qǐng)自己修改其參數(shù)運(yùn)行以便理解。
2.我們構(gòu)造了點(diǎn)、線、三角形的頂點(diǎn)數(shù)據(jù)(vertices),然后繪制出來(lái)。
3.關(guān)于繪制點(diǎn),若就以上圖代碼,繪制出來(lái)的點(diǎn)會(huì)很小,可能你會(huì)看不見(jiàn),這時(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 , 指首尾相接的線段,第一條線和最后一條線沒(méi)有連接在一起;
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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容