OpenGL阴影peter-panning

Fla*_*lau 6 c++ opengl glsl shadow

我通过两个绘制过程向一个场景添加阴影,一个是深度图,另一个是普通帧缓冲.

使用深度图时不使用偏差,会有很多阴影痤疮.

阴影痤疮没有偏见

通过向深度图检查添加偏差来解决此问题.

阴影与偏见

但是,当光线移动到不同的角度时,这会导致阴影从对象"分离".

彼得平移

我相信这种效应被称为peter-panning,是由于不同角度使用更大的偏差造成的.

通常的解决方法似乎是在绘制阴影贴图时剔除三角形,但是由于地板平面是2D对象,我不相信这会正常工作.

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

如何将peter-panning固定在这样的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;
}
Run Code Online (Sandbox Code Playgroud)

片段着色器

#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);
}
Run Code Online (Sandbox Code Playgroud)

Ers*_*oat 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

问题是确定阴影贴图中每个深度的最佳偏移。如果应用的偏移量不够,深度冲突仍然会存在。如果您应用非常大的偏移量,彼得·潘宁就会变得引人注目。偏移应取决于阴影贴图的精度以及表面相对于光源方向的斜率。

OpenGL 可以自动计算偏移量并将其添加到存储在 Z 缓冲区中的值。您可以使用 glPolygonOffset 函数设置偏移。有两个参数可用:取决于表面坡度的偏移乘数,以及确定附加最小可能偏移量的值(取决于阴影贴图的格式):

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