Opengl ES 3.0 on iOS--- HelloWord(繪制彩色矩形)

簡(jiǎn)介

本文記錄了我初學(xué)Opengl 繪制彩色矩形的過程,可能我對(duì)內(nèi)容的描述不夠準(zhǔn)確,還請(qǐng)多多指正

實(shí)現(xiàn)

配置圖層

+(Class)layerClass{
    return [CAEAGLLayer class];
}

將當(dāng)前View的Layer替換成 CAEAGLLayer類,opengl的繪制內(nèi)容也是在該View上顯示的.

//設(shè)置不透明度為YES,因?yàn)橥该鲌D層性能不好
self.layer.opaque = YES;
self.layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

可以對(duì)CAEAGLLayer進(jìn)行額外屬性的配置:

kEAGLDrawablePropertyRetainedBacking 傳入布爾值,表示是否保持繪制狀態(tài),若設(shè)置為NO,則下次將重新繪制.
kEAGLDrawablePropertyColorFormat 設(shè)置layer的顏色緩沖區(qū)格式,EAGLContext對(duì)象 使用此格式來(lái)創(chuàng)建渲染緩沖區(qū)的存儲(chǔ).
kEAGLColorFormatRGB565 ---> 16bit RGB格式
kEAGLColorFormatRGBA8 ---> 32-bit RGBA格式

配置上下文

        _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
        if (!_context) {
            return;
        }
        // 將當(dāng)前上下文設(shè)置為我們創(chuàng)建的上下文
        if (![EAGLContext setCurrentContext:_context]) {
            return;
        }
    }

設(shè)置緩沖區(qū)(渲染緩沖和幀緩沖)

    //在緩沖區(qū)中返回n個(gè)渲染緩沖對(duì)象句柄,不保證這些句柄是連續(xù)的整數(shù),但是肯定沒有被使用.
    GLuint renderbuffer[1];
    glGenRenderbuffers(ARRAY_SIZE(renderbuffer), renderbuffer);

    //將緩沖區(qū)對(duì)象和句柄 綁定到指定的緩沖區(qū)目標(biāo).
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[0]);

    //檢驗(yàn)是否創(chuàng)建綁定成功
    if (glIsRenderbuffer(renderbuffer[0]) == GL_TRUE) {
        NSLog(@"成功生成渲染緩存");
    }
    //為緩沖區(qū)對(duì)象分配存儲(chǔ)空間.
    [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
    
    //設(shè)置幀緩沖區(qū)(Frame Buffer),和渲染緩沖區(qū)大致相同
    GLuint framebuffer[1];
    glGenFramebuffers(ARRAY_SIZE(framebuffer), framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer[0]);
    if (glIsFramebuffer(framebuffer[0]) == GL_TRUE) {
        NSLog(@"成功綁定幀緩存");
    }
    
    //將相關(guān)的buffer依附到 幀緩存上 
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[0]);
    
        //釋放渲染緩存
        //glDeleteRenderbuffers(ARRAY_SIZE(renderbuffer), renderbuffer);
        //釋放幀緩存
        //glDeleteFramebuffers(ARRAY_SIZE(framebuffer), framebuffer);

渲染緩存: 是OpenGL ES管理的一塊高效內(nèi)存區(qū)域,渲染緩存的數(shù)據(jù)只有關(guān)聯(lián)一個(gè)幀緩存對(duì)象才有意義,并且需要保證圖像緩存格式 必須與OpenGL ES要求的渲染格式相符.

幀緩存:它是屏幕所顯示畫面的一個(gè)直接映象,又稱為位映射圖(Bit Map)或光柵。幀緩存的每一存儲(chǔ)單元對(duì)應(yīng)屏幕上的一個(gè)像素,整個(gè)幀緩存對(duì)應(yīng)一幀圖像。

函數(shù)解釋

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[0]);
參數(shù):

target: 指定的幀緩沖區(qū)目標(biāo) 必須是 GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, 或 GL_FRAMEBUFFER. (GL_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER);
attachment: 幀緩存對(duì)象依附的目標(biāo) GL_COLOR_ATTACHMENT(0~i) ---> 第i個(gè)顏色緩存 0為默認(rèn)值, GL_DEPTH_ATTACHMENT ---> 深度緩存, GL_STENCIL_ATTACHMENT ---> 模板緩存
renderbuffertarget :必須為 GL_RENDERBUFFER,指定的渲染緩存區(qū)目標(biāo)
renderbuffer: 渲染緩沖區(qū)對(duì)象句柄.

準(zhǔn)備著色器源碼

OpenGL中,任何事物都在3D空間中,而屏幕和窗口卻是2D像素?cái)?shù)組,這導(dǎo)致OpenGL的大部分工作都是關(guān)于把3D坐標(biāo)轉(zhuǎn)變?yōu)檫m應(yīng)你屏幕的2D像素。3D坐標(biāo)轉(zhuǎn)為2D坐標(biāo)的處理過程是由OpenGL的圖形渲染管線.OpenGL中,任何事物都在3D空間中,而屏幕和窗口卻是2D像素?cái)?shù)組,這導(dǎo)致OpenGL的大部分工作都是關(guān)于把3D坐標(biāo)轉(zhuǎn)變?yōu)檫m應(yīng)你屏幕的2D像素。3D坐標(biāo)轉(zhuǎn)為2D坐標(biāo)的處理過程是由OpenGL的圖形渲染管線

著色器(Shader)是運(yùn)行在GPU上的小程序。這些小程序?yàn)閳D形渲染管線的某個(gè)特定部分而運(yùn)行。從基本意義上來(lái)說(shuō),著色器只是一種把輸入轉(zhuǎn)化為輸出的程序。著色器也是一種非常獨(dú)立的程序,因?yàn)樗鼈冎g不能相互通信;它們之間唯一的溝通只有通過輸入和輸出。

著色器是使用一種叫GLSL的類C語(yǔ)言寫成的。GLSL是為圖形計(jì)算量身定制的,它包含一些針對(duì)向量和矩陣操作的有用特性。

頂點(diǎn)著色器

#version 300 es //OpenGL ES 3.0

//接受的輸入變量
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;

//輸出變量
out vec3 outColor;

//相當(dāng)于C語(yǔ)言的main函數(shù)
void main()
{   
    //繪制圖形
    gl_Position = vec4(position[0],position[1],position[2], 1.0);
    
    outColor = color;
}

圖形渲染管線的第一個(gè)部分是頂點(diǎn)著色器(Vertex Shader),它把一個(gè)單獨(dú)的頂點(diǎn)作為輸入.一個(gè)頂點(diǎn)(Vertex)是一個(gè)3D坐標(biāo)的數(shù)據(jù)的集合。而頂點(diǎn)數(shù)據(jù)是用頂點(diǎn)屬性(Vertex Attribute)表示的,它可以包含任何我們想用的數(shù)據(jù).

片段著色器

#version 300 es

precision mediump float;    //表示 數(shù)據(jù)精確度 這里設(shè)置的為中級(jí)

in vec3 outColor;

out vec4 FragColor; //輸出的色彩

void main()
{
    FragColor = vec4(outColor.x,outColor.y,outColor.z, 1.0);;
}

片段著色器的主要目的是計(jì)算一個(gè)像素的最終顏色,這也是所有OpenGL高級(jí)效果產(chǎn)生的地方。通常,片段著色器包含3D場(chǎng)景的數(shù)據(jù)(比如光照、陰影、光的顏色等等),這些數(shù)據(jù)可以被用來(lái)計(jì)算最終像素的顏色

在所有對(duì)應(yīng)顏色值確定以后,最終的對(duì)象將會(huì)被傳到最后一個(gè)階段,我們叫做Alpha測(cè)試混合(Blending)階段。這個(gè)階段檢測(cè)片段的對(duì)應(yīng)的深度(和模板(Stencil))值,用它們來(lái)判斷這個(gè)像素是其它物體的前面還是后面,決定是否應(yīng)該丟棄。這個(gè)階段也會(huì)檢查alpha值(alpha值定義了一個(gè)物體的透明度)并對(duì)物體進(jìn)行混合(Blend)。所以,即使在片段著色器中計(jì)算出來(lái)了一個(gè)像素輸出的顏色,在渲染多個(gè)三角形的時(shí)候最后的像素顏色也可能完全不同。

創(chuàng)建著色器對(duì)象

static GLuint createGLShader(const char *shaderText, GLenum shaderType)
{
    //創(chuàng)建著色器,將根據(jù)傳入的type參數(shù) 創(chuàng)建一個(gè)新的 頂點(diǎn)或片段著色器,返回值為新的著色器對(duì)象句柄
    //GL_VERTEX_SHADER(頂點(diǎn)著色器)     GL_FRAGMENT_SHADER(片段著色器)
    GLuint shader = glCreateShader(shaderType);

    //為著色器對(duì)象 提供著色器源代碼.
    //參數(shù): shader --> 著色器對(duì)象句柄
    //      count --> 著色器源字符串?dāng)?shù)量
    //      string --> 字符串的數(shù)組指針
    //      length ---> 指向保存美工著色器字符串大小且元素?cái)?shù)量為count的整數(shù)數(shù)組指針.如果length為NULL 著色器字符串將被認(rèn)定為空.
    glShaderSource(shader, 1, &shaderText, NULL);

    //調(diào)用該方法,將指定的著色器源代碼 進(jìn)行編譯
    //參數(shù)shader 為著色器句柄
    glCompileShader(shader);

    //調(diào)用該方法獲取 著色器源代碼編譯是否成功,并獲取其他相關(guān)信息
    //第二個(gè)參數(shù) pname 表示要查詢什么信息
    /*
     GL_COMPILE_STATUS ---> 是否編譯成功 成功返回 GL_TRUE
     GL_INFO_LOG_LENGTH ---> 查詢?cè)创a編譯后長(zhǎng)度
     GL_SHADER_SOURCE_LENGTH ---> 查詢?cè)创a長(zhǎng)度
     GL_SHADER_TYPE ---> 查詢著色器類型()
     GL_DELETE_STATUS ---> 著色器是否被標(biāo)記刪除
     */
    int 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 = (char *)malloc(sizeof(char) * infoLen);
            if (infoLog) {


                //檢索信息日志
                //參數(shù): shader 著色器對(duì)象句柄
                //      maxLength 保存信息日志的緩沖區(qū)大小
                //      length 寫入信息日志長(zhǎng)度 ,不需要知道可傳NULL
                //      infoLog 保存日志信息的指針
                glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
                GLlog("Error compiling shader: %s\n", infoLog);
                free(infoLog);
            }
        }
        //刪除著色器對(duì)象, 參數(shù)shader為要?jiǎng)h除的著色器對(duì)象的句柄
        //若一個(gè)著色器鏈接到一個(gè)程序?qū)ο?那么該方法不會(huì)立刻刪除著色器,而是將著色器標(biāo)記為刪除,當(dāng)著色器不在連接到任何程序?qū)ο髸r(shí),它的內(nèi)存將被釋放.
        glDeleteShader(shader);
        return 0;
    }
    
    return shader;
}

創(chuàng)建程序?qū)ο?/h2>
 // 創(chuàng)建一個(gè)程序?qū)ο?返回程序?qū)ο蟮木浔?    GLuint program = glCreateProgram();

    // 得到需要的著色器
    GLuint vertShader = createGLShader(vertext, GL_VERTEX_SHADER);  //頂點(diǎn)著色器
    GLuint fragShader = createGLShader(frag, GL_FRAGMENT_SHADER);   //片元著色器
    
    if (vertShader == 0 || fragShader == 0) {
        return 0;
    }

    //將程序?qū)ο蠛?著色器對(duì)象鏈接  //在ES 3.0中,每個(gè)程序?qū)ο?必須連接一個(gè)頂點(diǎn)著色器和片段著色器
    //program程序?qū)ο缶浔?shader著色器句柄
    glAttachShader(program, vertShader);
    glAttachShader(program, fragShader);


    //鏈接程序?qū)ο?生成可執(zhí)行程序(在著色器已完成編譯 且程序?qū)ο筮B接了著色器)
    //鏈接程序會(huì)檢查各種對(duì)象的數(shù)量,和各種條件.
    //在鏈接階段就是生成最終硬件指令的時(shí)候(和C語(yǔ)言一樣)
    glLinkProgram(program);


    //檢查鏈接是否成功
    GLint success;
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    
    
    if (!success) {
        GLint infoLen;
        //使用 GL_INFO_LOG_LENGTH 表示獲取信息日志
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            GLchar *infoText = (GLchar *)malloc(sizeof(GLchar)*infoLen + 1);
            if (infoText) {
                memset(infoText, 0x00, sizeof(GLchar)*infoLen + 1);

                // 從信息日志中獲取信息
                glGetProgramInfoLog(program, infoLen, NULL, infoText);
                GLlog("%s", infoText);
                free(infoText);

                //此函數(shù)用于校驗(yàn)當(dāng)前的程序?qū)ο?校驗(yàn)結(jié)果可通過 glGetProgramiv函數(shù)檢查,此函數(shù)只用于調(diào)試,因?yàn)樗苈?
                //glValidateProgram(program);
            }
        }
        glDeleteShader(vertShader);
        glDeleteShader(fragShader);

        //刪除程序?qū)ο?        glDeleteProgram(program);
        return 0;
    }
    
    /*
 * 鏈接完著色器,生成可執(zhí)行程序. 將著色器斷開刪除
 */
    //斷開指定程序?qū)ο蠛推沃?    glDetachShader(program, vertShader);
    glDetachShader(program, fragShader);

    //將著色器標(biāo)記為刪除
    glDeleteShader(vertShader);
    glDeleteShader(fragShader);

程序?qū)ο缶褪且粋€(gè)容器對(duì)象,將著色器與之連接,最后鏈接生成最終的可執(zhí)行程序.

輸入頂點(diǎn)數(shù)據(jù)

        //三角形的三點(diǎn)坐標(biāo)+顏色坐標(biāo)
        static GLfloat vertices[] = {
            //點(diǎn)坐標(biāo)                     //顏色
            0.5f,  0.5f, 0.0f,          1.0f, 0.0f, 0.0f,
            0.5f, -0.5f, 0.0f,          0.0f, 1.0f, 0.0f,
            -0.5f, -0.5f, 0.0f,         0.0f, 0.0f, 1.0f,
            -0.5f, 0.5f, 0.0f,          1.0f, 0.0f, 1.0f
        };
        
        static unsigned int indices[] = {
            0,1,3,
            1,2,3
        };
        
        unsigned int VAO,VBO,EBO;
        //創(chuàng)建VAO對(duì)象,VBO對(duì)象,EBO對(duì)象
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        
        //綁定VAO VBO EBO
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        
        將頂點(diǎn)數(shù)據(jù) 和 索引數(shù)據(jù) 復(fù)制到緩沖區(qū)中
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

        //設(shè)置頂點(diǎn)屬性指針 輸入數(shù)據(jù)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)0);
        //激活 0號(hào)變量,為了性能,若不激活著色器無(wú)法接受數(shù)據(jù)
        glEnableVertexAttribArray(0);
        
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
        glEnableVertexAttribArray(1);

VAO VBO EBO

不使用VAO VBO繪制代碼:

    static GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };
    GLint posSlot = glGetAttribLocation(_program, "position");
    glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(posSlot);
    
    static GLfloat colors[] = {
        0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 0.0f
    };
    GLint colorSlot = glGetAttribLocation(_program, "color");
    glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
    glEnableVertexAttribArray(colorSlot);

VBO

如上面的例子所示, 普通的頂點(diǎn)數(shù)組的傳輸,需要在繪制的時(shí)候頻繁地從CPU到GPU傳輸頂點(diǎn)數(shù)據(jù),這種做法效率低下.
為了加快顯示速度,顯卡增加了一個(gè)擴(kuò)展 VBO (Vertex Buffer object),即頂點(diǎn)緩存。它直接在 GPU 中開辟一個(gè)緩存區(qū)域來(lái)存儲(chǔ)頂點(diǎn)數(shù)據(jù),因?yàn)樗怯脕?lái)緩存儲(chǔ)頂點(diǎn)數(shù)據(jù),因此被稱之為頂點(diǎn)緩存。使用頂點(diǎn)緩存能夠大大較少了CPU到GPU 之間的數(shù)據(jù)拷貝開銷,因此顯著地提升了程序運(yùn)行的效率。

函數(shù)

1, 創(chuàng)建頂點(diǎn)緩存對(duì)象
void glGenBuffers (GLsizei n, GLuint* buffers);

參數(shù) n : 表示需要?jiǎng)?chuàng)建頂點(diǎn)緩存對(duì)象的個(gè)數(shù)
參數(shù) buffers :用于存儲(chǔ)創(chuàng)建好的頂點(diǎn)緩存對(duì)象句柄

2, 將頂點(diǎn)緩存對(duì)象設(shè)置為當(dāng)前數(shù)組緩存對(duì)象
void glBindBuffer (GLenum target, GLuint buffer);

target :指定綁定的目標(biāo),取值為 GL_ARRAY_BUFFER(用于頂點(diǎn)數(shù)據(jù)) 或 GL_ELEMENT_ARRAY_BUFFER(用于索引數(shù)據(jù))
buffer :頂點(diǎn)緩存對(duì)象句柄

3, 為頂點(diǎn)緩存對(duì)象分配空間(這里就是將數(shù)據(jù)一次性 拷貝至顯存中)
void glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);

target:指定綁定的目標(biāo),取值為 GL_ARRAY_BUFFER(用于頂點(diǎn)數(shù)據(jù)) 或 GL_ELEMENT_ARRAY_BUFFER(用于索引數(shù)據(jù)).
size :指定頂點(diǎn)緩存區(qū)的大小,以字節(jié)為單位計(jì)數(shù);
data :用于初始化頂點(diǎn)緩存區(qū)的數(shù)據(jù),可以為 NULL,表示只分配空間,之后再由 glBufferSubData 進(jìn)行初始化;
usage :表示該緩存區(qū)域?qū)?huì)被如何使用,它的主要目的是用于對(duì)該緩存區(qū)域做何種程度的優(yōu)化,比如經(jīng)常修改的數(shù)據(jù)可能就會(huì)放在GPU緩存中達(dá)到快速操作的目的.

usage:

GL_STATIC_DRAW  表示該緩存區(qū)不會(huì)被修改
GL_DYNAMIC_DRAW     表示該緩存區(qū)會(huì)被周期性更改
GL_STREAM_DRAW  表示該緩存區(qū)會(huì)被頻繁更改

4,更新頂點(diǎn)緩沖區(qū)數(shù)據(jù)
void glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);

offset: 表示需要更新的數(shù)據(jù)的起始偏移量;
size: 表示需要更新的數(shù)據(jù)的個(gè)數(shù),也是以字節(jié)為計(jì)數(shù)單位;
data: 用于更新的數(shù)據(jù);

5,釋放頂點(diǎn)緩存
void glDeleteBuffers (GLsizei n, const GLuint* buffers);

n : 表示頂點(diǎn)緩存對(duì)象的個(gè)數(shù)
buffers :頂點(diǎn)緩存對(duì)象句柄

VAO

VAO的全名是 Vertex Array Object。它不用作存儲(chǔ)數(shù)據(jù),但它與頂點(diǎn)繪制相關(guān)。
它的定位是狀態(tài)對(duì)象,記錄存儲(chǔ)狀態(tài)信息。VAO記錄的是一次繪制中做需要的信息,這包括數(shù)據(jù)在哪里、數(shù)據(jù)格式是什么等信息。VAO其實(shí)可以看成一個(gè)容器,可以包括多個(gè)VBO。 由于它進(jìn)一步將VBO容于其中,所以繪制效率將在VBO的基礎(chǔ)上更進(jìn)一步。目前OpenGL ES3.0及以上才支持頂點(diǎn)數(shù)組對(duì)象。

圖片.png
函數(shù)

1, 創(chuàng)建頂點(diǎn)數(shù)組對(duì)象
glGenVertexArrays (GLsizei n, GLuint* arrays) ;

n : 表示頂點(diǎn)數(shù)組對(duì)象的個(gè)數(shù)
arrays :頂點(diǎn)數(shù)組對(duì)象句柄

2, 將頂點(diǎn)數(shù)組對(duì)象設(shè)置為當(dāng)前頂點(diǎn)數(shù)組對(duì)象
glBindVertexArray (GLuint array) ;

arrays :頂點(diǎn)數(shù)組對(duì)象句柄

3,釋放頂點(diǎn)數(shù)組對(duì)象
glDeleteVertexArrays (GLsizei n, const GLuint* arrays);

n : 表示頂點(diǎn)數(shù)組對(duì)象的個(gè)數(shù)
arrays :頂點(diǎn)數(shù)組對(duì)象句柄

使用

如代碼中所寫,在綁定VAO后,后續(xù)的VBO操作都會(huì)存儲(chǔ)到當(dāng)前綁定的VAO中.這樣就將當(dāng)前繪制狀態(tài)記錄下來(lái)了. 當(dāng)下次還要繪制當(dāng)前圖形時(shí), 只需再次綁定當(dāng)前VAO, 進(jìn)行后面的繪制操作即可.對(duì)于OpenGL ES2.0 使用VAO 則需要使用另外提供的API來(lái)實(shí)現(xiàn).

GLvoid glGenVertexArraysOES(GLsizei n, GLuint *arrays)
GLvoid glBindVertexArrayOES(GLuint array);
GLvoid glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays);; 
GLboolean glIsVertexArrayOES(GLuint array);

EBO

索引緩沖對(duì)象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO),當(dāng)繪制現(xiàn)有圖形時(shí),存在頂點(diǎn)數(shù)據(jù)復(fù)用時(shí),可以使用EBO.

函數(shù)

1,創(chuàng)建EBO(和VBO類似)

unsigned int EBO;
glGenBuffers(1, &EBO);

2,綁定EBO,將索引復(fù)制到緩沖里

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

3, 使用glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);替代glDrawArrays

函數(shù)補(bǔ)充

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

該函數(shù)用于將頂點(diǎn)屬性傳入頂點(diǎn)著色器
參數(shù):

index: 對(duì)應(yīng)頂你個(gè)點(diǎn)著色器中變量的location
size :表示該頂點(diǎn)屬性對(duì)應(yīng)的分量數(shù)量.也就是接收者為幾位向量 如寫入3 則表示為vec3 接收者為3維向量. 必須是 1~4.
type :表明每個(gè)分量的類型 可用的符號(hào)常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT
normalized: 是否對(duì)每個(gè)分量進(jìn)行歸一化處理, 也就是若type為float類型.
stride:指定連續(xù)頂點(diǎn)屬性之間的偏移量,如果設(shè)置0,則表示各個(gè)分量是緊密排在一起,中間沒有其他多余數(shù)據(jù).
ptr 頂點(diǎn)數(shù)據(jù)指針

此函數(shù)在有無(wú)VBO的情況下,使用有所差異~,在不適用VBO時(shí),ptr確實(shí)是頂點(diǎn)數(shù)據(jù)指針.
當(dāng)使用VBO時(shí),頂點(diǎn)數(shù)據(jù)都已經(jīng)拷貝至顯存中,這里的ptr 就表示為緩沖區(qū)數(shù)據(jù)的便宜量了.

無(wú)EBO:
glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);

有EBO:

static GLfloat vertices[] = {
            //點(diǎn)坐標(biāo)                     //顏色
            0.5f,  0.5f, 0.0f,          1.0f, 0.0f, 0.0f,
            0.5f, -0.5f, 0.0f,          0.0f, 1.0f, 0.0f,
            -0.5f, -0.5f, 0.0f,         0.0f, 0.0f, 1.0f,
            -0.5f, 0.5f, 0.0f,          1.0f, 0.0f, 1.0f
        };
.......(VBO與其他代碼).......

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));

在這里0號(hào)屬性和1號(hào)屬性緊密相連,且0號(hào)和1號(hào)的分量數(shù)都為3,以0號(hào)屬性開頭.
故: 第一個(gè)0號(hào)和第二個(gè)0號(hào) 中間有6個(gè)間距 stride = 6*sizeof(float).
1號(hào)在0號(hào)后面, 0號(hào)ptr為 (void*)0 . 1號(hào)ptr為 (void* )(3*sizeof(float))

繪制

使用 EBO:

glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0);
參數(shù):
model:指定呈現(xiàn)那種圖元(將這些點(diǎn)繪制成怎樣的形狀). 可選項(xiàng):

GL_POINTS(點(diǎn)),
GL_LINE_STRIP(多端線),
GL_LINE_LOOP(線圈),
GL_LINES(線段),
GL_TRIANGLE_FAN, (三角形扇)
GL_TRIANGLES, (三角形)
count: 傳入頂點(diǎn)數(shù)據(jù)的數(shù)量
type: 索引數(shù)組的元素屬性 GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.
indices: 指向索引數(shù)組的指針, 當(dāng)使用VBO時(shí),則表示為偏移量,若為緊密相連時(shí)則傳入0.

不使用EBO

glDrawArrays(GL_TRIANGLES, 0, 3);
參數(shù): model 和上面那個(gè)含義一樣.
first 表示頂點(diǎn)數(shù)據(jù)起始索引, 從頭開始則為0.
count 表示要傳入頂點(diǎn)數(shù)據(jù)的數(shù)量.

最后顯示

[_context presentRenderbuffer:GL_RENDERBUFFER];

最終結(jié)果.png

學(xué)習(xí)參考

你好三角形
OpenGL 簡(jiǎn)書專題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,115評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,234評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,621評(píng)論 1 326
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,822評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,380評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,128評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,319評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,548評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,048評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,285評(píng)論 2 376

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