尝试在延迟着色之上实现抗锯齿,我尝试使用多采样渲染缓冲区,然后使用缓冲区blit通道解析样本。
-
按照延迟着色的传统,我使用发出 3 种颜色输出的专用着色器渲染场景:
- 位置
- 法线
- 漫反射和镜面反射
-
然后,这些用于光照计算通道,从而生成最终的场景纹理
-
场景纹理使用简单的着色器在全屏四边形上渲染到屏幕上
正如您可能猜到的那样,屏幕上的 MSAA 在渲染到屏幕时不会应用于场景纹理的内容: 为了实现抗锯齿,我因此选择在步骤 1) 中使用多采样渲染缓冲区,并引入了额外的步骤 1.1) 进行分辨率。当然,多重采样仅对颜色图是必要/有用的,而不是其他 2 张地图。
我的问题和问题是,显然,具有多个渲染缓冲区/颜色附件的帧缓冲区只能为相同类型的附件定义;这意味着如果一个附件是多重采样的,那么所有其他附件都必须是多采样的。
这在分辨率期间成为位置和法线缓冲区的问题,因为几何体和照明会因抗锯齿而受到影响。
- 我对帧缓冲区附件的理解是否有效?
- 有没有办法绕过它,以便在漫反射和高光贴图上仍然具有多重采样,但不影响其他贴图?
// Create the frame buffer for deferred shading: 3 color attachments and a depth buffer
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
{
// - Position color buffer
glGenRenderbuffers(1, &gPosition);
glBindRenderbuffer(GL_RENDERBUFFER, gPosition);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gPosition);
// - Normal color buffer
glGenRenderbuffers(1, &gNormal);
glBindRenderbuffer(GL_RENDERBUFFER, gNormal);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, gNormal);
// - Color + specular color buffer
glGenRenderbuffers(1, &gColorSpec);
glBindRenderbuffer(GL_RENDERBUFFER, gColorSpec);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, gColorSpec);
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);
// - Generate the depth buffer for rendering
glGenRenderbuffers(1, &sceneDepth);
glBindRenderbuffer(GL_RENDERBUFFER, sceneDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_DEPTH_COMPONENT, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sceneDepth);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Create a frame buffer with 3 attachments for sample resolution
glGenFramebuffers(1, &gFrameRes);
glBindFramebuffer(GL_FRAMEBUFFER, gFrameRes);
{
glGenTextures(1, &gPositionRes);
glBindTexture(GL_TEXTURE_2D, gPositionRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPositionRes, 0);
glGenTextures(1, &gNormalRes);
glBindTexture(GL_TEXTURE_2D, gNormalRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormalRes, 0);
glGenTextures(1, &gColorSpecRes);
glBindTexture(GL_TEXTURE_2D, gColorSpecRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gColorSpecRes, 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ...
//
// Once the scene is rendered, resolve:
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gFrameRes);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glReadBuffer(GL_COLOR_ATTACHMENT1);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glReadBuffer(GL_COLOR_ATTACHMENT2);
glDrawBuffer(GL_COLOR_ATTACHMENT2);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
上述代码示例的结果是,发光对象的边缘显示出不适当的暗/黑或亮/白像素的伪影,可能是因为它们的位置和/或法线在此过程中发生了变化。
这在分辨率期间成为位置和法线缓冲区的问题,因为几何体和照明会因抗锯齿而受到影响。
应该是。
位置和法线不使用多重采样,而获取的漫反射/镜面反射颜色却是逻辑上不连贯的。请记住什么是多重采样:每个像素有多个样本,来自重叠三角形的不同数据可能会写入同一像素中的不同样本。因此,您可以在同一个像素中拥有来自两个或多个三角形的漫反射/镜面反射颜色。但这也意味着您也应该拥有与每个子像素颜色相关联的位置和法线。否则,您的照明通行证将没有意义;您将对未生成它们的颜色使用位置和正常值。
使用延迟渲染进行适当的多重采样是昂贵的。使其工作的唯一方法是对所有内容进行多重采样,然后在每个样本级别上执行光照通道计算。由于与超级采样相比,多重采样的大部分性能提升不是按样本进行计算,因此您只能在几何通道中获得多重采样(而不是超级采样)的好处,而不是光照通道。
这就是为什么人们在使用延迟渲染时尽量避免多重采样的原因。这就是为什么存在像FXAA之类的伪抗锯齿技术的原因。