我不确定它是否适合您的延迟渲染器,但您可能会考虑加权、混合顺序无关的透明度。有一个不带彩色传输(web)的旧版本和一个支持彩色传输(web)和许多其他东西的新版本。它非常快,因为它只使用一个不透明、一个透明度和一个合成通道,并且它适用于 OpenGL 3.2+。
我实现了第一个版本,它工作得很好,具体取决于您的场景和适当调整的加权函数,但存在高 alpha 值的问题。我使用论文中的加权函数没有得到好的结果,但只有在使用线性、归一化的眼睛空间 z 值之后。
请注意,当使用 OpenGL < 4.0 时,您不能为每个缓冲区指定混合函数 (glBlendFunci),因此您需要解决这个问题(参见第一篇论文)。
- 使用这些附件设置帧缓冲区:
- 0:RGBA8,不透明
- 1:RGBA16F,累积
- 2:R16F,显露
- 清除附件 #0 到屏幕清除颜色 (r,g,b,1),#1 到 (0,0,0,1) 和 #2 到 0。
将不透明几何体渲染到附件#0 和深度缓冲区。
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
将透明几何体渲染到附件#1 和#2。关闭深度缓冲区写入,但启用深度测试。
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
写入累积和显示目标的片段着色器部分如下所示:
uniform mat4 projectionMatrix;
layout (location = 1) out vec4 accum;
layout (location = 2) out float revealage;
/// @param color Regular RGB reflective color of fragment, not pre-multiplied
/// @param alpha Alpha value of fragment
/// param wsZ Window-space-z value == gl_FragCoord.z
void writePixel(vec3 color, float alpha, float wsZ) {
float ndcZ = 2.0 * wsZ - 1.0;
// linearize depth for proper depth weighting
//See: https://stackoverflow.com/questions/7777913/how-to-render-depth-linearly-in-modern-opengl-with-gl-fragcoord-z-in-fragment-sh
//or: https://stackoverflow.com/questions/11277501/how-to-recover-view-space-position-given-view-space-depth-value-and-ndc-xy
float linearZ = (projectionMatrix[2][2] + 1.0) * wsZ / (projectionMatrix[2][2] + ndcZ);
float tmp = (1.0 - linearZ) * alpha;
//float tmp = (1.0 - wsZ * 0.99) * alpha * 10.0; // <-- original weighting function from paper #2
float w = clamp(tmp * tmp * tmp * tmp * tmp * tmp, 0.0001, 1000.0);
accum = vec4(color * alpha* w, alpha);
revealage = alpha * w;
}
绑定附件纹理#1 和#2,并通过使用合成着色器绘制四边形将它们合成到附件#0。
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
合成的片段着色器如下所示:
uniform sampler2DMS accumTexture;
uniform sampler2DMS revealageTexture;
in vec2 texcoordVar;
out vec4 fragmentColor;
void main() {
ivec2 bufferCoords = ivec2(gl_FragCoord.xy);
vec4 accum = texelFetch(accumTexture, bufferCoords, 0);
float revealage = accum.a;
// save the blending and color texture fetch cost
/*if (revealage == 1.0) {
discard;
}*/
accum.a = texelFetch(revealageTexture, bufferCoords, 0).r;
// suppress underflow
if (isinf(accum.a)) {
accum.a = max(max(accum.r, accum.g), accum.b);
}
// suppress overflow
if (any(isinf(accum.rgb))) {
accum = vec4(isinf(accum.a) ? 1.0 : accum.a);
}
vec3 averageColor = accum.rgb / max(accum.a, 1e-4);
// dst' = (accum.rgb / accum.a) * (1 - revealage) + dst * revealage
fragmentColor = vec4(averageColor, revealage);
}