Я получил базовое 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), ¢er, &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 на квадрат действительно исправило это.