最近做課題的時候需要計算一個 view(就是一次渲染得到的幀) 下的重疊像素個數(兩個物體或更多的物體重疊)。
- 最開始我的想法是渲染一個物體輸出一張紋理,這樣對比物體之間的紋理就知道重疊了。但是這樣當物體很多的時候需要輸出太多的紋理到CPU,太慢了,也很麻煩。
- 后來和同學討論了一下,覺得是不是可以使用一張紋理作為幀緩沖的輸出,同時作為片段着色器的輸入,這樣可以每渲染一個像素點加1,最終大於1的位置表示重疊。但是后來這樣試了,在片段着色器中采樣這張紋理的時候,采樣出來的值明顯不對,沒有隨着渲染一個物體后再次采樣會逐步積累增加。(感覺因為是同一張紋理,但是是輸入和輸出,在GPU中分配了兩個存儲位置,各自使用自己的,所以沒有疊加,我瞎猜的……)
- 后來查到仿佛可以使用累積緩沖,每次渲染的結果疊加在一起,但是需要加權,value = α*value + (1-α)*newvalue ,感覺不太好操作,放棄了。
- 最后看了一下stencil,發現可以實現我需要的功能。雖然幾個月前看過一次,但是當時沒有太懂,這次算是理解了。
現在再來回顧一下 Stencil 的用法:
- glStencilMask(value) 位遮罩, value=0x00表示模板緩沖不可寫入;value=0xFF表示可以寫入。
- void glStencilFunc(GLenum func, GLint ref, GLuint mask) 指定ref值與存儲的模板值對比
- void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) 指定模板值的寫入策略
流程代碼
glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); glStencilMask(0xFF); //開啟模板寫入 (清空模板緩沖需要開啟模板寫入!!!) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // 我們現在使用模板緩沖 glStencilOp(GL_INCR, GL_INCR, GL_INCR); glStencilMask(0x00); //關閉模板寫入 this->DrawSkyBox(false); this->DrawFloor(false); glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilMask(0xFF); //開啟模板寫入 this->DrawBuildings(false); // 先渲染building,shader中的編號保持一致,但是sky一定在前 glStencilMask(0x00); //關閉模板寫入
遇到的坑是清空模板緩沖前一定要開啟模板寫入!!!
模板值讀取:
這個耽擱了很久,開始是附加模板紋理到幀緩沖中,然后從紋理中讀取值,但是怎么都讀出來不對……
后來發現可以直接使用其API讀取 glReadPixels 。
使用此API讀取時,其是直接面向當前幀緩沖獲取模板值數據,故與模板生成方式無關,可以是默認屏幕的幀緩沖,也可以使自己生成的幀緩沖(可以是RBO模板緩沖、模板緩沖紋理、深度-模板緩沖紋理)。
float* WKS::Texture::GetStencilData(GLuint width, GLuint height) { float* pixels = new float[width * height]; glReadPixels(0, 0, width, height, GL_STENCIL_INDEX, GL_FLOAT, pixels); return pixels; }
一定要記得在對應的幀緩沖中使用。