//------------------------------------------------------------------------------ // Desc : uber terrain shader v1.0 // Author : Brice Vandemoortele (http://www.mentalwarp.com) // Date : July 2008 // contact the author at brice@mentalwarp.com for any questions, bugs or requests :) //------------------------------------------------------------------------------ //This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License // http://creativecommons.org/licenses/by-sa/3.0/ //------------------------------------------------------------------------------ //----------------------------------------------- // Matrices //----------------------------------------------- float4x4 wvpMatrix : WorldViewProjection; float4x4 wvMatrixIT : worldviewinversetranspose; //only to fetch camera position float4x4 wMatrix : World; //----------------------------------------------- // Inputs //----------------------------------------------- bool Use_quadFalloff < string UIName = "Use quadratic falloff"; string UIWidget = "RadioButton"; > = true; bool NoDiff < string UIName = "Only specular contribution"; string UIWidget = "RadioButton"; > = false; float3 LightPos1 : Position < string UIName = "LightPosition 1"; string Space = "World";> = {0.0f, 0.0f, 0.0f}; float4 LightCol1 < string type = "color"; string UIName = "LightColor 1"; string UIWidget = "Color";> = {1.0, 1.0, 1.0, 1.0}; float3 LightPos2 : Position < string UIName = "LightPosition 2"; string Space = "World";> = {0.0f, 0.0f, 0.0f}; float4 LightCol2 < string type = "color"; string UIName = "LightColor 2"; string UIWidget = "Color";> = {1.0, 1.0, 1.0, 1.0}; // COPY THIS LINE AND INCREMENT TO ADD MORE POINT LIGHTS bool Use_wUV < string UIName = "Generate world space UV coordinates"; string UIWidget = "RadioButton"; > = false; half wUVratio < string UIWidget = "slider"; float UIMin = 0.1; float UIMax = 2; string UIName = "world UV ratio"; > = 1; bool Use_diff < string UIName = "Use diffuse mapping"; string UIWidget = "RadioButton"; > = true; #define SET_STANDARD_SAMPLER_STATES \ MinFilter = LinearMipMapLinear;\ MagFilter = Linear;\ LODBias = -.5 texture diffuseTexture1 ; sampler2D diffuseSampler1 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture diffuseTexture2 ; sampler2D diffuseSampler2 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture diffuseTexture3 ; sampler2D diffuseSampler3 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture diffuseTexture4 ; sampler2D diffuseSampler4 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; bool Use_nm < string UIName = "Use normal mapping"; string UIWidget = "RadioButton"; > = true; half BumpFactor < string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 3; string UIName = "Bump Scale"; > = 1.0; texture normalTexture1 ; sampler2D normalSampler1 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture normalTexture2 ; sampler2D normalSampler2 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture normalTexture3 ; sampler2D normalSampler3 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture normalTexture4 ; sampler2D normalSampler4 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; bool Use_specAlpha < string UIName = "Use specular stored in diffuse alpha"; string UIWidget = "RadioButton"; > = false; bool Use_specRGB < string UIName = "Use full RGB specular samplers"; string UIWidget = "RadioButton"; > = false; half SpecInt < string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 10; string UIName = "Specular Intensity"; > = 1.0; bool Use_spAlpha < string UIName = "Use specular power in specular alpha"; string UIWidget = "RadioButton"; > = false; half SpecPow < string UIWidget = "slider"; float UIMin = 1; float UIMax = 100; string UIName = "Specular Exponent"; > = 1.0; texture specTexture1 ; sampler2D specSampler1 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture specTexture2 ; sampler2D specSampler2 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture specTexture3 ; sampler2D specSampler3 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; texture specTexture4 ; sampler2D specSampler4 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; half AmbientFactor < string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 2; string UIName = "Ambient Factor"; > = 1; bool Use_Hemispheric_Ambient < string UIName = "Use hemispheric ambient"; string UIWidget = "RadioButton"; > = false; half3 SkyColor < string type = "color"; string UIName = "Sky Color"; > = {1.0, 1.0, 1.0}; half3 GroundColor < string type = "color"; string UIName = "Ground Color"; > = {0, 0, 0}; bool Use_vertexColor < string UIName = "Use vertex color"; string UIWidget = "RadioButton"; > = false; bool Use_vertexLighting < string UIName = "Use vertex color for lighting"; string UIWidget = "RadioButton"; > = false; bool Use_LM < string UIName = "Use lightmap on 2nd UV set"; string UIWidget = "RadioButton"; > = false; texture lmTexture1 ; sampler2D lmSampler1 = sampler_state { Texture = ; SET_STANDARD_SAMPLER_STATES;}; half modSpec_LM < string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 1; string UIName = "Modulate specular by LM"; > = 1; bool Use_gc < string UIName = "Use gamma correction"; string UIWidget = "RadioButton"; > = false; bool Use_checker < string UIName = "Apply a checker (color and black)"; string UIWidget = "RadioButton"; > = false; half checkerSize < string UIWidget = "slider"; string UIName = "Checker ratio"; > = 512; half3 checkerColor < string type = "color"; string UIName = "Checker lighter color"; > = {0.25, 0.0, 0.12}; //--------------------------------------------- // structures //--------------------------------------------- struct vIN { float4 Position : POSITION; half3 Normal : NORMAL; half4 Color : COLOR0; half2 TexCoord : TEXCOORD0; half3 Tangent : TEXCOORD1; half3 Bitangent : TEXCOORD2; half4 Blend : TEXCOORD3; half2 TexCoord2 : TEXCOORD4; }; struct vOUT { float4 Position : POSITION; half4 Color : COLOR0; half4 TexCoord : TEXCOORD0; half3 posWorld : TEXCOORD1; half3 EyeDir : TEXCOORD2; half3 Tgt : TEXCOORD3; half3 Btg : TEXCOORD4; half3 Nml : TEXCOORD5; half4 Blend : TEXCOORD6; }; struct ps_maps { half3 diffuse; half3 normal; half3 specular; half specPower; half3 lm; }; struct ps_vtx { half3 tex; half2 tex2; half4 color; half4 blend; half3 eye; half3 tgt; half3 btg; half3 nml; half3 posWorld; }; struct light_prop { float3 lightPos; float3 lightCol; float lightInt; }; //--------------------------------------------- // Vertex shader //--------------------------------------------- vOUT blendVS(vIN IN) { vOUT OUT; half3 tangent = IN.Tangent.xyz; half3 normal = IN.Normal.xyz; half3 Bitangent = IN.Bitangent.xyz; // position ,uvs OUT.Position = mul(wvpMatrix, IN.Position); OUT.posWorld = mul(wMatrix, IN.Position).xyz; OUT.TexCoord = half4(IN.TexCoord.xy,IN.TexCoord2.xy); half3 EyeDir = normalize(wvMatrixIT[3].xyz - IN.Position.xyz); OUT.EyeDir.xyz = mul(wMatrix, half4(EyeDir.xyz, 0.0)).xyz; OUT.Tgt = mul(wMatrix, half4(tangent, 0)).xyz; OUT.Btg = mul(wMatrix, half4(Bitangent, 0)).xyz; OUT.Nml = mul(wMatrix, half4(normal, 0)).xyz; OUT.Color = IN.Color; float normalizeFactor = IN.Blend.x + IN.Blend.y + IN.Blend.z + (1-IN.Blend.w); OUT.Blend = float4( IN.Blend.xyz/normalizeFactor, (1-IN.Blend.w)/normalizeFactor); return OUT; } //--------------------------------------------- // Pixel shader //--------------------------------------------- float4 gammaToLinear(float4 inputMap ){return inputMap *= (Use_gc)?inputMap:1;} float3 linearToGamma(float3 inputMap ){return inputMap = (Use_gc)?sqrt(inputMap):inputMap;} light_prop packLightArray(half3 p, half4 c) { light_prop OUT; OUT.lightPos = p.xyz; OUT.lightCol = c.rgb; OUT.lightInt = c.a; return OUT; } half3 worldVector(half3 inVector, half3 Nml, half3 Tgt, half3 Btg) // compute WorldSpace vector { half3 vectorlWorld; vectorlWorld.xyz = BumpFactor * inVector.x * Tgt; vectorlWorld.xyz += BumpFactor * inVector.y * Btg; vectorlWorld.xyz += inVector.z * Nml; vectorlWorld = normalize(vectorlWorld); return vectorlWorld; } void ComputePointLights(light_prop lights[], half3 wEye, half3 wNormal, half specPower, float3 posWorld, inout half3 diffuseCont, inout half3 specularCont) { const half3 wReflect = -reflect( wEye, wNormal ).xyz; //calculate specular with proper reflection vector instead of halfangle to produce correct rim specular for(int l = 0; l < 2; l++) // CHANGE TO FIT NUMBER OF LIGHTS { half3 LightDir = lights[l].lightPos.xyz - posWorld.xyz; half Dist = length(LightDir); LightDir /= Dist; half Decay = 1 - saturate( lights[l].lightInt * Dist ); Decay *= (Use_quadFalloff)?Decay:1; diffuseCont += (NoDiff)?0:max(0,dot(LightDir,wNormal)) * lights[l].lightCol * Decay; specularCont += saturate(pow(saturate(dot(LightDir,wReflect)),specPower)) * lights[l].lightCol * SpecInt; //generate satpow instruction } } half3 computeAmbient(half3 vector) { half3 hemiAmbient = AmbientFactor * lerp(GroundColor,SkyColor,saturate(vector.y)).rgb; half3 cubeAmbient = 0; //AmbientFactor * texCUBElod(EnvSampler, float4(vector.xyz,AmbientBlurFactor)).rgb; half3 ambient = (Use_Hemispheric_Ambient)?hemiAmbient:cubeAmbient; return ambient; } ps_vtx initVtx(vOUT IN) { ps_vtx OUT; half2 posUV = IN.posWorld.xz; OUT.tex = (Use_wUV)?float3(posUV.xy * wUVratio,0):float3(IN.TexCoord.xy,0); OUT.tex2 = IN.TexCoord.zw; OUT.color = (Use_vertexColor)?IN.Color:1; OUT.blend = IN.Blend; OUT.eye = normalize(IN.EyeDir); OUT.tgt = (Use_wUV)?half3(IN.Nml.y,-IN.Nml.x,IN.Nml.z):IN.Tgt; OUT.btg = (Use_wUV)?half3(-IN.Nml.x,IN.Nml.z,-IN.Nml.y):IN.Btg; OUT.nml = normalize(IN.Nml); OUT.posWorld = IN.posWorld; return OUT; } ps_maps fetchMap(ps_vtx IN) { ps_maps OUT; half4 blendA = 1; if (Use_diff) { half4 dif1 = tex2D(diffuseSampler1, IN.tex.xy); half4 dif2 = tex2D(diffuseSampler2, IN.tex.xy); half4 dif3 = tex2D(diffuseSampler3, IN.tex.xy); half4 dif4 = tex2D(diffuseSampler4, IN.tex.xy); blendA = gammaToLinear(dif1 * IN.blend.x + dif2 * IN.blend.y + dif3 * IN.blend.z + dif4 * IN.blend.w); } OUT.diffuse = blendA.rgb; OUT.specular = (Use_specAlpha)?blendA.a:1; OUT.normal = IN.nml; if (Use_nm) { half3 nm1 = tex2D(normalSampler1, IN.tex.xy).xyz; half3 nm2 = tex2D(normalSampler2, IN.tex.xy).xyz; half3 nm3 = tex2D(normalSampler3, IN.tex.xy).xyz; half3 nm4 = tex2D(normalSampler4, IN.tex.xy).xyz; OUT.normal = normalize((nm1 * IN.blend.x + nm2 * IN.blend.y + nm3 * IN.blend.z + nm4 * IN.blend.w) * 2 - 1); OUT.normal = worldVector(OUT.normal.xyz, IN.nml, IN.tgt, IN.btg); }; half4 blendB; if (Use_specRGB) { half4 spec1 = tex2D(specSampler1, IN.tex.xy); half4 spec2 = tex2D(specSampler2, IN.tex.xy); half4 spec3 = tex2D(specSampler3, IN.tex.xy); half4 spec4 = tex2D(specSampler4, IN.tex.xy); blendB = gammaToLinear(spec1 * IN.blend.x + spec2 * IN.blend.y + spec3 * IN.blend.z + spec4 * IN.blend.w); blendB.a *= SpecPow; OUT.specular = blendB.rgb; }; OUT.specPower = (Use_spAlpha)?blendB.a:SpecPow; OUT.specPower = max(0.5,OUT.specPower); OUT.lm = 0; if (Use_LM) { OUT.lm = gammaToLinear(tex2D(lmSampler1, IN.tex2.xy)); }; return OUT; } half3 computeChecker(half2 UVs) { return lerp(half3(0,0,0),checkerColor,saturate(1000*sin(checkerSize*UVs.x)*sin(checkerSize*UVs.y))); } float4 blendPS(vOUT IN) : COLOR { ps_vtx input_vtx = initVtx(IN); ps_maps input_maps = fetchMap(input_vtx); half3 ambientCont = 0; half3 diffuseCont = 0; half3 specularCont = 0; ambientCont += computeAmbient(input_maps.normal); light_prop lights[2]; // CHANGE TO FIT NUMBER OF LIGHTS lights[0] = packLightArray(LightPos1,LightCol1); lights[1] = packLightArray(LightPos2,LightCol2); // COPY THIS LINE AND MODIFY INPUTS TO ADD MORE POINT LIGHTS ComputePointLights(lights, input_vtx.eye, input_maps.normal, input_maps.specPower, input_vtx.posWorld, diffuseCont, specularCont); specularCont *= lerp(linearToGamma(input_maps.lm),1,1-modSpec_LM); diffuseCont += linearToGamma(input_maps.lm); diffuseCont += (Use_vertexLighting)?input_vtx.color:0; half3 checker = input_vtx.color.xyz * computeChecker(input_vtx.tex); half3 final = input_vtx.color * (ambientCont + linearToGamma( (diffuseCont * input_maps.diffuse.rgb + specularCont * input_maps.specular ).rgb)); final = (Use_checker)?checker:final; return float4(final , 1.0); } //------------------------------------------------- // techniques //------------------------------------------------- technique Terrain_Blend_Shader { pass P0 { CullFaceEnable=true; DepthTestEnable=true; DepthMask = true; DepthFunc = LEqual; VertexProgram = compile vp40 blendVS(); FragmentProgram = compile fp40 blendPS(); } }