PerPixel Lighting With Offset(Parallax) Mapping         Shader in the Per-Pixel Lighting series

Intro, and materials you can get from this page

Offset -> fast, multipass, unlimited-light, method
Offset_Limited3 -> fast, 1-pass, 3-light limited, method

Both support ambient, diffuse, specular lighting with 1 diffuse texture and 1 normal texture which has height info in its alpha-channel.

See Perpixel II on what "fast, nice" refers to as well as some theory.

Shaders: Offset mapping stuff

//**************************************************************************************//
 //                                            //
 //            BUMP - NORMAL - OFFSET/PARALLAX - RELIEF            //
 //                MAPPING COMES NOW                    //
 //                                            //
 //**************************************************************************************//
 
 
 // General functions 
 
 // Expand a range-compressed vector
 float3 expand(float3 v)
 {
     return (v - 0.5) * 2;
 }
 
 
 
 void Offset_Vert(
          float4 position    : POSITION,
          float3 normal        : NORMAL,
           float2 uv        : TEXCOORD0,
           float3 tangent         : TEXCOORD1,
 
           out float4 oPosition    : POSITION,
           out float2 oUv        : TEXCOORD0,
           out float3 oLightDir    : TEXCOORD1,
          out float3 oEyeDir     : TEXCOORD2,
          out float3 oHalfAngle    : TEXCOORD3,
     
          uniform float scale,
          uniform float4 lightPosition, // object space
           uniform float3 eyePosition,   // object space
           uniform float4x4 worldviewproj)
 {
 //why normalize lightDir if don't normalize eyeDir? both will be mul-ed
 //by matrix! 
 
     oPosition = mul(worldviewproj , position);
     oUv = uv * scale; 
 
      
     float3 eyeDir = eyePosition - position.xyz;
     float3 binormal = cross(tangent, normal);
     float3x3 rotation = float3x3(tangent, binormal, normal);
     eyeDir = normalize(mul(rotation, eyeDir));
 
 
     float3 lightDir = normalize(lightPosition.xyz -  (position * lightPosition.w));
     lightDir = normalize(mul(rotation, lightDir));
     
     oHalfAngle = normalize(eyeDir + lightDir); 
 
     oLightDir = lightDir;
     oEyeDir = eyeDir;
 
         //changing these variables to calculate with output value and not use them doesn't yield any  
                //boost
     
 }
 
 void Offset_Frag(
           float2 uv    : TEXCOORD0,
           float3 lightVec : TEXCOORD1,
           float3 eyeDir : TEXCOORD2,
           float3 halfAngle: TEXCOORD3,
 
           out float4 oColor    : COLOR, 
 
           uniform float4 lightDiffuse,
           uniform float4 lightSpecular,
           uniform float exponent,
           uniform float4 scaleBias, 
 
           uniform sampler2D   normalHeightMap : register(s0)
         )
 {
     float height = tex2D(normalHeightMap, uv).a;
     float scale = scaleBias.x;
     float bias = scaleBias.y;
     float displacement = (height * scale) + bias;
     float3 uv2 = float3(uv, 1); 
     float2 newTexCoord = ((eyeDir * displacement) + uv2).xy;
     float3 bumpVec = expand(tex2D(normalHeightMap, newTexCoord ).xyz);
     float3 N = normalize(bumpVec);
 
     float NdotL = dot(normalize(lightVec), N);
     float NdotH = dot(normalize(halfAngle), N); 
 
     float4 Lit = lit(NdotL,NdotH,exponent);
     
     oColor = lightDiffuse * Lit.y + lightSpecular * Lit.z;
         //do I need to normalize here the normal (=bumpvec)?
 }
 
 void Offset_Lim3_Vert(
          float4 position    : POSITION,
          float3 normal        : NORMAL,
           float2 uv        : TEXCOORD0,
           float3 tangent         : TEXCOORD1, 
 
           out float4 oPosition    : POSITION,
           out float2 oUv        : TEXCOORD0,
           out float3 oLightDir0  : TEXCOORD1,
          out float3 oLightDir1  : TEXCOORD2,
          out float3 oLightDir2    : TEXCOORD3,    
          out float3 oHalfAngle0    : TEXCOORD4,
          out float3 oHalfAngle1    : TEXCOORD5,
          out float3 oHalfAngle2    : TEXCOORD6,
          out float3 oEyeDir     : TEXCOORD7,
      
           uniform float scale,
          uniform float4 lightPosition0, // object space
          uniform float4 lightPosition1, // object space
          uniform float4 lightPosition2, // object space
           uniform float3 eyePosition,   // object space
           uniform float4x4 worldviewproj )
 {
     oPosition = mul(worldviewproj , position);
     oUv = uv * scale; 
 
     float3 eyeDir = eyePosition - position.xyz;
     float3 binormal = cross(tangent, normal);
     float3x3 rotation = float3x3(tangent, binormal, normal);
     eyeDir = normalize(mul(rotation, eyeDir));
 
 
 
      
     oEyeDir = eyeDir;
  
 
      float3 temp_lightDir = normalize(lightPosition0.xyz -  (position * lightPosition0.w));
     oLightDir0 = normalize(mul(rotation, temp_lightDir));
     oHalfAngle0 = normalize(eyeDir + oLightDir0);
 
     temp_lightDir = normalize(lightPosition1.xyz -  (position * lightPosition1.w));
     oLightDir1 = normalize(mul(rotation, temp_lightDir));
     oHalfAngle1 = normalize(eyeDir + oLightDir1);
     
     temp_lightDir = normalize(lightPosition2.xyz -  (position * lightPosition2.w));
     oLightDir2 = normalize(mul(rotation, temp_lightDir));
     oHalfAngle2 = normalize(eyeDir + oLightDir2);
 }
 
 
 void Offset_Lim3_Frag( 
           float2 uv        : TEXCOORD0,
           float3 LightDir0      : TEXCOORD1,
          float3 LightDir1      : TEXCOORD2,
          float3 LightDir2    : TEXCOORD3,    
          float3 HalfAngle0    : TEXCOORD4,
          float3 HalfAngle1    : TEXCOORD5,
          float3 HalfAngle2    : TEXCOORD6,
           float3 EyeDir         : TEXCOORD7, 
 
           out float4 oColor    : COLOR, 
 
         uniform float4 lightDiffuse0,
         uniform float4 lightDiffuse1,
         uniform float4 lightDiffuse2, 
 
         uniform float4 lightSpecular0,
         uniform float4 lightSpecular1,
         uniform float4 lightSpecular2, 
 
         uniform float exponent0,
 //        uniform float exponent1,
 //        uniform float exponent2, 
 
         uniform float4 ambient,
 
         uniform float4 scaleBias,
             
           uniform sampler2D   normalHeightMap, //: register(s0)
           uniform sampler2D diffuseMap 
         )
 {
     float height = tex2D(normalHeightMap, uv).a;
     float scale = scaleBias.x;
     float bias = scaleBias.y;
     float displacement = (height * scale) + bias;
     float3 uv2 = float3(uv, 1); 
     float2 newTexCoord = ((EyeDir * displacement) + uv2).xy;
     float3 bumpVec = expand(tex2D(normalHeightMap, newTexCoord ).xyz);
     float3 diffusetex = tex2D(diffuseMap, newTexCoord).xyz; 
 
     float3 N = normalize(bumpVec);
 
 
     float NdotL0 = dot(normalize(LightDir0), N);
     float NdotH0 = dot(normalize(HalfAngle0), N);
     float4 Lit0 = lit(NdotL0,NdotH0,exponent0); 
 
     float NdotL1 = dot(normalize(LightDir1), N);
     float NdotH1 = dot(normalize(HalfAngle1), N);
     float4 Lit1 = lit(NdotL1,NdotH1,exponent0); 
 
     float NdotL2 = dot(normalize(LightDir2), N);
     float NdotH2 = dot(normalize(HalfAngle2), N);
     float4 Lit2 = lit(NdotL2,NdotH2,exponent0);
     
     oColor = float4(diffusetex,1) * 
         (lightDiffuse0 * Lit0.y + lightDiffuse1 * Lit1.y + lightDiffuse2 * Lit2.y)
     + lightSpecular0 * Lit0.z + lightSpecular1 * Lit1.z + lightSpecular2 * Lit2.z
      + ambient;
 }

Materials: Offset mapping stuff


//**************************************************************************************//
 //                                            //
 //            BUMP - NORMAL - OFFSET/PARALLAX - RELIEF            //
 //                MAPPING COMES NOW                    //
 //                (WITH TEXTURING)                    //
 //**************************************************************************************//
 
 vertex_program Offset_Vert cg
 {
      source ARNOLD.cg 
  
      default_params
     {
         param_named_auto lightPosition light_position_object_space 0
         param_named_auto eyePosition camera_position_object_space
         param_named_auto worldviewproj worldviewproj_matrix
     }  
 
     entry_point Offset_Vert
     profiles vs_1_1 arbvp1
 } 
 
 fragment_program Offset_Frag cg
 {
     source ARNOLD.cg 
 
     default_params
     {
         param_named_auto lightDiffuse light_diffuse_colour 0
         param_named_auto lightSpecular light_specular_colour 0
         param_named exponent float 127
 
         param_named scaleBias float4 0.04 -0.02 1 0  
 
             //why use a float4 is I only use x and y of it?
     }  
 
     entry_point Offset_Frag
     profiles ps_2_0 arbfp1
 } 
 
 vertex_program Offset_Lim3_Vert cg
 {
     source ARNOLD.cg
 
     default_params
      {
         param_named_auto lightPosition0 light_position_object_space 0
         param_named_auto lightPosition1 light_position_object_space 1
         param_named_auto lightPosition2 light_position_object_space 2
         param_named_auto eyePosition camera_position_object_space
         param_named_auto worldviewproj worldviewproj_matrix
     } 
 
     entry_point Offset_Lim3_Vert 
     profiles vs_1_1 arbvp1
 }
 
 fragment_program Offset_Lim3_Frag cg
 {
     source ARNOLD.cg 
 
     default_params
     {
         param_named_auto lightDiffuse0 light_diffuse_colour 0
         param_named_auto lightDiffuse1 light_diffuse_colour 1
         param_named_auto lightDiffuse2 light_diffuse_colour 2 
 
         param_named_auto lightSpecular0 light_specular_colour 0
         param_named_auto lightSpecular1 light_specular_colour 1
         param_named_auto lightSpecular2 light_specular_colour 2 
 
         param_named exponent0 float 127
 //        param_named exponent1 float 127
 //        param_named exponent2 float 127
         //If changed, needs adjusting in Fragshader's every lit!!!!!  
 
         param_named ambient float4 0.0 0.0 0.0 1.0 
 
         param_named scaleBias float4 0.04 -0.02 1 0 
     } 
 
     entry_point Offset_Lim3_Frag
     profiles ps_2_0 arbfp1
 }
 
 //Any lights, offset with specular
 //Apart from being slow, this sax, 'cuz diffuse texture's uv's cannot be offsetted
 //due to the iteration (or at least I can't do it easily)
 //(and due to I cannot transfer data beetwen Fragshaders of different passes)
 material Offset
 {
     technique
     {
         pass
         {    
             vertex_program_ref Ambient
             {
             }    
         }
         pass
         {
             // do this for each light
             iteration once_per_light
              scene_blend add
              
 
             // Vertex program reference
             vertex_program_ref Offset_Vert
             {
                 param_named scale float 5
             }
             // Fragment program
             fragment_program_ref Offset_Frag
             {    
             } 
 
             // Base bump map
             texture_unit
             {
                 texture CC-slateb_NH.dds
                 filtering trilinear
                 //filtering anisotropic
                 //max_anisotropy 3
                 //colour_op replace
             }
         }
         //Decal pass
         pass
         {
             lighting off
             
              vertex_program_ref OneTexture
             {
                 param_named scale float 5
             }   
 
             scene_blend modulate
             texture_unit
              {
                 //filtering anisotropic
                 //max_anisotropy 3
                 filtering trilinear
                 texture CC-slateb.dds
             }            
         }
     }    
 } 
 
 material Offset_Limited3
 {
     technique
      {
 //        pass
 //        {    
 //            vertex_program_ref Ambient
 //            {
 //            }
 //        }
         pass
           { 
              // do this for each light
 //            iteration once_per_light        
 //            scene_blend add
             
             // Vertex program reference
             vertex_program_ref Offset_Lim3_Vert
             {
                 param_named scale float 5
 //                param_named lightnum float 2
              }  
 
             // Fragment program
             fragment_program_ref Offset_Lim3_Frag
             {
 //                param_named lightnum float 2
             }
             
             // Base bump map
             texture_unit
              {
                 texture CC-slateb_NH.dds
                 filtering trilinear
                 //filtering anisotropic
                 //max_anisotropy 3
                 //colour_op replace
             }
             
             texture_unit
             {
                 //filtering anisotropic
                  //max_anisotropy 3
                 filtering trilinear
                 texture CC-slateb.dds
             }
         }
     }
 }


Alias: PerPixel_Lighting_With_Offset(Parallax)_Mapping