Я реализовал VSM (а также ESM) в своем движке, но результаты для меня не такие, как я ожидал, и я видел во многих примерах, опубликованных в сети.
Я установил фильтрацию карт теней на GL_LINEAR, но когда я сравниваю результат с обычной картой теней, это заметно хуже.
Я пытался вычислить моменты непосредственно в точечном шейдере или получить его из текстуры, как в большинстве уроков, но результаты те же.
Код:
uniform samplerCubeShadow shadowMap;
...
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d2=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
...
float shadowTexel=texture(shadowMap,vec4(coord.xyz,d2));
// VSM (Variance Shadow Map)
// get partial derivatives
float dx = dFdx(d2);
float dy = dFdy(d2);
vec2 moments = vec2(d2, d2*d2+0.25*(dx*dx+dy*dy));
return chebychevInequality(moments, shadowTexel);
Используя этот код, я получаю результаты, как на картинке выше. Я также пытался не использовать samplerCubeShadow, но samplerCube, но результаты еще хуже. Сначала у меня были жесткие тени. Во-вторых, тени не заполняют область как следует, когда получают моменты от другой текстуры. Посмотрите на второй экран. Вот также посмотрите на сгенерированную карту куба. Это не похоже на карту глубины, даже если я добавлю глубину / момент 1 во все 3 канала.
Шейдер для получения моментов:
// Vartex shader
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// Fragment shader
float depth = v_position.z / v_position.w ;
depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system
float moment1 = depth;
float moment2 = depth * depth;
// Adjusting moments (this is sort of bias per pixel) using derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy) ;
FragColor = vec4( moment1,moment2, 0.0, 0.0 );
Я действительно застрял. Я надеюсь, что вы поможете мне решить мои проблемы.
РЕДАКТИРОВАТЬ:
Я нашел решение второй проблемы. Я включил смешивание, и это дает мне неправильную карту глубины.
Я также получил лучший результат для первой проблемы, но теперь я борюсь с надлежащей глубиной, чтобы сравнить ее с глубиной из карты теней.
В простом СМ я использую этот код:
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
где pos это позиция в View Space. Затем я читаю значения из карты теней, используя:
texture(shadowMap,vec4(coord.xyz,d))
В VSM я храню глубину в канале R в текстуре RG32F. Значение глубины рассчитывается следующим образом:
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
Затем в шейдере для точечного освещения я использую вектор координат (как в стандартном SM) для чтения значений из карты теней, и это работает нормально. Но проблема в этой части:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
В каких координатах должно быть расстояние? В каких координатах у меня есть глубина от карты теней? Это должно быть линейным? Может ли кто-нибудь объяснить мне это? Сейчас я немного растерялся, я пытался получить много способов, и все результаты оказались не такими, как я ожидал.
РЕДАКТИРОВАТЬ 2: Следуя совету JarkkoL и этому руководству, я изменил свой код. Теперь я сохраняю глубину, используя этот код:
// VS
v_position=ModelViewMatrix*Vertex;
gl_Position=ProjectionMatrix*v_position;
// FS
const float Near = 0.1;
const float Far = 90.0; // camera far plane
const float LinearDepthConstant = 1.0 / (Far - Near);
float depth = length(v_position)*LinearDepthConstant;
И я сравниваю это со значением, которое я получаю таким образом:
float dist=length( vec3(inverse(ViewMatrix)*vec4(pos,1.0)) - PointLight.position )*LinearDepthConstant; // pos is read from depth buffer and is in view space so I want invert it to world space as it was in tutorial
И вот результат:
В красном круге я отметил видимые границы между гранями кубической карты. Там все еще что-то не так. Я думаю, что это может быть что-то с инверсией View Matrix, но я не уверен.
Ответы:
Что касается швов кубической карты, вы можете просто фильтровать по краям.
Видеть:
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
Что касается фактического качества теней, то все преимущества методов VSM и ESM заключаются в специальной форме, которую принимает тест видимости. Возможно, вы захотите ввести постоянный фактор для чрезмерного или недостаточного затемнения теней, чтобы край не был таким твердым.
Для данных ESM это просто:
источник