本文是關(guān)于OpenGL ES的系統(tǒng)性學(xué)習(xí)過程,記錄了自己在學(xué)習(xí)OpenGL ES時(shí)的收獲。
這篇文章的作用是學(xué)習(xí)的OpenGL ES深度測(cè)試技術(shù),實(shí)例是繪制立方體,并進(jìn)行紋理貼圖。
環(huán)境是Xcode8.1+OpenGL ES 2.0
目前代碼已經(jīng)放到github上面,OpenGL ES入門14-深度測(cè)試
歡迎關(guān)注我的 OpenGL ES入門專題
實(shí)現(xiàn)效果


深度緩沖
深度緩沖與顏色緩沖類似,深度緩沖存儲(chǔ)的是每個(gè)片段的信息, 通常情況下與顏色緩沖區(qū)有相同的寬度和高度。在iOS中,我們需要自己創(chuàng)建深度緩沖,我們可以創(chuàng)建16位的深度緩沖區(qū)。當(dāng)深度測(cè)試啟用的時(shí)候, OpenGL 測(cè)試深度緩沖區(qū)內(nèi)的深度值。OpenGL 執(zhí)行深度測(cè)試的時(shí)候,如果此測(cè)試通過,深度緩沖內(nèi)的值將被設(shè)為新的深度值,如果深度測(cè)試失敗,則丟棄該片段。屏幕空間坐標(biāo)直接有關(guān)的視區(qū),由 OpenGL 的 glViewport 函數(shù)給定,并且可以通過片段著色器中內(nèi)置的 gl_FragCoord 變量訪問。gl_FragCoord 的 X 和 y 表示該片段的屏幕空間坐標(biāo)。gl_FragCoord 還包含一個(gè) z 坐標(biāo),它包含了片段的實(shí)際深度值。此 z 坐標(biāo)值是與深度緩沖區(qū)的內(nèi)容進(jìn)行比較的值。深度緩沖區(qū)中包含深度值介于0.0和1.0之間,物體接近近平面的時(shí)候,深度值接近0.0,物體接近遠(yuǎn)平面時(shí),深度接近1.0。
創(chuàng)建深度緩沖區(qū)
在iOS中需要手動(dòng)創(chuàng)建深度緩沖區(qū),我們使用 glGenRenderbuffers 創(chuàng)建深度緩沖區(qū),然后為其分配16位的存儲(chǔ)空間,最后綁定深度緩沖與幀緩沖。
- (void)setupFrameAndRenderBuffer
{
// Setup color render buffer
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
// Setup depth render buffer
int width, height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
// Create a depth buffer that has the same size as the color buffer.
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
// Setup frame buffer
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// Attach color render buffer and depth render buffer to frameBuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, _depthRenderBuffer);
// Set color render buffer as current render buffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// Check FBO satus
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Error: Frame buffer is not completed.");
exit(1);
}
}
開啟深度測(cè)試
深度測(cè)試默認(rèn)是關(guān)閉的,我們需要用 glEnable 來手動(dòng)開啟。啟用深度測(cè)試后,如果片段通過深度測(cè)試,OpenGL自動(dòng)在深度緩沖區(qū)存儲(chǔ)片段的 z 值,如果深度測(cè)試失敗,那么相應(yīng)地丟棄該片段
glEnable(GL_DEPTH_TEST);
清除深度緩沖區(qū)
在啟用了深度測(cè)試后,在渲染之前需要使用 GL_DEPTH_BUFFER_BIT 清除深度緩沖區(qū),否則深度緩沖區(qū)將保留上一次進(jìn)行深度測(cè)試時(shí)所寫的深度值。
glClear(GL_DEPTH_BUFFER_BIT);
只讀的深度緩沖區(qū)
在某些時(shí)候我們不希望更新深度緩沖區(qū),我們可以使用一個(gè)只讀的深度緩沖區(qū)。OpenGL允許我們通過將其深度掩碼設(shè)置為GL_FALSE禁用深度緩沖區(qū)寫入。
glDepthMask(GL_FALSE);
深度測(cè)試函數(shù)
我們可以修改OpenGL的深度測(cè)試比較運(yùn)算符,從而可以控制OpenGL通過或丟棄碎片以及如何更新深度緩沖區(qū)。默認(rèn)情況下使用GL_LESS,默認(rèn)丟棄深度值高于或等于當(dāng)前深度緩沖區(qū)的值的片段。
| 運(yùn)算符 | 描述 |
|---|---|
| GL_ALWAYS | 永遠(yuǎn)通過測(cè)試 |
| GL_NEVER | 永遠(yuǎn)不通過測(cè)試 |
| GL_LESS | 在片段深度值小于緩沖區(qū)的深度時(shí)通過測(cè)試 |
| GL_EQUAL | 在片段深度值等于緩沖區(qū)的深度時(shí)通過測(cè)試 |
| GL_LEQUAL | 在片段深度值小于等于緩沖區(qū)的深度時(shí)通過測(cè)試 |
| GL_GREATER | 在片段深度值大于緩沖區(qū)的深度時(shí)通過測(cè)試 |
| GL_NOTEQUAL | 在片段深度值不等于緩沖區(qū)的深度時(shí)通過測(cè)試 |
| GL_GEQUAL | 在片段深度值大于等于緩沖區(qū)的深度時(shí)通過測(cè)試 |
繪制立方體
1、初始化深度緩沖區(qū)。
- (void)setupFrameAndRenderBuffer
{
// Setup color render buffer
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
// Setup depth render buffer
int width, height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
// Create a depth buffer that has the same size as the color buffer.
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
// Setup frame buffer
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// Attach color render buffer and depth render buffer to frameBuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, _depthRenderBuffer);
// Set color render buffer as current render buffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// Check FBO satus
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Error: Frame buffer is not completed.");
exit(1);
}
}
2、初始化投影矩陣。
- (void)setupProjectionMatrix
{
mat4_t projectMatrix = mat4_perspective(M_PI/3, self.frame.size.width/self.frame.size.height, 1, 10);
GLint projectionSlot = glGetUniformLocation(_program, "projection");
glUniformMatrix4fv(projectionSlot, 1, GL_FALSE, (GLfloat *)&projectMatrix);
}
3、初始化模型視圖矩陣。
- (void)setupModelViewMatrix
{
static CGFloat angle = 0;
mat4_t modelView = mat4_create_translation(0, 0, -4);
modelView = mat4_rotate(modelView, angle, 1, 1, 0);
GLint modelViewSlot = glGetUniformLocation(_program, "modelView");
glUniformMatrix4fv(modelViewSlot, 1, GL_FALSE, (GLfloat *)&modelView);
angle += M_PI/180;
}
4、初始化頂點(diǎn)、紋理坐標(biāo)數(shù)據(jù)。
- (void)setupVBO
{
_vertCount = 36;
GLfloat vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
// 創(chuàng)建VBO
_vbo = createVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(vertices), vertices);
glEnableVertexAttribArray(glGetAttribLocation(_program, "position"));
glVertexAttribPointer(glGetAttribLocation(_program, "position"), 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL);
glEnableVertexAttribArray(glGetAttribLocation(_program, "texcoord"));
glVertexAttribPointer(glGetAttribLocation(_program, "texcoord"), 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL+sizeof(GL_FLOAT)*3);
}
5、初始化紋理。
- (void)setupTexure
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"wood" ofType:@"jpg"];
unsigned char *data;
int size;
int width;
int height;
// 加載紋理
if (read_jpeg_file(path.UTF8String, &data, &size, &width, &height) < 0) {
printf("%s\n", "decode fail");
}
// 創(chuàng)建紋理
_texture = createTexture2D(GL_RGB, width, height, data);
if (data) {
free(data);
data = NULL;
}
}
6、初始化定時(shí)器。
- (void)setupLinker
{
CADisplayLink *linker = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
linker.frameInterval = 1;
[linker addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
7、渲染。
- (void)render
{
glEnable(GL_DEPTH_TEST);
//glDepthFunc(GL_ALWAYS);
//glDepthMask(GL_FALSE);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
// 模型矩陣
[self setupModelViewMatrix];
// 紋理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _texture);
glUniform1i(glGetUniformLocation(_program, "image"), 0);
// 繪制
glDrawArrays(GL_TRIANGLES, 0, _vertCount);
//將指定 renderbuffer 呈現(xiàn)在屏幕上,在這里我們指定的是前面已經(jīng)綁定為當(dāng)前 renderbuffer 的那個(gè),在 renderbuffer 可以被呈現(xiàn)之前,必須調(diào)用renderbufferStorage:fromDrawable: 為之分配存儲(chǔ)空間。
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
參考資料
http://www.cnblogs.com/kesalin/archive/2012/12/06/3D_math.html