1

我一直在 OpenGL 中工作 SSAO。我决定在 OpenGL 中为我的延迟渲染器实现本教程中的 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()函数中,除了我对渲染器的照明阶段使用相同的代码,效果很好(我认为)。我已经检查了这段代码一千次,但没有发现任何明显错误的地方。我尝试了 、 、 和 的各种值biasradius但这intenisty似乎scale不是问题。我担心我的法线或位置是错误的,所以这里有一些它们的屏幕截图:

重建位置: 在此处输入图像描述 和正常缓冲区: 在此处输入图像描述

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

有谁知道这里有什么问题?

4

1 回答 1

3

奇怪的是,乘以 2 减去 1 对法线贴图没有帮助。这样做通常是为了克服与以无符号/归一化纹理格式存储法线相关的问题。* 0.5 + 0.5除非您的普通 G-Buffer 是有符号/非规范化格式,否则您可能需要在首次写入和* 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;
}
于 2013-07-17T17:20:43.077 回答