C++ 是否有任何与顺序无关的透明技术适用于延迟着色?
我已经为我的OpenGL引擎调查了许多与顺序无关的透明方法,起初我认为我希望使用加权平均混合,以最大限度地提高速度 但是,我的引擎使用延迟着色,在选择混合技术时需要考虑这一点。理想情况下,我希望有一种技术不会要求我实现用于半透明对象的前向着色 在许多情况下,我需要使用透明度:C++ 是否有任何与顺序无关的透明技术适用于延迟着色?,c++,opengl,transparency,deferred-shading,C++,Opengl,Transparency,Deferred Shading,我已经为我的OpenGL引擎调查了许多与顺序无关的透明方法,起初我认为我希望使用加权平均混合,以最大限度地提高速度 但是,我的引擎使用延迟着色,在选择混合技术时需要考虑这一点。理想情况下,我希望有一种技术不会要求我实现用于半透明对象的前向着色 在许多情况下,我需要使用透明度: 草/头发(抗锯齿剪切) 玻璃(彩色混合) 淡入淡出的对象 烟/云 水/液体(涉及折射,我知道这里不可能有真正的OIT) 火花/魔法/火焰(不需要点燃,可以使用添加剂混合,不用担心这些) 为了速度,我愿意牺牲图像的正确性
- 草/头发(抗锯齿剪切)
- 玻璃(彩色混合)
- 淡入淡出的对象
- 烟/云
- 水/液体(涉及折射,我知道这里不可能有真正的OIT)
- 火花/魔法/火焰(不需要点燃,可以使用添加剂混合,不用担心这些)
另外,有没有更好的方法来渲染不依赖混合的抗锯齿剪切(草/头发/树叶)?纯alpha测试往往会产生难看的别名。 < P>我不确定它是否适合您的延迟渲染器,但您可能会考虑加权、混合顺序无关透明性。有一个()和一个()以及很多其他的东西。它的速度相当快,因为它只使用一个不透明、一个透明和一个合成过程,并且与OpenGL 3.2+配合使用。
我实现了第一个版本,它工作得很好,这取决于你的场景和一个经过适当调整的权重函数,但在高alpha值方面存在问题。使用论文中的权重函数,我没有得到好的结果,但只是在使用线性、标准化的眼空间z值之后。
请注意,当使用OpenGL<4.0时,不能为每个缓冲区指定一个混合函数(glblendfunc),因此需要解决这个问题(请参阅第一篇文章)
- 使用以下附件设置帧缓冲区:
- 0:RGBA8,不透明
- 1:RGBA16F,累加
- 2:R16F,revealage
- 清除附件#0到屏幕的清晰颜色(r、g、b、1),#1到(0,0,0,1)和#2到0
- 将不透明几何体渲染到附件#0和深度缓冲区
glEnable(GLU深度试验)
glDepthMask(GL_TRUE) - 将透明几何体渲染到附件1和附件2。关闭深度缓冲区写入,但保持启用深度测试
glDepthMask(GL_FALSE)
glEnable(GL_混合物)
glBlendEquation(GL_FUNC_ADD)
glBlendFuncSeparate(GL_ONE、GL_ONE、GL_ZERO、GL_ONE减去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;
}
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);
}
我的做法是:
- 使用透明曲面的抖动渲染(aka)以低分辨率渲染整个场景,使用绘制ID(任何ID,只要它在帧中唯一)旋转抖动遮罩,渲染绘制ID、世界法线、FragDepth(请参见)、BRDF Alpha(请参见)到帧缓冲区
- 您的照明(仅漫反射和镜面反射)、SSR或SSAO是否照常通过
- 以正常分辨率(也称为“材质过程”)将不透明和“截止”曲面渲染为“不透明”帧缓冲区(OFB)
- 创建2个“透明”帧缓冲区(FB0和FB1),FB1为FB0分辨率的一半
- 渲染透明曲面而不混合到启用深度测试/写入的FB0
- Blit FB0缓冲区深度位到FB1
- 再次使用混合OIT将透明曲面渲染到FB1,但使用glDepthFunc(GL_更大)和glDepthMask(GL_FALSE),手动测试不透明曲面的深度以在着色器中丢弃(稍微慢一点,但可能无法绑定2个深度缓冲区)
- 为OFB生成mipmap
- 手动合成从FB1到OFB mip 0的最远透明曲面,在着色器中从OFB mip 1及以上进行采样(速度较慢,但允许失真和粗糙/彩色传输)
- 再次为OFB生成mipmap
- 合成从FB0到OFB mip 0最近的透明曲面,从OFB mip 1采样,该OFB mip 1现在包含来自FB1的透明曲面
- 这不是一个过程,但您的第一个渲染和照明过程是在较低的分辨率下完成的,因此不会对性能造成太大影响,而且“最远”的透明曲面的分辨率是正常分辨率的一半
- 在返回IBL之前,您只能获得4层透明度,但如果您将动态灯光用作回退解决方案,则可以。使用较大的抖动遮罩可以获得8层,但重建曲面的速度会较慢
- 它允许像任何延迟渲染器一样使用大量灯光
- 由于混合了差异渲染和正向渲染,因此它允许每个材质环境和BRDF查找表,这非常方便
- 重查找技术(如SSR和SSAO)以较低的分辨率完成,这有助于提高性能
- 像SSR和SSAO一样,照明是在低分辨率下进行的
- 这样可以产生奇特的效果,如屏幕空间折射和透明表面
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);