//------------------------------------------------------------------- // GLOBAL VARIABLES float4x4 WorldViewProj : WorldViewProj; float3 LightVector; float Ambient; float3 EyePosition; texture DiffuseMap; texture NormalMap; sampler DiffuseMapSampler = sampler_state { Texture = ; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = LINEAR; AddressU = WRAP; AddressV = WRAP; }; sampler NormalMapSampler = sampler_state { Texture = ; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = LINEAR; AddressU = WRAP; AddressV = WRAP; }; //------------------------------------------------------------------- // I/O STRUCTURES struct VS_INPUT { float4 Position : POSITION; //in object space float3 Normal : NORMAL; //in object space float2 TexCoord : TEXCOORD0; float3 T : TEXCOORD1; //in object space float3 B : TEXCOORD2; //in object space float3 N : TEXCOORD3; //in object space }; struct VS_OUTPUT_DIFFUSE { float4 Position : POSITION; //in projection space float2 TexCoord0 : TEXCOORD0; float2 TexCoord1 : TEXCOORD1; float3 Normal : TEXCOORD2; //in tangent space float3 LightVector : TEXCOORD3; //in tangent space }; struct VS_OUTPUT_SPEC { float4 Position : POSITION; //in projection space float2 TexCoord : TEXCOORD0; float3 Normal : TEXCOORD1; //in tangent space float3 HalfAngleVector : TEXCOORD2; //in tangent space float3 LightVector : TEXCOORD3; //in tangent space }; //------------------------------------------------------------------- // VERTEX SHADERS VS_OUTPUT_DIFFUSE Diffuse_VS(VS_INPUT IN) { VS_OUTPUT_DIFFUSE OUT; // pass texture coordinates for fetching the diffuse map OUT.TexCoord0 = IN.TexCoord.xy; // pass texture coordinates for fetching the normal map OUT.TexCoord1 = IN.TexCoord.xy; // compute the 3x3 tranform from tangent space to object space; we will // use it "backwards" (vector = mul(matrix, vector) to go from object // space to tangent space, though. float3x3 objToTangentSpace; objToTangentSpace[0] = IN.T; objToTangentSpace[1] = IN.B; objToTangentSpace[2] = IN.N; // transform normal from object space to tangent space, and pack into [0..1] range. // SHORTCUT: the tangent-space normal is, by definition, <0,0,1>, so we // don't really have to transform it. (Packed, <0,0,1> is <0.5,0.5,1>). OUT.Normal = float3(0.5,0.5,1); // transform light vector from object space to tangent space, and pack into [0..1] range OUT.LightVector = normalize(mul(objToTangentSpace, LightVector.xyz)) * 0.5 + 0.5.xxx; // transform position to projection space OUT.Position = mul(IN.Position, WorldViewProj); return OUT; } float4 BumpDiffuse_PS(VS_OUTPUT_DIFFUSE IN) : COLOR { //fetch base color float4 color = tex2D(DiffuseMapSampler, IN.TexCoord0.xy); //fetch bump normal and unpack it to [-1..1] range float3 bumpNormal = 2 * tex2D(NormalMapSampler, IN.TexCoord1.xy) - 1; //compute diffuse lighting coefficient float diffuse = saturate(dot(bumpNormal, 2 * IN.LightVector - 1)); //compute self-shadowing term float shadow = saturate(4 * dot(2 * IN.Normal.xyz - 1, 2 * IN.LightVector.xyz - 1)); //compute final color return color * shadow * saturate(diffuse + Ambient); } VS_OUTPUT_SPEC Specular_VS(VS_INPUT IN) { VS_OUTPUT_SPEC OUT; // pass texture coordinates for fetching the normal map OUT.TexCoord = IN.TexCoord.xy; // compute the 3x3 tranform from tangent space to object space; we will // use it "backwards" (vector = mul(matrix, vector) to go from object // space to tangent space, though. float3x3 objToTangentSpace; objToTangentSpace[0] = IN.T; objToTangentSpace[1] = IN.B; objToTangentSpace[2] = IN.N; // transform normal from object space to tangent space, and pack into [0..1] range. // SHORTCUT: the tangent-space normal is, by definition, <0,0,1>, so we // don't really have to transform it. (Packed, <0,0,1> is <0.5,0.5,1>). OUT.Normal = float3(0.5,0.5,1); // compute view vector float3 viewVector = normalize(EyePosition - IN.Position.xyz); // compute half angle vector in object space float3 halfAngleVector = normalize(LightVector + viewVector); // transform half angle vector from object space to tangent space, and pack into [0..1] range OUT.HalfAngleVector = normalize(mul(objToTangentSpace, halfAngleVector)) * 0.5 + 0.5.xxx; // transform light vector from object space to tangent space, and pack into [0..1] range OUT.LightVector = normalize(mul(objToTangentSpace, LightVector.xyz)) * 0.5 + 0.5.xxx; // transform position to projection space OUT.Position = mul(IN.Position, WorldViewProj); return OUT; } float4 BumpySpecular_PS(VS_OUTPUT_SPEC IN) : COLOR { float4 OUT; //fetch bump normal and expand to [-1..1] range float3 bumpNormal = 2 * tex2D(NormalMapSampler, IN.TexCoord.xy).xyz - 1; //compute specular color // note that both bumpNormal and IN.HalfAngleVector are in tangent space float specular = saturate(dot(bumpNormal, 2 * IN.HalfAngleVector - 1)); float specular2 = specular * specular; float specular4 = specular2 * specular2; float specular8 = specular4 * specular4; float specular16 = specular8 * specular8; //compute self-shadowing term float shadow = saturate(4 * dot(2 * IN.Normal.xyz - 1, 2 * IN.LightVector.xyz - 1)); //compute final color OUT.xyz = shadow * specular16; OUT.w = 1; return OUT; } Technique BumpSpecular { Pass P0 { VertexShader = compile vs_1_1 Diffuse_VS(); PixelShader = compile ps_1_1 BumpDiffuse_PS(); AlphaBlendEnable = False; } Pass P1 { VertexShader = compile vs_1_1 Specular_VS(); PixelShader = compile ps_1_1 BumpySpecular_PS(); AlphaBlendEnable = True; SrcBlend = One; DestBlend = One; } }