OpenGL顺序无关的透明(OIT)加权平均法

有了
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

你可能感兴趣的:(OpenGL)