Ok. Итак, я всю последнюю неделю возился с тенями в своем игровом движке. Я в основном реализовал каскадные карты теней (CSM), но у меня возникла небольшая проблема с затенением, которую я просто не могу решить.
Единственный источник света в этой сцене - это направленный свет (солнце), указывающий на {-0,1 -0,25 -0,65}. Я вычисляю 4 набора границ усеченной пирамиды для четырех разбиений моих CSM с помощью этого кода:
// each projection matrix calculated with same near plane, different far
Frustum make_worldFrustum(const glm::mat4& _invProjView) {
Frustum fr; glm::vec4 temp;
temp = _invProjView * glm::vec4(-1, -1, -1, 1);
fr.xyz = glm::vec3(temp) / temp.w;
temp = _invProjView * glm::vec4(-1, -1, 1, 1);
fr.xyZ = glm::vec3(temp) / temp.w;
...etc 6 more times for ndc cube
return fr;
}
Для света я получаю такую матрицу вида:
glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});
Затем я создаю каждую орто-матрицу из границ каждой пирамиды:
lightMatVec.clear();
for (auto& frus : cam.frusVec) {
glm::vec3 arr[8] {
glm::vec3(viewMat * glm::vec4(frus.xyz, 1)),
glm::vec3(viewMat * glm::vec4(frus.xyZ, 1)),
etc...
};
glm::vec3 minO = {INFINITY, INFINITY, INFINITY};
glm::vec3 maxO = {-INFINITY, -INFINITY, -INFINITY};
for (auto& vec : arr) {
minO = glm::min(minO, vec);
maxO = glm::max(maxO, vec);
}
glm::mat4 projMat = glm::ortho(minO.x, maxO.x, minO.y, maxO.y, minO.z, maxO.z);
lightMatVec.push_back(projMat * viewMat);
}
У меня есть 4 слоя TEXTURE_2D_ARRAY, привязанные к 4 буферам кадра, в которые я рисую сцену с помощью очень простого вершинного шейдера (фрагмент отключен или сквозной альфа-канал).
Затем я рисую финальную сцену. Вершинный шейдер выводит четыре текстовых координаты тени:
out vec3 slShadcrd[4];
// stuff
for (int i = 0; i < 4; i++) {
vec4 sc = WorldBlock.skylMatArr[i] * vec4(world_pos, 1);
slShadcrd[i] = sc.xyz / sc.w * 0.5f + 0.5f;
}
И фрагментный шейдер, который определяет разбиение для использования с:
int csmIndex = 0;
for (uint i = 0u; i < CameraBlock.csmCnt; i++) {
if (-view_pos.z > CameraBlock.csmSplits[i]) index++;
else break;
}
И сэмплируем массив карты теней с помощью этой функции:
float sample_shadow(vec3 _sc, int _csmIndex, sampler2DArrayShadow _tex) {
return texture(_tex, vec4(_sc.xy, _csmIndex, _sc.z)).r;
}
И это сцена, которую я получаю (с каждым разделением слегка окрашенным и наложенными 4 глубинными слоями): Большой! Выглядит неплохо.
Но если я поверну камеру немного вправо: Тогда тени начнут исчезать (и в зависимости от угол, появляющийся там, где их не должно быть).
У меня включен GL_DEPTH_CLAMP, так что проблема не в этом. Я отбраковываю передние грани, но отключение этого значения не имеет значения.
Что мне не хватает? Я чувствую, что это проблема с одним из моих прогнозов, но все они мне кажутся правильными. Спасибо!
РЕДАКТИРОВАТЬ: нарисованы все четыре усеченных элемента света. Все они есть, но только z меняется относительно камеры (см. Комментарий ниже):
РЕДАКТИРОВАТЬ: Возможно, более полезно, так выглядят усеченные пирамиды, когда я обновляю их только один раз, когда камера находится в (0,0,0) и направлена вперед (0,1,0). Также на этот раз я нарисовал их с тестированием глубины.
ВАЖНОЕ РЕДАКТИРОВАНИЕ: похоже, что эта проблема напрямую связана с матрицей обзора источника света, в настоящее время:
glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});
Изменение значений для глаза и цели, похоже, влияет на искаженные тени. Но я не знаю, что мне на самом деле следует установить? Это должно быть легко для кого-то, кто понимает лучше меня: D