Tutorial 15

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.

Wynik końcowy



color map
specular map
oświetlenie

Światło punktowe (point light):

Pozycja: X: Y: Z:
Kolor refleksu: R: G: B:
Kolor rozproszenia: R: G: B:

Światło otoczenia (ambient light):

Kolor: R: G: B: