// This is the HLSL effect which creates the displacement and calculates the new normals, tangents, and binormals.
// It then renders the scene using a directional light, specular highlights, a normal map, and a texture.
float4×4 World;
float4×4 View;
float4×4 Projection;
float Ambient;
float3 LightDirection;
float3 CameraPosition;
float SpecularPower;
float3 LightStrength;
float BulgeWidth; // width of the “bulge” displacement
float BulgeHeight; // height of the “bulge” displacement
Texture Image;
sampler ImageSampler = sampler_state { texture = < Image >;
magfilter = LINEAR; minfilter = LINEAR; mipfilter = LINEAR;
AddressU = wrap; AddressV = wrap; };
Texture NormalMap;
sampler NormalMapSampler = sampler_state { texture = < NormalMap >;
magfilter = LINEAR; minfilter = LINEAR; mipfilter = LINEAR;
AddressU = wrap; AddressV = wrap; };
struct VertexShaderInput
{
float4 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float3 LightDirT : TEXCOORD1;
float3 EyeDirection : TEXCOORD2;
};
void calcSurfaceHeightNormTanBinorm(float x, float z, out float y, out float3 normal, out float3 tangent, out float3 biNormal)
{
// Use the “Witch of Agnesi” function [ y = (8a^3) / (x^2 + 4a^2) ] to determine the surface displacement
// However, use the distance from the vertex (x,z) to the origin (0,0) as the independent variable, instead of just using x.
float dist = sqrt(x*x + z*z) / BulgeWidth; // dist = distance from vertex to origin. Multiplying by BulgeWidth serves to widen the “bulge”
float bW_sqrd = pow(BulgeWidth, 2);
float h = BulgeHeight;
// Determine height by using the “Witch of Agnesi” function, but using dist as the independent variable
y = 8*(h*h*h) / (dist*dist + 4*h*h);
// Use partial derivatives of “Witch of Agnesi” Curve to caluculate normal
normal.x = (-16 * h*h*h * x/bW_sqrd) / pow(((x*x + z*z)/bW_sqrd + 4*h*h), 2);
normal.z = (-16 * h*h*h * z/bW_sqrd) / pow(((x*x + z*z)/bW_sqrd + 4*h*h), 2);
normal.y = 1.0f;
normal = normalize(normal);
// Use partial derivative dy/dx, holding z constant, to calculate tangent
tangent.x = 1.0f;
tangent.z = 0.0f;
tangent.y = (-16 * h*h*h * bW_sqrd*x) / pow( (bW_sqrd*(x*x + z*z) + 4*h*h), 2);
tangent = normalize(tangent);
// Use cross product of normal and tangent to calculate biNormal
biNormal = cross(normal, tangent);
}
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output = (VertexShaderOutput)0;
// Calculate the displaced height for this vertex as well as it’s new normal, tangent, and binormal
float3 normal, tangent, biNormal;
calcSurfaceHeightNormTanBinorm(input.Position.x, input.Position.z, input.Position.y, normal, tangent, biNormal); // (y, normal, tangent, and biNormal are out parameters)
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
// Use normal, tangent, and biNormal to create a tangentToObject matrix
float3×3 tangentToObject;
tangentToObject[0] = normalize(biNormal);
tangentToObject[1] = normalize(tangent);
tangentToObject[2] = normalize(normal);
float3×3 tangentToWorld = mul(tangentToObject, World); // Create a tangentToWorld matrix to be used for tangent-space lighting
output.LightDirT = mul(tangentToWorld, LightDirection); // Take LightDirection from world to tangent space
float3 worldEyeDirection = normalize(worldPosition – CameraPosition);
output.EyeDirection = mul(tangentToWorld, worldEyeDirection); // Take worldEyeDirection from world to tangent space
output.TexCoord = input.TexCoord;
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
// Original Color:
float4 texColor = tex2D(ImageSampler, input.TexCoord);
// Shading Contribution:
float3 normalFromNormalMap = tex2D(NormalMapSampler, input.TexCoord);
float3 normalT = (normalFromNormalMap – 0.5f) * 2.0f;
float shading = max(dot(-normalize(normalT), normalize(input.LightDirT)), 0);
shading *= LightStrength;
// Specular Contribution:
float specular = 0.0f;
float3 reflection = -reflect(normalize(input.LightDirT), normalT);
specular = max(dot(normalize(reflection), normalize(input.EyeDirection)), 0);
specular = pow(specular, SpecularPower);
specular *= LightStrength;
// Final Output Color:
float4 outColor = float4(0,0,0,1);
outColor.xyz = texColor * (Ambient + shading) + specular;
return outColor;
}
technique Technique1
{
pass Pass1
{
VertexShader = compile vs_2_0 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}