【我的OpenGL学习进阶之旅】关于OpenGL ES 开启深度测试,直接黑屏的问题的解决方法
目錄
- 一、問題描述
- 二、排查問題
- 2.1 懷疑createProgram鏈接著色器出了問題
- 2.2 懷疑是不是沒有draw()?
- 2.3 對比以前寫的可以正常顯示的其他Demo代碼
- 三、解決問題
- 3.1 去掉`glEnable(GL_DEPTH_TEST);`
- 3.3 加上 `glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
- 3.4 加上 `glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`和 `glEnable(GL_DEPTH_TEST);`
- 四、知其然知其所以然
- 4.1 什么是深度緩沖區?
- 4.1.1 了解深度
- 4.1.2 思考下,為什么要使用深度緩沖區?
- 4.1.3 究竟什么是深度緩沖區?
- 4.2 什么是深度測試?
- 4.2.1 啟用深度測試
- 4.2.2 深度緩沖區的深度值
- 4.3 深度測試中的深度沖突現象
- 4.3 開啟深度測試的注意事項
- 五、總結
一、問題描述
今日,寫了個OpenGL的小程序,發現場景直接黑屏。
二、排查問題
2.1 懷疑createProgram鏈接著色器出了問題
看日志
正常創建了Program,也沒有異常打印,所以應該不是這里的問題
2.2 懷疑是不是沒有draw()?
加上日志打印,排查draw() 方法有沒有正常執行,如何
日志在正常打印,說明在不停的繪制,但是為啥不停的繪制還是黑屏?還得繼續排查
2.3 對比以前寫的可以正常顯示的其他Demo代碼
我對比了之前可以正常顯示的其他Demo的代碼,發現我這個Demo使用了
glEnable(GL_DEPTH_TEST);開啟了深度測試,而其他的代碼是沒有開啟深度測試的
有異常的代碼如下所示:
三、解決問題
3.1 去掉glEnable(GL_DEPTH_TEST);
我們直接將代碼glEnable(GL_DEPTH_TEST);去掉,如下所示:
void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw() mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);// glEnable(GL_DEPTH_TEST);// ... 其他繪制邏輯發現此時有畫面,但是畫面會有異常,如下所示:
我們可以發現,隨著手指觸摸它,讓它不停旋轉,之前每一次旋轉繪制的幀都沒有清除掉, 很難看!
但是至少畫面出來了,看來是因為開啟深度測試導致的黑屏,我們繼續排查為啥開啟深度測試會導致黑屏呢?
3.3 加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
我們在上一步去掉 glEnable(GL_DEPTH_TEST);的情況下,加上代碼
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);完整代碼如下:
void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw() mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// glEnable(GL_DEPTH_TEST);// ... 其他繪制邏輯運行的效果也有異常,如下所示:
這一次比上一次好了,沒有那么多歷史的幀殘留在繪制。但是光照效果還是有異常,我們加上深度測試看一看。
這是因為:如果關閉深度測試,OpenGL只根據繪制的先后順序決定顯示結果。那么后繪制的平面遮擋了一部分先繪制的本應該顯示出來的立方體,這種效果是不符合實際的。
3.4 加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);和 glEnable(GL_DEPTH_TEST);
這一次我們不光加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);,我們還加上 glEnable(GL_DEPTH_TEST); 如下所示:
void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw() mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);// ... 其他繪制邏輯這一次,我們可愛的超越妹妹終于正常顯示了
不停旋轉,畫面也正常,如下所示:
看來,下面的兩行代碼必須搭配使用才可以正常。
但是為什么呢? 我們接下來分析。
四、知其然知其所以然
本節內容參考自下面兩篇博客:
- OpenGL-深度測試 https://www.it610.com/article/1409069347401465856.htm
- LEARN OPENGL 深度測試 https://zhuanlan.zhihu.com/p/369781131
- NDK OpenGL ES 3.0 開發(十):深度測試 https://blog.csdn.net/Kennethdroid/article/details/101709694
4.1 什么是深度緩沖區?
深度緩沖區(Detph buffer)同顏色緩沖區(color buffer)是對應的,
顏色緩沖區存儲的像素的顏色信息,
而深度緩沖區存儲像素的深度信息。
4.1.1 了解深度
深度其實就是該像素點在3D世界中距離攝像機的距離,z值
4.1.2 思考下,為什么要使用深度緩沖區?
-
在不使用深度測試的時候
如果我們先繪制一個距離比較近的物理再繪制距離較遠的物理,則距離遠的位圖因為后繪制,會把距離近的物體覆蓋掉 -
有了深度緩沖區后
繪制物體的順序就不那么重要的。實際上,只要存在深度緩沖區OpenGL都會把像素的深度值寫入到緩沖區中除非調用glDepthMask(GL FALSE).來禁止寫入。
4.1.3 究竟什么是深度緩沖區?
其實深度緩沖區,就是一塊內存區域,專門存儲著每個像素點(繪制在屏幕上的)深度值,深度值(Z值)越大,則離攝像機就越遠。
深度緩沖區一般由窗口管理系統來創建,深度值一般由16位,24位或者32位值表示,通常是24位。當然位數越高的話,深度的精確度越好
4.2 什么是深度測試?
在決定是否繪制一個物體的表面時,首先將表面對應像素的深度值與當前深度緩沖區中的值進行比較,
如果大于等于深度緩沖區中值,則丟棄這部分;
否則利用這個像素對應的深度值和顏色值,分別更新深度緩沖區和顏色緩沖區。
這一過程稱之為深度測試(Depth Testing)。
深度緩沖區通常用于隱藏表面的消除。
傳統上,它保存渲染表面上每個像素與視點最近物體的距離值,對于每個新的輸入片段,將其與視點的具體和存儲值比較。
默認情況下,如果輸入片段的深度值小于深度緩沖區中保存的值(意味著它離觀看者更近),則 輸入片段的深度值代替保存在深度緩沖區中的值,然后其顏色值代替顏色緩沖區中的顏色值。
4.2.1 啟用深度測試
這是深度緩沖區的標準方法,如果這就是你想做的,那么調用帶GL_DEPTH_TEST的glEnable來啟用深度測試。因為深度測試默認是關閉的,所以必須得調用下面的方法才能啟動深度測試
glEnable(GL_DEPTH_TEST);當它啟用的時候,如果一個片段通過了深度測試的話,OpenGL會在深度緩沖中儲存該片段的z值;如果沒有通過深度緩沖,則會丟棄該片段。
如果你啟用了深度緩沖,你還應該在每個渲染迭代之前使用GL_DEPTH_BUFFER_BIT來清除深度緩沖,否則你會仍在使用上一次渲染迭代中的寫入的深度值: 這也就是我們為什么一開始黑屏的原因。
所以我們 需要在繪制場景前,清除顏色緩沖區時,清除深度緩沖區:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);4.2.2 深度緩沖區的深度值
清除深度緩沖區的默認值是1.0,表示最大的深度值,深度值的范圍在[0,1]之間,值越小表示越靠近觀察者,值越大表示遠離觀察者。
在進行深度測試時,當前深度值和深度緩沖區中的深度值,進行比較的函數,可以由用戶通過glDepthFunc指定,這個函數包括一個參數
void glDepthFunc (GLenum func);
深度測試啟用后,默認情況下深度測試函數使用 GL_LESS,這將丟棄深度值高于或等于當前深度緩沖區的值的片段。
例如我們可以使用GL_AWALYS參數,這與默認不開啟深度測試效果是一樣的:
glDepthFunc(GL_ALWAYS);4.3 深度測試中的深度沖突現象
深度測試中,深度沖突現象需要值得注意。深度沖突(Z-fighting)是指兩個平面(或三角形)相互平行且靠近的過于緊密,模板緩沖區不具有足夠的精度確定哪一個平面靠前,導致這兩個平面的內容不斷交替顯示,看上去像平面內容爭奪頂靠前的位置。
防止深度沖突的方法:
4.3 開啟深度測試的注意事項
五、總結
我們一開始啟用深度測試,直接黑屏的原因就是因為沒有使用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);清除深度緩沖區。
因為如果不使用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);清除深度緩沖區,那么就會仍在使用上一次渲染迭代中的寫入的深度值。
而深度測試(Depth Testing)過程,在決定是否繪制一個物體的表面時,首先將表面對應像素的深度值與當前深度緩沖區中的值進行比較,
如果大于等于深度緩沖區中值,則丟棄這部分;
否則利用這個像素對應的深度值和顏色值,分別更新深度緩沖區和顏色緩沖區。
所以,這樣值不變,就會直接被丟棄,所以就黑屏了!
因此,我們使用深度測試的時候,最好如下面所示一樣搭配使用:
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);總結
以上是生活随笔為你收集整理的【我的OpenGL学习进阶之旅】关于OpenGL ES 开启深度测试,直接黑屏的问题的解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redsi通过geo计算距离
- 下一篇: AutoCAD202364位 简体中文免