批评SSAO算法OpenGL
Crytek SSAO Algorithm OpenGL
我正在尝试实现 Crytek 屏幕 space 环境遮挡算法的简单变体。
我理解的算法;
- 对于一个像素,p,在视图 space.
的球体中围绕 p 采样
- 将采样点sp投影到屏幕space。
- 将采样点的深度与当前像素的深度进行比较。
基本上就这些了。如果采样点的深度更高(它位于几何之外),它不会遮挡当前像素 (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-axis 时才会出现伪像,因此转换可能有些可疑,尽管在渲染对象和相机等时工作正常。
从基本上任何其他角度看时,将遮挡因子设置为 0.5 看起来就像您期望的那样(这将使您在所有颜色通道中变灰)。
意外整数除法后的结果固定为浮点数除法。
添加了闪烁的视频here。
有什么线索吗?
编辑:检测到四舍五入问题。
编辑:在沿着 z-axis.
移动时向工件添加了视频 link
您的代码有两个可疑之处。
整数除法
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 轴时,当与上面的舍入误差配对时,我认为这就是发生的情况。
我正在尝试实现 Crytek 屏幕 space 环境遮挡算法的简单变体。
我理解的算法;
- 对于一个像素,p,在视图 space. 的球体中围绕 p 采样
- 将采样点sp投影到屏幕space。
- 将采样点的深度与当前像素的深度进行比较。
基本上就这些了。如果采样点的深度更高(它位于几何之外),它不会遮挡当前像素 (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-axis 时才会出现伪像,因此转换可能有些可疑,尽管在渲染对象和相机等时工作正常。
从基本上任何其他角度看时,将遮挡因子设置为 0.5 看起来就像您期望的那样(这将使您在所有颜色通道中变灰)。
意外整数除法后的结果固定为浮点数除法。
添加了闪烁的视频here。
有什么线索吗?
编辑:检测到四舍五入问题。 编辑:在沿着 z-axis.
移动时向工件添加了视频 link您的代码有两个可疑之处。
整数除法
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 轴时,当与上面的舍入误差配对时,我认为这就是发生的情况。