/****************************************************************************** Copyright Benjamin Quintero, 2008 Reference: Alex Hogan, 2006 (www.alexhogan.com) The author accepts no liability from your use or misuse of the content provided here. Use at your own risk. Description: This shader is designed to reflect results that can be seen in some real-time games using idTech and Unreal Engine games. It features: - Color, Normal, Specular, Emissive Maps - Tranparency: zero-alpha pixels are skipped allowing for 1bit tranparency. ******************************************************************************/ string description = "Real-Time Lighting Model Shader"; string url = "http://www.inlandstudios.com"; float4x4 worldMatrix : World; float4x4 wvpMatrix : WorldViewProjection; float4x4 worldViewMatrix : WorldView; float4x4 worldViewMatrixI : WorldViewInverse; float4x4 viewInverseMatrix : ViewInverseTranspose; float4x4 ViewInv : ViewInverse; float4x4 wvIT : WorldViewInverseTranspose; float4x4 viewMatrix : View; texture colorMapTex : Diffuse < string ResourceName = "white.jpg"; string ResourceType = "2D"; >; sampler2D colorMap = sampler_state { Texture = < colorMapTex >; MinFilter = LinearMipMapLinear; MagFilter = Linear; }; texture normalMapTex : Height < string ResourceName = "flatnormals.tga"; string ResourceType = "2D"; >; sampler2D normalMap = sampler_state { Texture = < normalMapTex >; MinFilter = LinearMipMapLinear; MagFilter = Linear; }; texture specMapTex : Diffuse < string ResourceName = "white.jpg"; string ResourceType = "2D"; >; sampler2D specMap = sampler_state { Texture = < specMapTex >; MinFilter = LinearMipMapLinear; MagFilter = Linear; }; //texture emissiveMapTex : Diffuse < string ResourceName = "black.jpg"; string ResourceType = "2D"; >; sampler2D emissiveMap = sampler_state { Texture = < emissiveMapTex >; MinFilter = LinearMipMapLinear; MagFilter = Linear; }; float lightIntensity < string UIWidget = "slider"; float UIMin = 0; float UIMax = 1; float UIStep = 0.1; string UIName = "Light Insensity"; > = 1; float3 lightColor : COLOR < string UIName = "Light Color"; string UIWidget = "Color"; string Type = "Color"; > = {1.0,1.0,1.0}; float3 lightOrigin : POSITION < string UIName = "Light Position"; string Space = "World"; > = {0,50,200}; float bumpScale < string UIWidget = "slider"; float UIMin = 0; float UIMax = 20; float UIStep = 0.1; string UIName = "Bump Scale"; > = 2; float specExponentAmount : SPECULARPOWER < string UIWidget = "slider"; float UIMin = 2; float UIMax = 50; float UIStep = 1; string UIName = "Specular Exponent"; > = 20; struct vertexIn { float4 Position : POSITION; float2 TexCoord : TEXCOORD0; float3 Normal : NORMAL; float3 Tangent : TEXCOORD1; float3 Binormal : TEXCOORD2; }; struct v2f { float4 Position : POSITION; float2 TexCoord : TEXCOORD0; // base coordinates float3 TexCoord1 : TEXCOORD1; // tangent space light vector float3 TexCoord2 : TEXCOORD2; // tangent space specular half-angle }; v2f vShaderMain(vertexIn IN) { v2f OUT; float3 worldPos = mul(worldMatrix, IN.Position).xyz; float3 worldNorm = mul(worldMatrix, float4(IN.Normal, 0)).xyz; float3 worldTan = mul(worldMatrix, float4(IN.Tangent, 0)).xyz; float3 worldBinormal = mul(worldMatrix, float4(IN.Binormal, 0)).xyz; float3 lightVec = lightOrigin - worldPos; float3 halfAngle = normalize( lightVec ) + normalize( viewInverseMatrix[3].xyz - worldPos ); float3x3 tanSpace; tanSpace = transpose( float3x3(worldTan, worldBinormal, worldNorm) ); OUT.Position = mul(wvpMatrix, float4(IN.Position.xyz,1)); OUT.TexCoord = IN.TexCoord; OUT.TexCoord1 = mul( lightVec, tanSpace ); OUT.TexCoord2 = mul( halfAngle, tanSpace ); return OUT; } float4 pShaderMain( v2f IN ) : COLOR { float4 color; float lightMod; float3 normal; float4 diffuseColor; float3 specular; float3 specularColor; float3 lightVec; // compute the scaled diffuse color diffuseColor = tex2D( colorMap, IN.TexCoord.xy); clip( diffuseColor.a - 0.01 ); // transparent surfaces skip some pixels // normalized tangent space vectors lightVec = normalize( IN.TexCoord1 ); // compute the scaled normal normal = tex2D(normalMap, IN.TexCoord.xy).xyz * 2.0 - 1.0; normal = float3(normal.x * bumpScale, normal.y * bumpScale, normal.z); normal = normalize( normal ); // lambert light angle lighting lightMod = dot( normal, lightVec) * lightIntensity; // get specular^1 specular.x = dot( normalize( IN.TexCoord2 ), normal); // --- formula #1 --- // calculate a specular approximation cosine //specular.x = saturate( specular.x * 4 - 3 ); //specular.x = (specular.x * specular.x); // ------------------ // --- formula #2 --- // manually calculate specular exponent //specular.x = (specular.x * specular.x); // 2x //specular.x = (specular.x * specular.x); // 4x //specular.x = (specular.x * specular.x); // 8x //specular.x = (specular.x * specular.x); // 16x // ------------------ // --- formula #3 --- // calculate an accurate exponential value // WARNING: "pow" function appears to produce inaccurate results in Maya.. specular.x = pow(specular.x, specExponentAmount); // ------------------ // compute the scaled specular color specularColor = tex2D( specMap, IN.TexCoord.xy).rgb; specularColor = specularColor * specular.xxx; // combine lighting into final result color.xyz = diffuseColor.xyz + specularColor; color.xyz = color.xyz * lightMod * lightColor; // get the emissive color to blend into the final result //float4 emissiveColor = tex2D( emissiveMap, IN.TexCoord.xy); //color.xyz = lerp( color.xyz, emissiveColor.xyz, emissiveColor.w); // emulate the the overbrightening from post processing color.rgb = saturate( color.rgb ); //color.rgb = color.rgb + saturate(color.rgb * 2 - 1); return float4(color.xyz, 1); } float4 pAmbientShaderMain( v2f IN ) : COLOR { float4 color; float lightMod; float3 normal; float4 diffuseColor; float3 lightVec; // compute the scaled diffuse color diffuseColor = tex2D( colorMap, IN.TexCoord.xy); clip( diffuseColor.a - 0.01 ); // transparent surfaces skip some pixels // normalized tangent space vectors lightVec = normalize( IN.TexCoord1 ); // compute the scaled normal normal = tex2D(normalMap, IN.TexCoord.xy).xyz * 2.0 - 1.0; normal = float3(normal.x * bumpScale, normal.y * bumpScale, normal.z); normal = normalize( normal * float3(2.0, 2.0, 1.0) ); // half-lambert light angle lighting lightMod = saturate(dot( normal, lightVec) * 0.5 + 0.5); lightMod = lightMod * lightIntensity; // combine lighting into final result color.rgb = (diffuseColor.rgb * lightMod) * lightColor; // get the emissive color to blend into the final result //float4 emissiveColor = tex2D( emissiveMap, IN.TexCoord.xy); //color.xyz = lerp( color.xyz, emissiveColor.xyz, emissiveColor.w); // emulate the the overbrightening from post processing color.rgb = saturate( color.rgb ); //color.rgb = color.rgb + saturate(color.rgb * 2 - 1); return float4(color.rgb, 1); } technique LightingModel { pass { DepthTestEnable=true; DepthMask = true; DepthFunc = LEqual; VertexProgram = compile arbvp1 vShaderMain(); FragmentProgram = compile arbfp1 pShaderMain(); } } technique AmbientLightingModel { pass { DepthTestEnable=true; DepthMask = true; DepthFunc = LEqual; VertexProgram = compile arbvp1 vShaderMain(); FragmentProgram = compile arbfp1 pAmbientShaderMain(); } }