簡(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);
// 創(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ì)象。
函數(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
, orGL_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];