OpenGL順序無關的透明(OIT)加權平均法
阿新 • • 發佈:2018-11-20
有了
OpenGL渲染到幀快取物件(FBO)
和
OpenGL紋理
的基礎,就可以用加權平均法(WA)做順序無關的透明度(OIT)了
加權平均法
Pass0 PixelShader
關閉深度測試,每一個畫素上可能是由 i 個顏色疊加起來的,我們把它們全被加起來,記錄在一張紋理(Color)上
vec3( r1, g1, b1 ) * Alpha1 + vec3( r2, g2 ,b2 ) * Alpha2 + … + vec3( ri, gi, bi ) * Alphai
同時我們把畫素累加的次數 i 也記錄在一張紋理(Number)上
Pass1 PixelShader
取樣紋理Color和Number
用color除以number則得到了某一個畫素上的樣色的平均值
初始化
GLenum g_drawBuffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT, GL_COLOR_ATTACHMENT4_EXT, GL_COLOR_ATTACHMENT5_EXT, GL_COLOR_ATTACHMENT6_EXT }; void InitAccumulationRenderTargets() { //生成兩張紋理 glGenTextures(2, g_accumulationTexId); //第一張為記錄顏色的紋理Color glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[0]); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA16F_ARB, //注意這裡的內部格式是Float,這樣在Shader中我們得到的紋理是一個可以超越[-1.0,1.0]的全精度浮點數 g_imageWidth, g_imageHeight, 0, GL_RGBA, GL_FLOAT, NULL); //第二張為記錄每個畫素上顏色疊加次數的紋理Number glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[1]); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_FLOAT_R32_NV, //注意這裡的內部格式是Float,這樣在Shader中我們得到的紋理是一個可以超越[-1.0,1.0]的全精度浮點數 g_imageWidth, g_imageHeight, 0, GL_RGBA, GL_FLOAT, NULL); //建立一個FBO glGenFramebuffersEXT(1, &g_accumulationFboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_accumulationFboId); //把這個FBO的COLOR_ATTACHMENT0與第一個紋理Color關聯 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[0], 0); //把這個FBO的COLOR_ATTACHMENT0與第二個紋理Number關聯 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[1], 0); CHECK_GL_ERRORS; }
刪除,用於停止渲染時
void DeleteAccumulationRenderTargets()
{
glDeleteFramebuffersEXT(1, &g_accumulationFboId);
glDeleteTextures(2, g_accumulationTexId);
}
渲染迴圈
void RenderAverageColors() { glDisable(GL_DEPTH_TEST);//我們不要深度測試,因為我們要把一條光線上的所用畫素疊加到一個畫素上 // --------------------------------------------------------------------- // 1. Accumulate Colors and Depth Complexity // --------------------------------------------------------------------- //繫結我們直接生成的FBO glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_accumulationFboId); //選擇我們要把東西畫到COLOR_ATTACHMENT0和COLOR_ATTACHMENT1上 glDrawBuffers(2, g_drawBuffers); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); //選擇直接疊加的融混方式 glBlendEquationEXT(GL_FUNC_ADD); glBlendFunc(GL_ONE, GL_ONE); glEnable(GL_BLEND); //繫結第一個pass的shader並開始畫 g_shaderAverageInit.bind(); g_shaderAverageInit.setUniform("Alpha", (float*)&g_opacity, 1); DrawModel(); g_shaderAverageInit.unbind(); //在畫第二個pass前一定要記得關閉為第一個pass開啟的狀態 glDisable(GL_BLEND); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glDrawBuffer(GL_BACK); // --------------------------------------------------------------------- // 2. Approximate Blending // --------------------------------------------------------------------- g_shaderAverageFinal.bind(); g_shaderAverageFinal.setUniform("BackgroundColor", g_backgroundColor, 3); g_shaderAverageFinal.bindTextureRECT("ColorTex0", g_accumulationTexId[0], 0); g_shaderAverageFinal.bindTextureRECT("ColorTex1", g_accumulationTexId[1], 1); glCallList(g_quadDisplayList); g_shaderAverageFinal.unbind(); CHECK_GL_ERRORS; }
Shader
Pass0的PixelShader
#extension ARB_draw_buffers : require
vec4 ShadeFragment();//不重要,可以自己構建,只要得到一個最後輸出的顏色即可
void main(void)
{
vec4 color = ShadeFragment();
//獲得要輸出的顏色,把rgb乘上透明度,加到Color這張紋理上
gl_FragData[0] = vec4(color.rgb * color.a, color.a);
//在記錄累加次數的紋理上加1
gl_FragData[1] = vec4(1.0);
}
Pass1的PixelShader
uniform samplerRECT ColorTex0;
uniform samplerRECT ColorTex1;
uniform vec3 BackgroundColor;
void main(void)
{
vec4 SumColor = textureRect(ColorTex0, gl_FragCoord.xy);
float n = textureRect(ColorTex1, gl_FragCoord.xy).r;
if (n == 0.0) {
gl_FragColor.rgb = BackgroundColor;
return;
}
vec3 AvgColor = SumColor.rgb / SumColor.a;//這裡沒有注意除0的情況!!若有透明的為0的情況需要修改
float AvgAlpha = SumColor.a / n;
float T = pow(1.0-AvgAlpha, n);
gl_FragColor.rgb = AvgColor * (1 - T) + BackgroundColor * T;
}
這篇文章參考
http://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf
你可以在這裡獲得所有的程式碼
或者訪問下面的網站:
http://developer.download.nvidia.com/SDK/10/opengl/screenshots/samples/dual_depth_peeling.html