c++ - Crytek SSAO 算法 OpenGL

标签 c++ opengl ssao

我正在尝试实现 Crytek 屏幕空间环境遮挡算法的简单变体。

我理解的算法;

  1. 对于像素 p,在 View 空间的球体中围绕 p 进行采样。
  2. 将采样点 sp 投影到屏幕空间。
  3. 将采样点的深度与当前像素的深度进行比较。

基本上就这些了。如果采样点的深度更高(它位于几何之外),它不会遮挡当前像素 (p)。

float z = gl_FragCoord.z; // depth-buffer value for the current pixel
int occluding_points = 0;
vec4 fPosition = model_transformation * vec4(position, 1.0f); // Really from vertex shader
#ifdef CRYTEK_AO
    const int NUM_SAMPLES = 10;
    float R = 3.0f;
    const float[10] steps = float[](0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f);
    for (int sample_id = 0; sample_id < NUM_SAMPLES; sample_id++) {
        // 1. Generate sample point in world space.
        float ang = steps[sample_id];
        vec4 sample_point = vec4(R * cos(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.x,
                                 R * sin(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.y,
                                 R * sin(M_PI * ang) + fPosition.z,
                                 1.0f);
        // 2. Transform sample point from view space to screen space to get its depth value.
        sample_point = projection * camera_view * sample_point; // Clip space
        sample_point = sample_point / sample_point.w;           // Perspective division - Normalized device coordinate
        float sample_depth = 0.5f * (sample_point.z + 1.0f);    // Viewport transform for z - window space

        // 3. Check whether sample_point is behind current pixel depth.
        if (sample_depth > z) { occluding_points++; }
    }
    occlusion_factor = occluding_points / float(NUM_SAMPLES);
    // Diffuse, specular components removed
    total_light += vec3(ambient_intensity) * (1.0f - occlusion_factor); // Ambient factor
    outColor = total_light;
#endif

下面是它的外观截图。出于某种原因,伪像仅在向下看 z 轴时出现,因此转换可能有些可疑,尽管在渲染对象和相机等时工作正常。

Ambient occlusion gone wrong...

基本上从任何其他角度看时,您都希望将遮挡因子设置为 0.5(这将使您在所有颜色 channel 中变灰)。

Nothing abnormal

意外整数除法后的结果固定为 float 除法。 enter image description here

添加了一个视频 here的闪烁。

有什么线索吗?

编辑:检测到四舍五入问题。 编辑:在沿 z 轴移动时向工件添加了视频链接。

最佳答案

您的代码有两处可疑之处。

整数除法

occlusion_factor = occluding_points / NUM_SAMPLES;

只需将 occluding_points 的类型更改为 float 就可以了。

采样

    vec4 sample_point = vec4(R * cos(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.x,
                             R * sin(2 * M_PI * ang) * sin(M_PI * ang) + fPosition.y,
                             R * sin(M_PI * ang) + fPosition.z,
                             1.0f);

这每次都会为您提供来自世界坐标中相同螺旋的样本,因此对于正确的表面,您将根据视角获得伪影。当您向下看 z 轴时,当与上面的舍入误差配对时,我认为这就是发生的情况。

关于c++ - Crytek SSAO 算法 OpenGL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44337017/

相关文章:

c++ - 如何使用boost属性树使用boost解析json字符串中数组中的元素?

c++ - 在多线程中消耗链表队列

c++ - 使用自定义屏幕缓冲区在 Windows 控制台中启用 ANSI 颜色支持 (C++)

opengl - 使用 OpenGL 绘制图 block 的技术

c++ - OpenGL - 纹理加载不正确

opengl - 这个深度缓冲区可视化应该看起来平滑吗?

opengl-es - 纯深度 SSAO 闪烁

c++ - 对常量数组的 undefined reference

java - openGL 中的帧速率和绘制流程