OpenGL ES入門14-深度測(cè)試

本文是關(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)效果

開啟深度測(cè)試.gif
未開啟深度測(cè)試.gif

深度緩沖

深度緩沖與顏色緩沖類似,深度緩沖存儲(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

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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