ANOTHER SAVAGE DAY ON PLANET EARTH (RELOADED)

Tutorial de Normal Mapping


En esta página encontrareis el codigo necesario para crear un shader (completamente funcional) que utilice una version personalizada del mapa de entorno cúbico. Como bien sabemos, el mapa cúbico de entorno es una técnica de render que permite simular entornos bastante complejos y realistas a traves de una textura especial que crea un entorno en el que podemos reflejar detalles de la malla de mejor manera que la que lo hariamos usando un mapa esférico. El problema puede derivarse del hecho de que nuestro engine no nos permita usar mapas cúbicos de entorno a traves del hardware propiamente, o que aun no este implementado, Este código esta escrito en HLSL (DirectX) aunque es muy sencillo, no utiliza nada exclusivo de DirectX y probablemente se pueda usar con OPEN GL con bastante facilidad.

Este seria el Vertex Shader

float3 fvLightPosition1;
                 float3 fvLightPosition2;
                 float3 fvEyePosition;
                 float4x4 matWorld;
                 float4x4 matWorldViewProjection;
                 float4x4 matWorldInv;
struct VS_INPUT 
                   {
                   float4 Position : POSITION0;
                   float2 Texcoord : TEXCOORD0;
                   float3 Normal :   NORMAL0;
                   float3 Binormal : BINORMAL0;
                   float3 Tangent :  TANGENT0;
   
                   };
struct VS_OUTPUT 
                   {
                   float4 Position :        POSITION0;
                   float2 Texcoord :        TEXCOORD0;
                   float3 ViewDirection :   TEXCOORD1;
                   float3 LightDirection1:   TEXCOORD2;
                   float3 LightDirection2:   TEXCOORD3;
                   float3 truViewDir:        TEXCOORD4;
                   float3 truNormal:         TEXCOORD5;
   
                   };
VS_OUTPUT vs_main( VS_INPUT IN )
                   {
                   VS_OUTPUT OUT;
                   OUT.Position = mul( IN.Position, matWorldViewProjection );
                   OUT.Texcoord = IN.Texcoord;
   
                   float3 RealPos = mul(IN.Position, matWorld);
   
                   float3 viewVec = fvEyePosition - RealPos;
                   OUT.truViewDir=viewVec;
                   float3 lightVec1 = fvLightPosition1 - RealPos;
                   float3 lightVec2 = fvLightPosition2 - RealPos;
   
                   float3 tViewVec;
                   float3 tLightVec1;
                   float3 tLightVec2;
 float3 Tangent = normalize(IN.Tangent);
                   float3 Normal = normalize(IN.Normal);
                   float3 Binormal = cross( Normal , Tangent );
                   Tangent = cross( Normal,Binormal);
   
                   Tangent = mul( matWorldInv,Tangent );
                   Binormal = mul( matWorldInv,Binormal );
                   Normal = mul( matWorldInv,Normal );
                   OUT.truNormal=Normal;
   
                   tViewVec.x = dot(viewVec,Tangent);
                   tViewVec.y = dot(viewVec,Binormal);
                   tViewVec.z = dot(viewVec,Normal);
   
                   tLightVec1.x = dot(lightVec1,Tangent);
                   tLightVec1.y = dot(lightVec1,Binormal);
                   tLightVec1.z = dot(lightVec1,Normal);
 tLightVec2.x = dot(lightVec2,Tangent);
                   tLightVec2.y = dot(lightVec2,Binormal);
                   tLightVec2.z = dot(lightVec2,Normal);
   
                   OUT.ViewDirection = tViewVec;
                   OUT.LightDirection1 = tLightVec1;
                   OUT.LightDirection2 = tLightVec2;
   
                   return( OUT );
                   }

Y este el pixel Shader

float4 LightColor1;
                 float4 LightColor2;
sampler2D baseMap;
                   sampler2D bumpMap;
                   sampler2D refMap;
float2 cube2uv(float3 IN){
                   float2 uv=0;
                   float3 inSQ=IN;
if(inSQ.x<0){inSQ.x=-inSQ.x;}
                   if(inSQ.y<0){inSQ.y=-inSQ.y;}
                   if(inSQ.z<0){inSQ.z=-inSQ.z;}
//Sabemos claramente que nunca dividiremos por 0, Por lo que estos calculos son seguros.
//Mapeamos las coordenadas en una textura en proporcion 2x1 de manera que evitamos errores
//de redondeo, aunque estamos desperdiciando el 25% del espacio de textura.
if(inSQ.x>=inSQ.y&&inSQ.x>=inSQ.z){
                   if(IN.x>0){
                   uv.x=0.125;
                   uv.y=0.25;
                   uv.x+=IN.z/IN.x*0.125;
                   uv.y-=IN.y/IN.x*0.25;
                   }else{
                   uv.x=0.625;
                   uv.y=0.25;
                   uv.x+=IN.z/IN.x*0.125;
                   uv.y+=IN.y/IN.x*0.25;
                   }
                   }
                   if(inSQ.y>inSQ.x&&inSQ.y>=inSQ.z){
                   if(IN.y>0){
                   uv.x=0.125;
                   uv.y=0.75;
                   uv.x-=IN.x/IN.y*0.125;
                   uv.y+=IN.z/IN.y*0.25;
                   }else{
                   uv.x=0.625;
                   uv.y=0.75;
                   uv.x+=IN.x/IN.y*0.125;
                   uv.y+=IN.z/IN.y*0.25;
                   }
                   }
                   if(inSQ.z>inSQ.x&&inSQ.z>inSQ.y){
                   if(IN.z>0){
                   uv.x=0.375;
                   uv.y=0.25;
                   uv.x-=IN.x/IN.z*0.125;
                   uv.y-=IN.y/IN.z*0.25;
                   }else{
                   uv.x=0.375;
                   uv.y=0.75;
                   uv.x-=IN.x/IN.z*0.125;
                   uv.y+=IN.y/IN.z*0.25;
 }
                   }
                 
return uv; 
                   }
struct PS_INPUT 
                   {
                   float2 Texcoord :        TEXCOORD0;
                   float3 ViewDirection :   TEXCOORD1;
                   float3 LightDirection1:   TEXCOORD2;
                   float3 LightDirection2:   TEXCOORD3;
                   float3 truViewDir:        TEXCOORD4;
                   float3 truNormal:         TEXCOORD5;
   
                   };
float4 ps_main( PS_INPUT IN ) : COLOR0
                   { 
                   float4 base = tex2D(baseMap,IN.Texcoord);
                   float3 normal = tex2D(bumpMap,IN.Texcoord);
                   normal = normal*2-1;
   
                   float3 viewDir = normalize(IN.ViewDirection);
   
                   float3 lightDir1 = normalize(IN.LightDirection1);
                   float3 lightDir2 = normalize(IN.LightDirection2);
   
                   normal = normalize(normal);
                   float3 truNormal=normalize(IN.truNormal);
   
                   float3 halfView1 = normalize(viewDir+lightDir1);
                   float3 halfView2 = normalize(viewDir+lightDir2);
                   float3 reflVec = reflect(-IN.truViewDir,truNormal);
                   float4 refl = tex2D(refMap,cube2uv(reflVec));
   
                   float diffuse1 = dot(normal,lightDir1)*1;
                   float specular1 = pow(dot(halfView1,normal),68)*base.w;
   
                   float diffuse2 = dot(normal,lightDir2)*1;
                   float specular2 = pow(dot(halfView2,normal),68)*base.w;
   
                   return(saturate(base *(diffuse1+specular1)*LightColor1 + 
                   base *(diffuse2+specular2)*LightColor2+refl*0.95));
                   }

El código marcado en azul indica la parte mas importante de estos shaders, que como se ve, son bastante normalitos, salvo por esa parte especificamente. Ahi calculamos como pasar un vector 3D a una posicion 2D en un mapa de coordenadas especificamente ordenado, como el que sigue:

no Image

Y el resultado es el siguiente.

No Image

Copyright 2008/2009 Santiago A. Navascués González.