2013-01-07 36 views
5

Mam scenę z kilkoma modelami z indywidualnymi pozycjami i obrotami. Biorąc pod uwagę normalne, shadery stosują proste dwukierunkowe oświetlenie do każdego piksela.Obróć normalne w cieniurze

To jest mój vertex shader.

#version 150 

in vec3 position; 
in vec3 normal; 
in vec2 texcoord; 

out vec3 f_normal; 
out vec2 f_texcoord; 

uniform mat4 model; 
uniform mat4 view; 
uniform mat4 proj; 

void main() 
{ 
    mat4 mvp = proj * view * model; 

    f_normal = normal; 
    f_texcoord = texcoord; 

    gl_Position = mvp * vec4(position, 1.0); 
} 

A oto fragment cieniowania.

#version 150 

in vec3 f_normal; 
in vec2 f_texcoord; 

uniform sampler2D tex; 

vec3 sun = vec3(0.5, 1.0, 1.5); 

void main() 
{ 
    vec3 light = max(0.0, dot(normalize(f_normal), normalize(sun))); 

    gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0); 
} 

Dla obiektów bez obrotu działa to dobrze. Ale w przypadku modeli wirowanych oświetlenie jest również obracane, co oczywiście nie powinno mieć miejsca.

Powodem tego jest to, że normalne nie są obracane. Próbowałem już f_normal = model * normal;, ale to dotyczy zarówno rotacji, jak i transformacji do normalnych.

Jak zatem obrócić obiekty normalne w module cieniującym wierzchołków przed wysłaniem ich do modułu cieniującego fragmentów w celu oświetlenia? Jakie jest wspólne podejście?

Odpowiedz

4

Musisz przekształcić normalne, przez górne 3 wiersze/kolce matrycy rzutowania modelu. (Jeśli jednak wykonujesz jakiekolwiek skalowanie, musisz użyć odwrotnej transpozycji tej macierzy See this article).

mat3 normalMatrix = mat3(mvp); 
normalMatrix = inverse(normalMatrix); 
normalMatrix = transpose(normalMatrix); 
f_normal = normalize(normal * normalMatrix); 
// You should also send your tranformed position to the fragment shader 
f_position = vec3(mvp * vec4(position, 1.0)); 

W module cieniującym fragmentu należy obliczyć odległość od źródła światła do fragmentu i znormalizować. Znajdź iloczyn punktowy wektora normalnego i wektora światła i pomnóż go przez kolor światła.

vec3 light = normalize(sun - f_position); 
light = max(dot(f_normal, light), 0.0) * vec3(1.0, 1.0, 1.0); 
gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0); 

W moim kodzie jest miejsce na optymalizację.

Polecam tę książkę OpenGL 4.0 Shading Language Cookbook.

+0

Dzięki. Czy radziłbyś mi wysłać osobną normalną matrycę (zawierającą tylko obrót modelu, ale nie skalowanie ani transformację) do modułu cieniującego? Widziałem gdzieś tę praktykę i zastanawiam się, czy to jest lepsze niż ponowne wydobywanie obrotu z macierzy modelu w module cieniującym. – danijar

+0

Niestety, mój czas zabrakło podczas edycji poprzedniego komentarza ... Myślę, że skalowanie jest konieczne, ponieważ może zmienić odległość powierzchni do źródła światła. Skoro potrzebujesz skali, musisz poprawić skalę, używając odwrotnej transpozycji górnej macierzy 3 x 3. Wysyłam moją normalną macierz do shadera jako jednolitą, tak, że jest obliczana tylko raz zamiast raz na jeden wierzchołek.Do tego może być potrzebna biblioteka matematyczna, ponieważ platforma może nie zapewniać funkcji wykonywania obliczeń macierzy. – bwroga

+0

To ma sens. Tak przy okazji, używam GLM. – danijar

0

Poniższe rozwiązanie działa z moimi modelami, ale nie mam dogłębnej wiedzy na temat matematyki. To jest moje źródło: Adding Depth and Realism - Surface Normals

Transformacja trzeba zastosować do swoich normalnych wektorów oznaczamy przez: N”= n * (M -1) T

Zasadniczo oznacza to, że pomnóż swoje normalne (N) przez odwrotną transpozycję macierzy ModelView (M). Jeśli macierz M to 4x4, powinieneś po prostu użyć wynikowego 3x3 lewego górnego kwadrantu (M -1) dla prawidłowego mnożenia.

Ponownie działa to dla mnie, ale nie mogę zbyt dobrze wyjaśnić matematyki.

+0

Dzięki, mam to. – danijar