Po raz pierwszy zaimplementowałem podstawowe mapowanie cieni w OpenGL przy użyciu shaderów i mam pewne problemy. Poniżej możesz zobaczyć przykład mojej renderowanej sceny:
Proces mapowania cieni, który śledzę, polega na tym, że renderuję scenę do bufora ramki przy użyciu macierzy widoku z punktu widzenia światła oraz matryc projekcyjnych i modelowych używanych do normalnego renderowania.
W drugim przejściu wysyłam powyższą macierz MVP z punktu widzenia światła do modułu cieniującego wierzchołek, który przekształca pozycję w przestrzeń światła. Moduł cieniujący dzieli podział perspektywy i zmienia położenie na współrzędne tekstury.
Oto mój moduł cieniujący wierzchołki,
#version 150 core
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;
uniform float scale;
in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;
smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;
void main(void){
pass_Normal = NormalMatrix * in_Normal;
pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
TexCoord = in_TexCoord;
gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}
I mój shader fragmentów,
#version 150 core
struct Light{
vec3 direction;
};
uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;
smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;
out vec4 out_Color;
float CalcShadowFactor(vec4 lightspace_Position){
vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;
vec2 UVCoords;
UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;
float Depth = texture(inShadowMap, UVCoords).x;
if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
else return 1.0;
}
void main(void){
vec3 Normal = normalize(pass_Normal);
vec3 light_Direction = -normalize(light.direction);
vec3 camera_Direction = normalize(-pass_Position);
vec3 half_vector = normalize(camera_Direction + light_Direction);
float diffuse = max(0.2, dot(Normal, light_Direction));
vec3 temp_Color = diffuse * vec3(1.0);
float specular = max( 0.0, dot( Normal, half_vector) );
float shadowFactor = CalcShadowFactor(lightspace_Position);
if(diffuse != 0 && shadowFactor > 0.5){
float fspecular = pow(specular, 128.0);
temp_Color += fspecular;
}
out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}
Jednym z problemów jest samo zacienienie, jak widać na zdjęciu, skrzynia ma swój własny cień rzucony na siebie. Próbowałem włączyć przesunięcie wielokąta (tj. GlEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)), ale niewiele się zmieniło. Jak widać w module cieniującym fragmenty, umieściłem wartość statycznego przesunięcia 0,001, ale muszę zmienić tę wartość w zależności od odległości światła, aby uzyskać bardziej pożądane efekty, co niezbyt przydatne. Próbowałem również użyć wygładzania przedniej powierzchni, gdy renderowałem do bufora ramki, co również niewiele się zmieniło.
Innym problemem jest to, że piksele poza polem widzenia Światła są zacienione. Jedynym przedmiotem, który powinien rzucać cienie, jest skrzynia. Chyba powinienem wybrać bardziej odpowiednią projekcję i wyświetlić matryce, ale nie jestem pewien, jak to zrobić. Jakie są typowe praktyki, czy powinienem wybrać rzut ortograficzny?
Z Google'a trochę rozumiem, że te kwestie nie są aż tak trywialne. Czy ktoś ma jakieś łatwe do wdrożenia rozwiązania tych problemów. Czy możesz podać mi dodatkowe wskazówki?
Zapytaj mnie, czy potrzebujesz więcej informacji na temat mojego kodu.
Oto porównanie z mapowaniem cienia zbliżenia skrzyni i bez niej. Samo-cieniowanie jest bardziej widoczne.