我已经为我的OpenGL引擎调查了许多与顺序无关的透明度方法,起初我以为我想使用加权平均混合来最大化速度。
但是,我的引擎使用延迟着色,我需要在选择混合技术时考虑到这一点。理想情况下,我想要一种不会要求我实现前向着色以用于半透明对象的技术。
在许多情况下,我需要使用透明度:
我愿意为了速度而牺牲图像的正确性(因此我最初选择加权平均混合)。我不需要点亮每一层半透明物体,但我至少希望最前面的像素能够正常点亮。
我正在使用OpenGL 3.x + Core Context,所以我想避免任何需要OpenGL 4.x的东西(尽可能使用它),但我可以自由使用任何不可用的东西。 OpenGL 2.x。
我的问题是:延迟着色的最佳顺序无关透明度技术是什么?和/或:在使用延迟着色时,点亮/遮蔽半透明对象的最佳方法是什么?
P.S。有没有更好的方法来渲染不依赖混合的抗锯齿切口(草/毛/叶)?纯alpha测试往往会产生难看的锯齿。
答案 0 :(得分:0)
我不确定它是否适合您的延迟渲染器,但您可以考虑加权,混合顺序无关的透明度。有一个older version w/o colored transmisson(web)和一个newer version that supports colored transmission(web)以及许多其他内容。它非常快,因为它只使用一个不透明,一个透明度和一个合成传递,它适用于OpenGL 3.2+ 我实现了第一个版本,它运行得很好,具体取决于你的场景和适当调整的加权函数,但是有高alpha值的问题。我没有通过论文中的加权函数获得良好的结果,但仅在使用线性,标准化的眼睛空间z值之后。 请注意,使用OpenGL时< 4.0你不能指定每个缓冲区的混合函数(glBlendFunci),所以你需要解决这个问题(参见第一篇论文)。
将不透明几何体渲染到附件#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);
}
答案 1 :(得分:0)
我的方式:
有关如何进行合成以及如何构造“透明”帧缓冲区的信息,请参见Morgan McGuire's blog。 使用绘图ID帧缓冲区和法线来重建透明曲面,我使用简单的加权平均值,其权重对应于当前法线点帧缓冲区的法线(法线点本身为1)。
缺点:
上方:
我的Blended OIT的重量函数(具有相同不透明度的更近的表面总是获得更高的重量):
void WritePixel(vec3 premultipliedReflect, float coverage)
{
float z = abs(CameraSpaceDepth);
float w = clamp(pow(abs(1 / z), 4.f) * coverage * coverage, 6.1*1e-4, 1e5);
out_0 = vec4(premultipliedReflect, coverage) * w;
out_1 = vec4(1 - coverage); //so you can render without blending
}
我的合成功能:
vec4 accum = texelFetch(in_Buffer0, ivec2(gl_FragCoord.xy), 0);
float r = texelFetch(in_Buffer1, ivec2(gl_FragCoord.xy), 0).r;
out_Buffer0 = vec4(accum.rgb / clamp(accum.a, 6.1*1e-4, 6.55*1e5), r);
有关“ CameraSpaceDepth”的信息,请参见this;有关fp值的信息,请参见this
这是this model的结果,其POC脏乱,您可以看到粗糙的表面传输: