Problemy z mapowaniem cienia po raz pierwszy

9

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:

wprowadź opis zdjęcia tutaj

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.

Grieverheart
źródło
Wygląda na to, że powinieneś bardziej szczegółowo opisać swoje problemy. Masz jedno zdanie na temat swoich problemów, a następne zdanie sugeruje poprawkę dla jednego. Powiedz nam dokładnie, jakie są problemy i co już zrobiłeś, aby spróbować je naprawić.
MichaelHouse
Zredagowałem mój post, mam nadzieję, że teraz jest lepiej.
Grieverheart

Odpowiedzi:

6

Nie powinieneś ocieniać wielokątów, które są zwrócone w stronę światła.

Zmieniłem twój shader fragmentów na poniższy kod.

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 fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}
kochol
źródło
8

Jeśli chodzi o samo-cieniowanie, nie jestem pewien, o czym mówisz - nie widzę żadnego „trądziku cienia” na skrzyni na twoim zrzucie ekranu. Jeśli masz na myśli, że twarze odwrócone od światła są ciemne, no cóż, oczywiście, że tak - powinny być! :) Boczna twarz wygląda trochę dziwnie, dzieli przekątną na pół oświetloną i na pół zacienioną. Jeśli używasz N kropki L do oświetlenia i masz dobre normalne (tj. Twarde normalne na tej kostce, a nie wygładzone normalne), to nie powinno się zdarzyć.

Używanie przesunięcia wielokąta i / lub rysowanie tylnych ścian w mapie cienia to standardowe rozwiązania (cóż ... naprawdę obejścia) dla trądziku cienia.

Jeśli chodzi o matrycę projekcyjną, powinieneś myśleć o świetle jak o kamerze. Jeśli jest to światło punktowe, byłby to aparat perspektywiczny, a jeśli to światło kierunkowe, byłoby to jak kamera ortograficzna. Skonstruuj matrycę projekcji w taki sam sposób, jak w przypadku kamery, używając pola widzenia światła, współczynnika kształtu itp.

Aby ograniczyć moduł cieniujący piksele do rysowania pikseli tylko w obszarze widoku światła, po obliczeniu promieni UV mapy cieni po prostu sprawdź, czy są one między 0 a 1, a discardjeśli nie (lub ustaw kolor światła na zero). Innym sposobem jest ustawienie tekstury mapy cienia na tryb „zacisku do ramki” i ustawienie koloru ramki zero, co zapewni, że wszystko poza mapą cienia zostanie całkowicie zacienione.

Nathan Reed
źródło
Oto zbliżenie pudełka i porównanie zi bez linku do mapowania cieni . Samo-cieniowanie jest tutaj bardziej widoczne. Czy mógłbyś wyjaśnić, dlaczego miałbyś używać kamery perspektywicznej dla świateł punktowych i ortograficznej dla kierunkowych?
Grieverheart
Okej, w tym ujęciu wygląda jak trądzik w cieniu. Wyciągnięcie tylnych ścian na mapę cienia (a nie czołowych) powinno to naprawić. Wiem, że powiedziałeś, że próbowałeś, ale może coś poszło nie tak? To jest po prostu glCullFace(GL_FRONT). Jeśli chodzi o punkt w porównaniu z kierunkowym, chodzi o promienie. Promienie kamery zbiegają się do punktu w kamerze perspektywicznej, a promienie światła zbiegają się do punktu w świetle punktowym; promienie kamery są równoległe dla kamery ortograficznej, a promienie światła są równoległe dla światła kierunkowego.
Nathan Reed
Rozumiem, to ma sens. Oto kolejne porównanie z maskowaniem twarzy i bez niej. Ten na dole ma włączone wygładzanie twarzy. Postaram się użyć projekcji ortograficznej do mojej matrycy Light View, co nie powinno jednak wpływać na problem z zacienieniem się, prawda? Może mój zakres klipów jest zbyt duży (tj. Mam 0,1 do 100)?
Grieverheart
Te wyglądają dokładnie tak samo. Przez resztę sceny używasz wygładzania twarzy, prawda? Zrobiłeś glEnable(GL_CULL_FACE)?
Nathan Reed
Nie są dokładnie takie same, spójrz dokładnie na pudełko. Na powyższym zdjęciu linia cienia jest wypukła, a linia na dole jest wklęsła.
Grieverheart
3

Piksele poza widokiem światła są ciemne, ponieważ są próbkowane spoza tekstury głębi światła.

Kiedy transformujesz wierzchołek za pomocą macierzy projekcji, otrzymujesz współrzędne klipu [lightspace_Position] fragmentu. Następnie konwertujesz to na współrzędne tekstury [UVCoords]. Jednak współrzędne miejsca przycięcia fragmentu mogą nadal znajdować się poza zakresem od -1,0 do 1,0 (poza ekranem, a zatem przycięte), a zatem przekonwertowane współrzędne tekstury mogą znajdować się poza zakresem od 0,0 do 1,0 tekstury.

Spróbuj tego:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Możesz także pomnożyć macierz odchylenia w [lightMVP] i uniknąć konwersji współrzędnych w module cieniującym fragmenty.

bgrater
źródło
Matryca odchylenia jest zwykle sposobem, w jaki ludzie chodzą. Jest to o wiele szybsze niż rozgałęzianie kodu w module cieniującym fragmenty.
Ian Young