【问题标题】:SSAO Changing Dramatically With Camera AngleSSAO 随摄像机角度发生巨大变化
【发布时间】:2013-07-15 05:19:07
【问题描述】:

我一直在 OpenGL 中工作 SSAO。我决定在 OpenGL 中为我的延迟渲染器实现 this 教程中的 SSAO。不幸的是,我一直无法让它正常工作。 SSAO 变暗的区域会根据相机的位置而发生很大变化。我知道当相机移动时 SSAO 的输出可能会有一些变化,但这比我在其他 SSAO 实现中观察到的要大得多。

这是片段着色器代码

void main() {

    vec3 origin = positionFromDepth(texture2D(gDepth, samplePosition));
    vec3 normal = texture2D(gNormal, samplePosition).xyz; //multiplying this 
//by 2 and subtracting 1 doesn't seem to help
    vec2 random = getRandom(samplePosition);

    float radius = uRadius/origin.z;
    float occlusion = 0.0;
    int iterations = samples/4;

    for (int i = 0; i<iterations; i++) {
        vec2 coord1 = reflect(kernel[i], random)*radius;
        vec2 coord2 = vec2(coord1.x*0.707 - coord1.y*0.707, coord1.x*0.707 + coord1.y*0.707);
        occlusion += occlude(samplePosition, coord1 * 0.25, origin, normal);
        occlusion += occlude(samplePosition, coord2 * 0.50, origin, normal);
        occlusion += occlude(samplePosition, coord1 * 0.75, origin, normal);
        occlusion += occlude(samplePosition, coord2, origin, normal);
    }

    color = vec4(origin, 1);

}

positionFromDepth() 函数:

vec3 positionFromDepth(float depth) {
    float near = frustrumData.x;
    float far = frustrumData.y;
    float right = frustrumData.z;
    float top = frustrumData.w;
    vec2 ndc;           
    vec3 eye;             
    eye.z = near * far / ((depth * (far - near)) - far);
    ndc.x = ((gl_FragCoord.x/buffersize.x) - 0.5) * 2.0; 
    ndc.y = ((gl_FragCoord.y/buffersize.y) - 0.5) * 2.0;
    eye.x = (-ndc.x * eye.z) * right/near;
    eye.y = (-ndc.y * eye.z) * top/near;
    return eye;
}

还有occlude() 函数:

float occlude(vec2 uv, vec2 offsetUV, vec3 origin, vec3 normal) {
    vec3 diff = positionFromDepth(texture2D(gDepth,(uv+offsetUV)))-origin;
    vec3 vec = normalize(diff);
    float dist = length(diff)/scale;
    return max(0.0,dot(normal,vec)-bias)*(1.0/(1.0+dist))*intensity;
}

我感觉问题可能出在positionFromDepth() 函数中,除了我对渲染器的照明阶段使用相同的代码,效果很好(我认为)。我已经检查了这段代码一千次,没有发现任何明显错误的地方。我尝试了biasradiusintenistyscale 的各种值,但这似乎不是问题所在。我担心我的法线或位置是错误的,所以这里是它们的一些屏幕截图:

重构后的位置: 和正常的缓冲区:

我会包含一个遮挡缓冲区的图像,但问题只有在相机移动时才最明显,而图像无助于显示。

有人知道这里出了什么问题吗?

【问题讨论】:

  • 嗨!我在实施相同的教程时遇到了同样的问题。你能告诉我,你解决了这个问题吗?你做了什么?

标签: opengl glsl deferred-rendering ssao


【解决方案1】:

奇怪的是,乘以 2 减去 1 对法线贴图没有帮助。这通常是为了克服与以无符号/归一化纹理格式存储法线相关的问题。除非您的普通 G-Buffer 是有符号/非规范化格式,否则您可能需要在首次写入时使用 * 0.5 + 0.5 打包和解压缩法线,在对纹理进行采样时使用 * 2.0 - 1.0

无论如何,SSAO 有多种方法,其中许多甚至根本不使用表面法线。因此,关于将法线存储在哪个向量空间的讨论经常被忽略。

我强烈怀疑您的法线在视图空间中,而不是在世界空间中。如果您将法线乘以顶点着色器中的“法线矩阵”,就像许多教程一样,那么您的法线将在视图空间中。

事实证明,视图空间法线真的没那么有用,现在使用世界空间法线效果更好的后处理效果的数量。大多数现代延迟着色引擎(例如 Unreal Engine 4、CryEngine 3 等)将普通 G-Buffer 存储在世界空间中,然后在像素着色器中将其转换为视图空间(如果需要)。


顺便说一下,我已经包含了一些用于从传统深度缓冲区重建对象空间位置的代码。您似乎正在使用视图空间位置/法线。您可能想尝试对象/世界空间中的所有内容。

mat4 inv_mv_mat 中的平面; 在 vec2 紫外线中; ... 浮动线性Z(浮动z) { #ifdef INVERT_NEAR_FAR 常量浮动 f = 2.5; 常量浮动 n = 25000.0; #别的 常量浮动 f = 25000.0; 常量浮动 n = 2.5; #万一 返回 n / (f - z * (f - n)) * f; } vec4 重建位置(浮点深度) { 深度=线性Z(深度); vec4 pos = vec4 (uv * depth, -depth, 1.0); vec4 ret = (inv_mv_mat * pos); 返回 ret / ret.w; }

在延迟着色光照通道的顶点着色器阶段需要进行一些额外设置,如下所示:

#版本 150 核心 在 vec4 vtx_pos 中; 在 vec2 vtx_st; 统一 mat4 modelview_mat; // 构建 G-Buffer 时使用的矩阵 统一 mat4 camera_matrix; // 用于在视口上拉伸 G-Buffer 的矩阵 统一浮动 buffer_res_x; 统一浮动 buffer_res_y; 出 vec2 tex_st; 平整 mat4 inv_mv_mat; 输出 vec2 uv; // 硬编码 45 度 FOV //const float fovy = 0.78539818525314331; // NV 吐在下面一行! //const float fovy = 弧度 (45.0); //const float tan_half_fovy = tan (fovy * 0.5); 常量浮动 tan_half_fovy = 0.41421356797218323; 浮动方面 = buffer_res_x / buffer_res_y; vec2 inv_focal_len = vec2 (tan_half_fovy * aspect, tan_half_fovy); 常量 vec2 uv_scale = vec2 (2.0, 2.0); 常量 vec2 uv_translate = vec2 (1.0, 1.0); 无效主要(无效) { inv_mv_mat = 逆(modelview_mat); tex_st = vtx_st; gl_Position = camera_matrix * vtx_pos; uv = (vtx_st * uv_scale - uv_translate) * inv_focal_len; }

【讨论】:

  • 感谢您的回答!查看您的 reconstruct_pos 函数有很大帮助。看起来我的问题是使用 gl_Fragcoord 而不是使用 uv 作为像素。 (使用 gl_Fragcoord 意味着每个样本都有相同的 x 和 y,这是错误的。)尽管如此,我仍然对法线的打包和解包感到困惑。为什么有必要?
  • 您需要对法线进行打包和解包,因为“通常”纹理无法存储 0.0-1.0 范围之外的值。有一些纹理格式允许这样做,但通常您的 G-Buffer 和法线贴图纹理本身存储在简单的无符号归一化纹理(有时称为 unorm)中。法线,尤其是在世界空间中的法线,可能具有具有负值的分量,因此以这些纹理格式存储它们需要重新缩放和偏置(例如 * 2 - 1)
  • 这是有道理的,但我为什么要在世界空间中存储法线呢?在视图空间中,法线不可能为负数,这意味着我不必打包它们并且可以获得更好的精度。我不明白为什么世界空间法线会更好。
猜你喜欢
  • 2020-02-20
  • 1970-01-01
  • 2021-01-15
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多