W przedostatnim z tutoriali zajmiemy się wykorzystaniem tzw. specular maps dzięki, którym możliwe jest zwiększenie realizmu oświetlania obiektów. Mapy te określają w jakich miejscach jak bardzo obiekt jest 'odblaskowy'.
Swoje zmiany w kodzie zaczynamy w shader'ach, gdzie musimy dodać obsługę włączania/wyłączania specular i color map:
precision mediump float; varying vec2 vTextureCoord; varying vec3 vTransformedNormal; varying vec4 vPosition; uniform bool uUseColorMap; uniform bool uUseSpecularMap; uniform bool uUseLighting;
Następnie musimy zdefiniować dwa samplery dla obu map. Robimy to zamieniając uSampler
na fragment poniżej:
uniform vec3 uAmbientColor; uniform vec3 uPointLightingLocation; uniform vec3 uPointLightingSpecularColor; uniform vec3 uPointLightingDiffuseColor; uniform sampler2D uColorMapSampler; uniform sampler2D uSpecularMapSampler;
Dalej pojawia się nasz standardowy fragment kodu odpowiedzialny za obsługę włączenia/wyłączenia światła:
void main(void) { vec3 lightWeighting; if (!uUseLighting) { lightWeighting = vec3(1.0, 1.0, 1.0); } else { vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz); vec3 normal = normalize(vTransformedNormal);
Po powyższym typowym fragmencie wstawiamy zmienną odpowiedzialną za światło refleksu:
float specularLightWeighting = 0.0;
Ustawienie 'odblaskowości' obiektu musimy uzależnić od ustawień użytkownika, dlatego też konieczne jest wcześniejsze jej zainicjalizowanie. Może zdażyć się przypadek, że widz wyłączy sobie taką opcję:
float shininess = 32.0; if (uUseSpecularMap) { shininess = texture2D(uSpecularMapSampler, vec2(vTextureCoord.s, vTextureCoord.t)).r * 255.0; }
Koniecznym jest też ograniczenie zakresu obdlasku dla jakiego wykonujemy kalkulacje:
if (shininess < 255.0) { vec3 eyeDirection = normalize(-vPosition.xyz); vec3 reflectionDirection = reflect(-lightDirection, normal); specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess); }
Na zakończenie łączymy w całość wszystkie zmienne, żeby otrzymać porządany efekt i nałożyć go na teksturę:
float diffuseLightWeighting = max(dot(normal, lightDirection), 0.0); lightWeighting = uAmbientColor + uPointLightingSpecularColor * specularLightWeighting + uPointLightingDiffuseColor * diffuseLightWeighting; } vec4 fragmentColor; if (uUseColorMap) { fragmentColor = texture2D(uColorMapSampler, vec2(vTextureCoord.s, vTextureCoord.t)); } else { fragmentColor = vec4(1.0, 1.0, 1.0, 1.0); } gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a); }
W pozostałych funkcjach zostały wprowadzone nieznaczne zmiany, aby dostosować je na potrzeby danego przykładu.
Pozycja: | X: | Y: | Z: |
Kolor refleksu: | R: | G: | B: |
Kolor rozproszenia: | R: | G: | B: |
Kolor: | R: | G: | B: |