c++ - OpenGL 阴影平移

标签 c++ opengl glsl shadow

我通过两次绘制过程为 OpenGL 中的场景添加阴影,一次绘制到深度图,一次绘制到普通帧缓冲区。

在使用深度贴图的时候没有使用bias,有很多阴影粉刺。

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);
}

最佳答案

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

Calculating tight near planes and far planes also helps avoid Peter Panning.

Slope-Scale Depth Bias

As previously mentioned, self-shadowing can lead to shadow acne. Adding too much bias can result in Peter Panning. Additionally, polygons with steep slopes (relative to the light) suffer more from projective aliasing than polygons with shallow slopes (relative to the light). Because of this, each depth map value may need a different offset depending on the polygon's slope relative to the light.

Direct3D 10+ hardware has the ability to bias a polygon based on its slope with respect to the view direction. This has the effect of applying a large bias to a polygon that is viewed edge-on to the light direction, but not applying any bias to a polygon facing the light directly. Figure 10 illustrates how two neighboring pixels can alternate between shadowed and unshadowed when testing against the same unbiased slope.

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

The problem is to determine optimal offset for each depth in shadow map. If you apply not enough offset, z-fighting will be still present. If you apply very large offset then Peter Panning will become noticeable. Offset should depend on precision of the shadow map, and on slope of the surface relative to direction to light source.

OpenGL can automatically compute and add offset to values which are stored in Z-Buffer. You can setup offset with glPolygonOffset function. Two parameters are available: multiplier for offset that depends on slope of the surface, and value that determines amount of additional smallest possible offsets (depends on format of shadow map):

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

关于c++ - OpenGL 阴影平移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40671823/

相关文章:

c++ - 在 C++ 中将包含方法定义的类声明放在头文件中是否被认为是错误的形式?

linux - 更新GLEW后,窗口变成黑色

c++ - glVertexAttrib3f 在一台机器上的奇怪行为

opengl-es - 如何绕过 GL_MAX_TEXTURE_SIZE 进行图像处理

c++ - 为什么 const 引用不能延长通过函数传递的临时对象的生命周期?

c++ - explicit operator= call 和 = operator 之间有区别吗?

c++ - 这是 boost::filesystem 中的错误吗?为什么 boost::filesystem::path::string() 在 Windows 和 Linux 上没有相同的签名?

java - OpenGL Interleaved VBO 跨步和偏移

opengl - glBufferSubData什么时候返回?

opengl - 通过顶点着色器进行纹理映射?