Сделал наконец Shadow Map. Но как и у всех, наверно, возникли артефакты: не затеняется обратная сторона ножа (на первом скрине) и полоски (на втором скрине). Пробовал разный bias, где-то помогает, но где-то тени вообще пропадают. Сейчас Shadow Map сделан с использованием автоматического сравнения и sampler2DShadow. Но хотелось бы это исправить и сделать все самому в шейдере. Пробовал разные функции (дальше закомментированы), но тени почему-то не появляются.


Создание ShadowMap:
glGenTextures(1, &light->shadowMap); glBindTexture(GL_TEXTURE_2D, light->shadowMap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 2048, 2048, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
Вершинный шейдер Shadow Map:
#version 130
in vec3 vertex_position;
uniform mat4 model_matrix;
uniform mat4 light_matrix;
void main(void) {
gl_Position = light_matrix * (model_matrix * vec4(vertex_position, 1.0));
}Фрагментный шейдер Shadow Map:
#version 130
void main(void) {
}Расчет теней и освещения в шейдере:
float PCF(in sampler2DShadow shadowMap, in vec4 smtexcoord) {
float res = 0.0;
res += textureProj(shadowMap, smtexcoord + vec4(0.0, -0.015, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(0.0, 0.015, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(0.015, -0.015, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(0.015, 0.0, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(0.015, 0.015, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(-0.015, -0.015, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(-0.015, 0.0, 0.0, 0.0));
res += textureProj(shadowMap, smtexcoord + vec4(-0.015, 0.015, 0.0, 0.0));
return (res/8.0);
}
.....
for(int i=0; i<MAXLIGHTSPOT; i++) {
vec3 lightDir = normalize(fragment_lightspot_dir[i]);
float shadow = 1.0;
//if(texture(lightspot_shadowMap[i], (fragment_lightspot_smtexcoord[i].xy/fragment_lightspot_smtexcoord[i].w)).z < (fragment_lightspot_smtexcoord[i].z)/fragment_lightspot_smtexcoord[i].w) {
//if(textureProj(lightspot_shadowMap[i], fragment_lightspot_smtexcoord[i].xyzw) < (fragment_lightspot_smtexcoord[i].z)/fragment_lightspot_smtexcoord[i].w) {
//if(shadow2D(lightspot_shadowMap[i], fragment_lightspot_smtexcoord[i]).x == 0) {
shadow = PCF(lightspot_shadowMap[i], fragment_lightspot_smtexcoord[i]);
//if(shadow == 0.0) { continue; }
//continue;
//}
float spotEffect = dot(lightspot_direction[i], -lightDir);
float spot = float(lightspot_cutoff[i]);
spotEffect = max(pow(spotEffect, lightspot_exponent[i]), 0.0);
float attenuation = spot * spotEffect / (lightspot_attenuation[i].x + lightspot_attenuation[i].y * fragment_lightspot_distance[i] + lightspot_attenuation[i].z * fragment_lightspot_distance[i] * fragment_lightspot_distance[i]);
color += (material_ambient * lightspot_ambient[i]) * attenuation;
color += (material_diffuse * lightspot_diffuse[i]) * attenuation;
color += (((material_specular * lightspot_specular[i]) * max(pow(dot(reflect(lightDir, normal), viewDir), material_shininess), 0.0))) * attenuation;
color *= shadow;
}
FragColor = vec4(color, material_alpha);\n"Так как 8 выборок считаются для каждого пикселя, то при 3 динамических источниках света уже заметны подтормаживания, что конечно плохо.
Если вместо закомментированного кода вставить следующее:
if(textureProj(lightspot_shadowMap[i], fragment_lightspot_smtexcoord[i].xyzw) < (fragment_lightspot_smtexcoord[i].z)/fragment_lightspot_smtexcoord[i].w) {
continue;
}то тормозов нет и заметен даже прирост производительности (т.к. не рассчитывается свет для пикселей в тени), но тени по краям становятся резкими, что выглядит некрасиво.
Существует ли способ размыть края тени, отбрасывая при этом пиксели в тени? Если нет, хотелось бы услышать советы по улучшению/оптимизации.
Правка: добавлены скрины
Рисуй только задние грани геометрии.
А еще можно юзать какую-нибудь модель освещения, что по нормалям затеняет. Тот же фонг с его диффузным освещением.
ialexbr
> Если вместо закомментированного кода вставить следующее:
> то тормозов нет и заметен даже прирост производительности (т.к. не
> рассчитывается свет для пикселей в тени),
Налицо укорения через DFC.
> Существует ли способ размыть края тени, отбрасывая при этом пиксели в тени?
> Если нет, хотелось бы услышать советы по улучшению/оптимизации.
Техники есть, например, сделать цепочку mix/max mips. Более сложенее динамический подгонять веса.
Если железо новое можно ускорить выборку через gather
И тд, и тп ... у тебя вся жизнь впереди :)
innuendo
> mix/max mips
чёто посмотрел ту нвидийную демку с ними - а там 20 фпс (если зазумиться чтобы модель и тень от неё примерно весь экран занимали).
неюзабельно.
ialexbr
> не затеняется обратная сторона ножа
нужно dot(нормаль, направление света) добавить
ialexbr
> Существует ли способ размыть края тени, отбрасывая при этом пиксели в тени?
> Если нет, хотелось бы услышать советы по улучшению/оптимизации.
Вроде в доке по какой то игре читал про такой трюк: посчитать тени без сглаживания в очень лоуресный рендертаргет (типа там 160х120), картинка при этом должна быть просто белым цветом с черными тенями. Необязательно рендерить сцену повторно, можно реюзать глубину сцены, если она уже посчитана перед наложением теней. Применяешь дальше к этой текстуре фильтр, которые находит края теней и расширяет их на несколько пикселей и сужает вовнутрь тоже на несколько и пишет, в результате, такую лоуресную маску, которая должна как раз собой покрыть мягкие края теней, но не взлезть ни внутрь, ни наружу.
Юзаешь дальше эту маску в затенении конечной сцены, там где её нет - делаешь одну грубую выборку, где есть - со сглаживанием.
Mr F
> чёто посмотрел ту нвидийную демку с ними - а там 20 фпс (если зазумиться чтобы
> модель и тень от неё примерно весь экран занимали).
> неюзабельно
1. Там две точности.
2. Там не самый оптимальный шейдер.
innuendo
> 1. Там две точности.
в смысле?
в любом случае, тормозит хуже чем VSM+SAT, который выглядел ничуть не хуже чем тут accurate версия, а шёл в 250 фпс у меня.
Mr F
> > 1. Там две точности.
> в смысле?
аккуратная и нет :)
> в любом случае, тормозит хуже чем VSM+SAT
Не уверен. Есть графики\картинки ?
innuendo
> Не уверен. Есть графики\картинки ?
какие графики?
есть то, что я запускал и то и другое и смотрел на фпс)
Mr F
> > Не уверен. Есть графики\картинки ?
> какие графики?
Сравнений :)
> есть то, что я запускал и то и другое и смотрел на фпс)
Это хорошо. Но можно же и код шейдера посмотреть ? :)
innuendo
> Но можно же и код шейдера посмотреть ? :)
шейдер самопальный с кучей костылей, говнокода и ограничений, так что я лучше его придержу)
но он работал, я уже постил демку: http://ndotl.wordpress.com/2013/09/05/penumbra-shadows/
Mr F
> шейдер самопальный с кучей костылей,
я имел ввиду шейдер той самой тормознутой демки :)
можно подумать, что SAT быстро считается ?
innuendo
> я имел ввиду шейдер той самой тормознутой демки
ну дак он в самой демке на сайте нвидии
innuendo
> можно подумать, что SAT быстро считается ?
нихрена не быстро, но при некоторых условиях можно соптимизировать, как я делал: рендерить каждый кастер в свой кусок атласа, тогда кол-во препассов SAT потребуется только для размера максимального квадратика в атласе, неважно что их много.
но это всё конечно неуниверсальное и геморрой... но хотя бы в некоторых ситуациях работает.
минмакс мипы пока не видел чтобы хоть в какой-то работали.
Mr F
> ну дак он в самой демке на сайте нвидии
Да. Думаешь, он очень оптимально написан ?
innuendo
> Да. Думаешь, он очень оптимально написа
я хз, других демок не видел.
если есть ещё какие - кинь)
Тема в архиве.