有了
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_ATTACHMENT1与第二个纹理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