Проблемы с каскадными картами теней - PullRequest
0 голосов
/ 12 января 2020

Я получил базовое c отображение теней с помощью одного направленного источника света, работающего в небольшой воксельной игре, над которой я работаю, и я пытался обновить ее до Cascaded Shadow Maps. Я посмотрел несколько учебных пособий - в основном , это на OGLDev и , это на LWJGL - и я реализовал его в Rust с GLium. Проблема в том, что даже после долгих исправлений он все равно не дает правильных теней, и он мерцает и сдвигается, когда я осматриваюсь. Вот видео о том, что происходит: https://youtu.be/bqmjqP2Y5-M

Мне кажется, проблема в том, как я вычисляю матрицу проецирования света для просмотра:

        // Half of the vertical FOV
        let fovy = radians(90.0) * 0.5;
        let tan_fy = fovy.tan();
        let fovx = fovy * aspect;
        let tan_fx = fovx.tan();

        // This is the camera view matrix, not projection
        let m = self.view_mat();
        // camera space -> world space
        // I'm using nalgebra for math, so this is the inverse
        let mi = m.try_inverse().unwrap();

        (0..num_cascades)
            .map(|i| {
                // Calculate frustum corners in camera space
                let near = VIEW_DISTANCE * (i as f32 / num_cascades as f32);
                let far = VIEW_DISTANCE * ((i + 1) as f32 / num_cascades as f32);
                let x_near = near * tan_fx;
                let x_far = far * tan_fx;
                let y_near = near * tan_fy;
                let y_far = far * tan_fy;

                // The frustum corners
                let n_a = Vec3::new(x_near, y_near, near);
                let n_b = Vec3::new(x_near, -y_near, near);
                let n_c = Vec3::new(-x_near, y_near, near);
                let n_d = Vec3::new(-x_near, -y_near, near);

                let f_a = Vec3::new(x_far, y_far, far);
                let f_b = Vec3::new(x_far, -y_far, far);
                let f_c = Vec3::new(-x_far, y_far, far);
                let f_d = Vec3::new(-x_far, -y_far, far);

                // Transform them to world space
                let v: Vec<_> = vec![n_a, n_b, n_c, n_d, f_a, f_b, f_c, f_d]
                    .into_iter()
                    .map(|x| (mi * na::Vector4::new(x.x, x.y, x.z, 1.0)).xyz())
                    .collect();

                // World spaace min and max points
                let min = v.iter().fold(
                    Vec3::new(10_000_000.0, 10_000_000.0, 10_000_000.0),
                    |x, a| x.zip_map(a, f32::min),
                );
                let max = v.iter().fold(
                    -Vec3::new(10_000_000.0, 10_000_000.0, 10_000_000.0),
                    |x, a| x.zip_map(a, f32::max),
                );
                // Center of the world-space bounding box of the frustum.
                // I also tried using the centroid of the frustum corners.
                let center = (min + max) * 0.5;
                let center = Point3::from(center);

                let view_up = Vec3::y();
                let view_right = Unit::new_normalize(sun_dir.cross(&view_up));
                let view_up = view_right.cross(&sun_dir);
                // world space -> light space
                let view =
                    na::Matrix4::look_at_rh(&(center - sun_dir), &center, &view_up);

                // To light space
                let v: Vec<_> = v
                    .into_iter()
                    .map(|x| (view * na::Vector4::new(x.x, x.y, x.z, 1.0)).xyz())
                    .collect();

                // And get a bounding box for orthographic projection
                let min = v.iter().fold(
                    Vec3::new(10_000_000.0, 10_000_000.0, 10_000_000.0),
                    |x, a| x.zip_map(a, f32::min),
                );
                let max = v.iter().fold(
                    -Vec3::new(10_000_000.0, 10_000_000.0, 10_000_000.0),
                    |x, a| x.zip_map(a, f32::max),
                );

                // Orthographic projection matrix encompassing the frustum
                let proj =
                    na::Matrix4::new_orthographic(min.x, max.x, min.y, max.y, min.z, max.z);

                // This is the final light view-projection matrix
                proj * view
            })
            .collect()

Мой шейдер в В этом случае проблема:

// Among others
uniform vec3 camera_pos;
uniform mat4 light_mats[NUM_CASCADES];
uniform sampler2DArrayShadow shadow_map;

// ...

float shadow(vec3 pos) {
    float far = 1024;
    float z = length(pos - camera_pos);
    uint i = uint(float(NUM_CASCADES) * z/far);

    float bias = 0.00001;
    vec4 map_coord = light_mats[i] * vec4(pos, 1.0);

    // * 0.5 + 0.5 because it needs to go from 0 to 1 instead of -1 to 1
    float depth = (map_coord.z - bias) / map_coord.w * 0.5 + 0.5;
    vec2 coords = map_coord.xy / map_coord.w * 0.5 + 0.5;

    return texture(shadow_map, vec4(coords, float(i), depth));
}

РЕДАКТИРОВАТЬ:

Я нашел и исправил некоторые проблемы, но в затененных углах все еще есть мерцающие полосы света. Оказывается, причина, по которой его было так трудно диагностировать, состоит в том, что было много проблем, действующих одновременно, поэтому он явно не совпадал с симптомами любого из них.

Черные линии, движущиеся вокруг всего, были исправлены с уклоном глубины в масштабе (обычно это делается с помощью glPolygonOffset, но я только что сделал это в шейдере.) Была проблема, когда, если вы посмотрите в определенных направлениях (обычно вдоль координатных осей), реальные тени исчезнут, и вы получите гигантский тени, не связанные с геометрией, движущейся вокруг всего. Когда я смотрел с точки зрения источника света, карта теней растягивалась на одной оси всякий раз, когда это происходило. Я на самом деле не знаю, как это вызвало проблему, но исправление прямоугольной проекции света c на квадрат действительно исправило это.

...