OpenGL影子抖动问题

6
我正在使用OpenGL将阴影添加到场景中,通过进行两次绘制通道来实现,一次是深度贴图,一次是正常的帧缓冲。

在不使用偏移的情况下,在深度贴图时会有很多阴影痤疮。

shadow acne without bias

为深度贴图检查添加偏移可以解决这个问题。

shadow with bias

然而,当光线移动到不同角度时,这会导致阴影与物体“脱离”。

peter panning

我认为这种效果称为彼得潘效应,是由于在不同角度使用更大的偏移导致的。

通常修复此问题的方法似乎是在绘制阴影映射时剔除背面三角形,但由于地面平面是一个2D对象,我认为它不会正常工作。

我使用的实际地形是程序生成的,因此创建其3D版本并不像在这个简单的示例中那么简单。

如何在这样的2D对象上修复彼得潘效应呢?


顶点着色器

#version 400

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texture_coords;

out VS_OUT {
    vec4 position;
    vec3 normal;
    vec2 texture_coords;
    vec4 shadow_position;
} vs_out;

uniform mat4 model;
uniform mat4 model_view;
uniform mat4 model_view_perspective;
uniform mat3 normal_matrix;
uniform mat4 depth_matrix;

void main() {
    vec4 position_v4 = vec4(position, 1.0);

    vs_out.position = model_view * position_v4;
    vs_out.normal = normal_matrix * normal;
    vs_out.texture_coords = texture_coords;
    vs_out.shadow_position = depth_matrix * model * position_v4;

    gl_Position = model_view_perspective * position_v4;
}

片元着色器
#version 400

in VS_OUT {
    vec4 position;
    vec3 normal;
    vec2 texture_coords;
    vec4 shadow_position;
} fs_in;

out vec4 colour;

uniform mat4 view;
uniform mat4 model_view_perspective;
uniform vec3 light_position;
uniform vec3 emissive_light;
uniform float shininess;
uniform int textured;
uniform sampler2D tex;
uniform sampler2DShadow shadow_texture;

void main() {
    const vec3 specular_albedo = vec3(1.0, 0.8, 0.6);

    colour = vec4(0.8, 0.8, 0.8, 0.8);
    if(textured != 0) {
        colour = texture(tex, fs_in.texture_coords);
    }

    vec3 light_direction = normalize(light_position);
    vec3 normal = normalize(fs_in.normal);

    float visibility = 1.0;
    if(fs_in.shadow_position.z <= 1.0) {
        float bias = max(0.05 * (1.0 - dot(normal, light_direction)), 0.005);
        if(fs_in.shadow_position.z > texture(shadow_texture, fs_in.shadow_position.xyz, 0.0) + bias){
            visibility = 0.0;
        }
    }

    /* Ambient */
    vec3 ambient = colour.xyz * 0.1;

    /* Diffuse */
    vec3 diffuse = visibility * (clamp(dot(normal, light_direction), 0, 1) * colour.xyz);

    /* Specular */
    vec3 specular = vec3(0.0);
    if(dot(normal, light_direction) > 0) {
        vec3 V = normalize(-fs_in.position.xyz);
        vec3 half_dir = normalize(light_direction + V);
        specular = visibility * (pow(max(dot(normal, half_dir), 0.0), shininess) * specular_albedo.xyz);
    }

    colour = vec4(((ambient + diffuse) * colour.xyz) + specular + emissive_light, 1.0);
}

在阴影通道中,您可以通过翻转三角形法线并将其偏移至着色器中的观察方向上的偏差来绘制地形两次 - 这将使绘制的三角形数量加倍,但它将允许您启用背面剔除(因此应该在最小化时平衡)。是的,在这种技术中,始终会出现Peter-Panning。 - BeyelerStudios
在阴影图构建阶段通常会引入斜率缩放偏移来解决这个问题。查找 glDepthOffset。请注意,这将在渲染期间破坏某些深度缓冲区优化,但这可能并不是很重要,因为您只是填充深度缓冲区而不运行复杂的片段着色器。 - Andon M. Coleman
谢谢,我会尝试这些的。 - flau
如果你的阴影计算中存在偏差,那么你可能无法避免彼得潘现象。你可以尝试使用更高分辨率的阴影贴图,以及更好的视锥体边界来解决这个问题。 - ChaoSXDemon
1个回答

5

https://msdn.microsoft.com/en-us/library/windows/desktop/ee416324(v=vs.85).aspx

计算近平面和远平面也有助于避免Peter Panning。 斜率-缩放深度偏差
如前所述,自阴影可能导致阴影痤疮。添加过多的偏差可能会导致Peter Panning。此外,与光线相对较陡峭的多边形比与光线相对较浅的多边形更容易受到投影混叠的影响。因此,每个深度贴图值可能需要根据多边形相对于光线的坡度不同而进行不同的偏移。
Direct3D 10+硬件可以根据其相对于视角方向的坡度来偏置多边形。这会导致在沿着光线方向边缘观察多边形时对其应用大偏差,但不会对直接面向光线的多边形应用任何偏差。图10说明了当针对同一无偏斜率进行测试时,两个相邻像素如何在阴影和非阴影之间交替变化。

http://www.sunandblackcat.com/tipFullView.php?l=eng&topicid=35

问题是确定阴影图中每个深度的最佳偏移量。如果应用的偏移量不足,则仍会出现z-fighting。如果应用的偏移量非常大,则Peter Panning将变得明显。偏移应取决于阴影图的精度以及表面相对于光源方向的坡度。 OpenGL可以自动计算并添加偏移值到存储在Z-Buffer中的值中。您可以使用glPolygonOffset函数设置偏移量。有两个参数可用:偏移量的乘数,取决于表面的坡度,以及确定最小可能偏移量的额外值(取决于阴影图的格式)。

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接