Now, once I had a terrain object I thought it would be great to get a water model to go with it. I thought I would just use the terrain class and modify it so that it was flat. I then needed a shader to do my water effect, and being new to all this 3D stuff and HLSL, did not have the skills to write my own, so I had a mooch on line and found NVIDIA's Ocean shader, with a few tweaks I got it working with my engine.
I am going to give you the whole class at the end along with the modified shader. What I will do now is show the effects of various parameter setting's.
Basic WaterHere is a basic implementation of the class

As you can see, in the picture above, I am reflecting the surrounding skybox and the bumpmap is also applied. The wave amplitude and frequency are set to 0 so you cant see any waves in this example.
RCWater water = new RCWater("Content/Textures/SkyBox/HazyMind/cubeMap", "Content/Textures/Terrain/waves2", "water");
water.SetShader("WAT");
water.Width = 128;
water.Height = 128;
water.WaveFrequency = .0f;
water.WaveAmplitude = 0f;
water.DeepWaterColor = Color.Navy.ToVector4();
water.ShallowWaterColor = Color.DarkSeaGreen.ToVector4();
water.ReflectionColor = Color.DarkGray.ToVector4();
water.ReflectionAmount = 0.7f;
water.Position = new Vector3(0, -1, 0);
water.AnimationSpeed = .05f;
game.Scene.AddObject(water, "water");
BumpMapThis is the bumpmap I am using to ripple the surface of the water

If I remove the bump map from the render this is the effect

Kinda takes the depth out of the water doesn't it.
You can manage the bump height with the BumpMapHeight parameter
water.BumpMapHeight = 0.1f;
The initial picture above uses the default BumpMapHeight of 0.1 the best range is from 0 to 2, so here are both those extremes
With a BumpMapHeight of 0, the water renders like still water, no ripples at all.

With a BumpMapHeight of 2, the water renders like a raging river
WavesOK, I will now set the BumpMapHeight back to default and start creating some waves.

As you can see this is a nice wave effect, this was done by setting the wave amplitude to 1 and the frequency to .1
water.WaveFrequency = .1f;
water.WaveAmplitude = 1f;
Lets see what this looks like now with the maximum recommended BumpMapHeight of 2

Now that just makes me feel a little sea sick...
Water ColorAll the examples so far have been setting the DeepWaterColor to Navy, ShallowWaterColor to DarkSeaGreen and the ReflectionColor to DarkGrey, but as you guessed you can alter these colors too.
So with the following parameter settings
water.DeepWaterColor = Color.Red.ToVector4();
water.ShallowWaterColor = Color.Gold.ToVector4();
water.ReflectionColor = Color.Black.ToVector4();
You get this effect (keeping the frequency and bump height map as before)

Just to clarify the DeepWaterColor and the ShallowWaterColor parameters. In the next image I have set the parameters like this
water.WaveFrequency = .0f;
water.WaveAmplitude = 0f;
water.DeepWaterColor = Color.Gold.ToVector4();
water.ShallowWaterColor = Color.Red.ToVector4();
water.ReflectionColor = Color.DarkGray.ToVector4();
Giving this result

You can see the water closest to the camera is Gold colored where the water in the distance is Red, this gives that deep ocean effect.
"HALO" sparkle effectIn the origional shader you could either have this effect on or off, this is now switchable via the sparkle parameter.
water.Sparkle = true;
Sparkle On

Sparkle Off
Transparent WaterSo what if you want to be able to see the fish swimming in your Ocean? Well you can do this by simply setting the AlphaBlendColor parameter like this
water.DeepWaterColor = Color.Navy.ToVector4();
water.ShallowWaterColor = Color.DarkSeaGreen.ToVector4();
water.ReflectionColor = Color.DarkGray.ToVector4();
water.AlphaBlendColor = Color.White;
So here is the ocean rendered without the AlphaBlendColor parameter being set and then with it set as in the code snippet above.


You still get all the lovely reflection and bump effects, they are just now rendered against a transparent mesh.
SynopsisThis is a very configurable shader and coupled with my RCWater class is easily accessible. As you can see in the images above it can be used in a variety of ways and I have only really touched on the possible parameter variations. However, this shader is GPU heavy, but then I am running it on my laptop which is not exactly designed for high performance 3D rendering.
RCWater
public class RCWater : RCObject, IRCRenderable, IRCLoadable
{
public struct VertexMultitextured
{
public Vector3 Position;
public Vector3 Normal;
public Vector4 TextureCoordinate;
public Vector4 TexWeights;
public static int SizeInBytes = (3 + 3 + 4 + 4) * 4;
public static VertexElement[] VertexElements = new VertexElement[]
{
new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ),
new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ),
};
}
private VertexBuffer vb;
private IndexBuffer ib;
VertexMultitextured[] myVertices;
private int myHeight = 128;
private int myWidth = 128;
private Vector3 basePosition;
public new Vector3 Position
{
get { return basePosition; }
set { basePosition = value; }
}
/// <summary>
/// Default 128
/// </summary>
public int Height
{
get { return myHeight; }
set { myHeight = value; }
}
/// <summary>
/// Default 128
/// </summary>
public int Width
{
get { return myWidth; }
set { myWidth = value; }
}
private string myEnvironmentAsset;
private string myBumpMapAsset;
private TextureCube myEnvironment;
private Texture2D my2DEnvironment;
private Texture2D myHeightMap;
private Texture2D myBumpMap;
private Vector4 myDeepWater = new Vector4(0,0,1,1);
private Vector4 myShallowWater = new Vector4(0,5,5,1);
private Vector4 myReflection = new Vector4(1,1,1,1);
private float myBumpHeight = 0.10f;
private float myHDRMult = 3.0f;
private float myReflectionAmt = 1.0f;
private float myWaterColorAmount = 1.0f;
private float myWaveAmplitude = 1.0f;
private float myWaveFrequency = 0.1f;
private bool sparkle;
/// <summary>
/// Use HALO sparkle effect
/// </summary>
public bool Sparkle
{
get { return sparkle; }
set { sparkle = value; }
}
/// <summary>
/// Min 0, Max 2.0
/// </summary>
public float BumpMapHeight
{
get { return myBumpHeight; }
set { myBumpHeight = value; }
}
/// <summary>
/// Min 0, Max 100
/// </summary>
public float HDRMultiplier
{
get { return myHDRMult; }
set { myHDRMult = value; }
}
/// <summary>
/// Min 0, Max 2.0
/// </summary>
public float ReflectionAmount
{
get { return myReflectionAmt; }
set { myReflectionAmt = value; }
}
/// <summary>
/// Min 0, Max 2.0
/// </summary>
public float WaterColorAmount
{
get { return myWaterColorAmount; }
set { myWaterColorAmount = value; }
}
/// <summary>
/// Min 0, Max 10.0
/// </summary>
public float WaveAmplitude
{
get { return myWaveAmplitude; }
set { myWaveAmplitude = value; }
}
/// <summary>
/// Min 0, Max 1.0
/// </summary>
public float WaveFrequency
{
get { return myWaveFrequency; }
set { myWaveFrequency = value; }
}
private float tick = 0.0f;
private float animSpeed = 0.0f;
private bool alphaCheck;
private Color myAlphaBlendColor;
/// <summary>
/// Set to get transparent water.
/// </summary>
public Color AlphaBlendColor
{
get { return myAlphaBlendColor; }
set
{
myAlphaBlendColor = value;
if (value == Color.Black)
alphaCheck = false;
else
alphaCheck = true;
}
}
/// <summary>
/// Min 0, Max 0.1
/// </summary>
public float AnimationSpeed
{
get { return animSpeed; }
set { animSpeed = value; }
}
/// <summary>
/// Color of deep water
/// </summary>
public Vector4 DeepWaterColor
{
get { return myDeepWater; }
set { myDeepWater = value; }
}
/// <summary>
/// Color of shallow water
/// </summary>
public Vector4 ShallowWaterColor
{
get { return myShallowWater; }
set { myShallowWater = value; }
}
/// <summary>
/// Color of reflection
/// </summary>
public Vector4 ReflectionColor
{
get { return myReflection; }
set { myReflection = value; }
}
public RCWater(string environmentMap, string bumpMap, string name) : base(name)
{
myEnvironmentAsset = environmentMap;
myBumpMapAsset = bumpMap;
myWidth = 128;
myHeight = 128;
}
public void LoadGraphicsContent(GraphicsDevice myDevice, ContentManager myLoader)
{
// Textures
try
{
myEnvironment = myLoader.Load<TextureCube>(myEnvironmentAsset);
}
catch
{
my2DEnvironment = myLoader.Load<Texture2D>(myEnvironmentAsset);
}
myBumpMap = myLoader.Load<Texture2D>(myBumpMapAsset);
myPosition = new Vector3(basePosition.X - (myWidth / 2), basePosition.Y, basePosition.Z - (myHeight / 2));
// Vertices
myVertices = new VertexMultitextured[myWidth * myHeight];
for (int x = 0; x < myWidth; x++)
for (int y = 0; y < myHeight; y++)
{
myVertices[x + y * myWidth].Position = new Vector3(y, 0, x);
myVertices[x + y * myWidth].Normal = new Vector3(0, -1, 0);
myVertices[x + y * myWidth].TextureCoordinate.X = (float)x / 30.0f;
myVertices[x + y * myWidth].TextureCoordinate.Y = (float)y / 30.0f;
}
vb = new VertexBuffer(myDevice, VertexMultitextured.SizeInBytes * myWidth * myHeight, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);
vb.SetData(myVertices);
//Index
short[] terrainIndices = new short[(myWidth - 1) * (myHeight - 1) * 6];
for (short x = 0; x < myWidth - 1; x++)
{
for (short y = 0; y < myHeight - 1; y++)
{
terrainIndices[(x + y * (myWidth - 1)) * 6] = (short)((x + 1) + (y + 1) * myWidth);
terrainIndices[(x + y * (myWidth - 1)) * 6 + 1] = (short)((x + 1) + y * myWidth);
terrainIndices[(x + y * (myWidth - 1)) * 6 + 2] = (short)(x + y * myWidth);
terrainIndices[(x + y * (myWidth - 1)) * 6 + 3] = (short)((x + 1) + (y + 1) * myWidth);
terrainIndices[(x + y * (myWidth - 1)) * 6 + 4] = (short)(x + y * myWidth);
terrainIndices[(x + y * (myWidth - 1)) * 6 + 5] = (short)(x + (y + 1) * myWidth);
}
}
ib = new IndexBuffer(myDevice, typeof(short), (myWidth - 1) * (myHeight - 1) * 6, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);
ib.SetData(terrainIndices);
}
public void Render(GraphicsDevice myDevice)
{
Effect effect = RCShaderManager.GetShader(myShader).Effect;
effect.Parameters["normalMap"].SetValue(myBumpMap);
effect.Parameters["sparkle"].SetValue(sparkle);
if(myEnvironment != null)
effect.Parameters["cubeMap"].SetValue(myEnvironment);
else
effect.Parameters["cubeMap"].SetValue(my2DEnvironment);
effect.Parameters["deepColor"].SetValue(myDeepWater);
effect.Parameters["shallowColor"].SetValue(myShallowWater);
effect.Parameters["reflectionColor"].SetValue(myReflection);
effect.Parameters["time"].SetValue(tick += animSpeed);
effect.Parameters["bumpHeight"].SetValue(myBumpHeight);
effect.Parameters["hdrMultiplier"].SetValue(myHDRMult);
effect.Parameters["reflectionAmount"].SetValue(myReflectionAmt);
effect.Parameters["waterAmount"].SetValue(myWaterColorAmount);
effect.Parameters["waveAmp"].SetValue(myWaveAmplitude);
effect.Parameters["waveFreq"].SetValue(myWaveFrequency);
bool alphaTest = myDevice.RenderState.AlphaTestEnable;
bool alphaBlend = myDevice.RenderState.AlphaBlendEnable;
CompareFunction alphaFunc = myDevice.RenderState.AlphaFunction;
Blend sourceBlend = myDevice.RenderState.SourceBlend;
Blend destinationBlend = myDevice.RenderState.DestinationBlend;
Color blendFator = myDevice.RenderState.BlendFactor;
if (alphaCheck)
{
if (myDevice.RenderState.AlphaTestEnable != true)
myDevice.RenderState.AlphaTestEnable = true;
if (myDevice.RenderState.AlphaBlendEnable != true)
myDevice.RenderState.AlphaBlendEnable = true;
if (myDevice.RenderState.AlphaFunction != CompareFunction.NotEqual)
myDevice.RenderState.AlphaFunction = CompareFunction.NotEqual;
if(myDevice.RenderState.SourceBlend != Blend.BlendFactor)
myDevice.RenderState.SourceBlend = Blend.BlendFactor;
if(myDevice.RenderState.DestinationBlend != Blend.One)
myDevice.RenderState.DestinationBlend = Blend.One;
if (myDevice.RenderState.BlendFactor != myAlphaBlendColor)
myDevice.RenderState.BlendFactor = myAlphaBlendColor;
}
myDevice.Vertices[0].SetSource(vb, 0, VertexMultitextured.SizeInBytes);
myDevice.Indices = ib;
myDevice.VertexDeclaration = new VertexDeclaration(myDevice, VertexMultitextured.VertexElements);
myDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, myWidth * myHeight, 0, (myWidth - 1) * (myHeight - 1) * 2);
if (alphaCheck)
{
if (myDevice.RenderState.AlphaTestEnable != alphaTest)
myDevice.RenderState.AlphaTestEnable = alphaTest;
if (myDevice.RenderState.AlphaBlendEnable != alphaBlend)
myDevice.RenderState.AlphaBlendEnable = alphaBlend;
if (myDevice.RenderState.AlphaFunction != alphaFunc)
myDevice.RenderState.AlphaFunction = alphaFunc;
if (myDevice.RenderState.SourceBlend != sourceBlend)
myDevice.RenderState.SourceBlend = sourceBlend;
if (myDevice.RenderState.DestinationBlend != destinationBlend)
myDevice.RenderState.DestinationBlend = destinationBlend;
if (myDevice.RenderState.BlendFactor != blendFator)
myDevice.RenderState.BlendFactor = blendFator;
}
}
}
Ocean.fx
/*********************************************************************NVMH3****
float Script : STANDARDSGLOBAL <
string UIWidget = "none";
string ScriptClass = "object";
string ScriptOrder = "standard";
string ScriptOutput = "color";
string Script = "Technique=Main;";
> = 0.8;
///////// UNTWEAKABLES //////////////////////
float4x4 world : World < string UIWidget = "none";>;   // World or Model matrix
float4x4 wvp : WorldViewProjection < string UIWidget = "none";>; // Model*View*Projection
float4x4 worldView : WorldView < string UIWidget = "none";>;
float4x4 viewI : ViewInverse < string UIWidget = "none";>;
bool sparkle;
float time : Time < string UIWidget = "none"; >;
//////////////// TEXTURES ///////////////////
texture normalMap : Normal <
string UIName = "Normal Map";
string ResourceName = "waves2.dds";
string ResourceType = "2D";
>;
texture cubeMap : Environment <
string UIName = "Environment Cube Map";
string ResourceName = "CloudyHillsCubemap2.dds";
string ResourceType = "Cube";
>;
sampler2D normalMapSamplerHALO = sampler_state
{
Texture = <normalMap>;
// this is a trick from Halo - use point sampling for sparkles
MagFilter = Linear;
MinFilter = Point;
MipFilter = None;
};
sampler2D normalMapSampler = sampler_state
{
Texture = <normalMap>;
MagFilter = Linear;
MinFilter = Linear;
MipFilter = Linear;
};
samplerCUBE envMapSampler = sampler_state
{
Texture = <cubeMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
///////// TWEAKABLE PARAMETERS //////////////////
float bumpHeight <
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 2.0; float UIStep = 0.01;
string UIName = "Bump Height";
> = 0.1;
float2 textureScale <
string UIName = "Texture Scale";
> = { 8.0, 4.0 };
float2 bumpSpeed <
string UIName = "Bumpmap Translation Speed";
> = { -0.05, 0.0 };
float fresnelBias <
string UIName = "Fresnel Bias";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 1.0; float UIStep = 0.01;
> = 0.1;
float fresnelPower <
string UIName = "Fresnel Exponent";
string UIWidget = "slider";
float UIMin = 1.0; float UIMax = 10.0; float UIStep = 0.01;
> = 4.0;
float hdrMultiplier <
string UIName = "HDR Multiplier";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 100.0; float UIStep = 0.01;
> = 3.0;
float4 deepColor : Diffuse <
string UIName = "Deep Water";
> = {0.0f, 0.0f, 0.1f, 1.0f};
float4 shallowColor : Diffuse <
string UIName = "Shallow Water";
> = {0.0f, 0.5f, 0.5f, 1.0f};
float4 reflectionColor : Specular <
string UIName = "Reflection";
> = {1.0f, 1.0f, 1.0f, 1.0f};
// these are redundant, but makes the ui easier:
float reflectionAmount <
string UIName = "Reflection Amount";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 2.0; float UIStep = 0.01;
> = 1.0f;
float waterAmount <
string UIName = "Water Color Amount";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 2.0; float UIStep = 0.01;
> = 1.0f;
float waveAmp <
string UIName = "Wave Amplitude";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 10.0; float UIStep = 0.1;
> = 1.0;
float waveFreq <
string UIName = "Wave frequency";
string UIWidget = "slider";
float UIMin = 0.0; float UIMax = 1.0; float UIStep = 0.001;
> = 0.1;
//////////// CONNECTOR STRUCTS //////////////////
struct AppData {
float4 Position : POSITION; // in object space
float2 TexCoord : TEXCOORD0;
float3 Tangent : TEXCOORD1;
float3 Binormal : TEXCOORD2;
float3 Normal : NORMAL;
};
struct VertexOutput {
float4 Position : POSITION; // in clip space
float2 TexCoord : TEXCOORD0;
float3 TexCoord1 : TEXCOORD1; // first row of the 3x3 transform from tangent to cube space
float3 TexCoord2 : TEXCOORD2; // second row of the 3x3 transform from tangent to cube space
float3 TexCoord3 : TEXCOORD3; // third row of the 3x3 transform from tangent to cube space
float2 bumpCoord0 : TEXCOORD4;
float2 bumpCoord1 : TEXCOORD5;
float2 bumpCoord2 : TEXCOORD6;
float3 eyeVector : TEXCOORD7;
};
// wave functions ///////////////////////
struct Wave {
float freq; // 2*PI / wavelength
float amp; // amplitude
float phase; // speed * 2*PI / wavelength
float2 dir;
};
#define NWAVES 2
Wave wave[NWAVES] = {
{ 1.0, 1.0, 0.5, float2(-1, 0) },
{ 2.0, 0.5, 1.3, float2(-0.7, 0.7) }
};
float evaluateWave(Wave w, float2 pos, float t)
{
return w.amp * sin( dot(w.dir, pos)*w.freq + t*w.phase);
}
// derivative of wave function
float evaluateWaveDeriv(Wave w, float2 pos, float t)
{
return w.freq*w.amp * cos( dot(w.dir, pos)*w.freq + t*w.phase);
}
// sharp wave functions
float evaluateWaveSharp(Wave w, float2 pos, float t, float k)
{
return w.amp * pow(sin( dot(w.dir, pos)*w.freq + t*w.phase)* 0.5 + 0.5 , k);
}
float evaluateWaveDerivSharp(Wave w, float2 pos, float t, float k)
{
return k*w.freq*w.amp * pow(sin( dot(w.dir, pos)*w.freq + t*w.phase)* 0.5 + 0.5 , k - 1) * cos( dot(w.dir, pos)*w.freq + t*w.phase);
}
///////// SHADER FUNCTIONS ///////////////
VertexOutput BumpReflectWaveVS(AppData IN,
uniform float4x4 WorldViewProj,
uniform float4x4 World,
uniform float4x4 ViewIT,
uniform float BumpScale,
uniform float2 textureScale,
uniform float2 bumpSpeed,
uniform float time,
uniform float waveFreq,
uniform float waveAmp
)
{
VertexOutput OUT;
wave[0].freq = waveFreq;
wave[0].amp = waveAmp;
wave[1].freq = waveFreq*2.0;
wave[1].amp = waveAmp*0.5;
float4 P = IN.Position;
// sum waves
P.y = 0.0;
float ddx = 0.0, ddy = 0.0;
for(int i=0; i<NWAVES; i++) {
P.y += evaluateWave(wave[i], P.xz, time);
float deriv = evaluateWaveDeriv(wave[i], P.xz, time);
ddx += deriv * wave[i].dir.x;
ddy += deriv * wave[i].dir.y;
}
// compute tangent basis
float3 B = float3(1, ddx, 0);
float3 T = float3(0, ddy, 1);
float3 N = float3(-ddx, 1, -ddy);
OUT.Position = mul(P, WorldViewProj);
// pass texture coordinates for fetching the normal map
OUT.TexCoord.xy = IN.TexCoord*textureScale;
time = fmod(time, 100.0);
OUT.bumpCoord0.xy = IN.TexCoord*textureScale + time*bumpSpeed;
OUT.bumpCoord1.xy = IN.TexCoord*textureScale*2.0 + time*bumpSpeed*4.0;
OUT.bumpCoord2.xy = IN.TexCoord*textureScale*4.0 + time*bumpSpeed*8.0;
// compute the 3x3 tranform from tangent space to object space
float3x3 objToTangentSpace;
// first rows are the tangent and binormal scaled by the bump scale
objToTangentSpace[0] = BumpScale * normalize(T);
objToTangentSpace[1] = BumpScale * normalize(B);
objToTangentSpace[2] = normalize(N);
OUT.TexCoord1.xyz = mul(objToTangentSpace, World[0].xyz);
OUT.TexCoord2.xyz = mul(objToTangentSpace, World[1].xyz);
OUT.TexCoord3.xyz = mul(objToTangentSpace, World[2].xyz);
// compute the eye vector (going from shaded point to eye) in cube space
float4 worldPos = mul(P, World);
OUT.eyeVector = ViewIT[3] - worldPos; // view inv. transpose contains eye position in world space in last row
return OUT;
}
// Pixel Shaders
float4 BumpReflectMain(VertexOutput IN,
uniform samplerCUBE EnvironmentMap) : COLOR
{
// fetch the bump normal from the normal map
float4 N;
if(!sparkle)
N = tex2D(normalMapSamplerHALO, IN.TexCoord.xy)*2.0 - 1.0;
else
N = tex2D(normalMapSampler, IN.TexCoord.xy)*2.0 - 1.0;
float3x3 m; // tangent to world matrix
m[0] = IN.TexCoord1;
m[1] = IN.TexCoord2;
m[2] = IN.TexCoord3;
float3 Nw = mul(m, N.xyz);
// float3 E = float3(IN.TexCoord1.w, IN.TexCoord2.w, IN.TexCoord3.w);
float3 E = IN.eyeVector;
float3 R = reflect(-E, Nw);
return texCUBE(EnvironmentMap, R);
}
float4 OceanMain(VertexOutput IN,
uniform samplerCUBE EnvironmentMap,
uniform half4 deepColor,
uniform half4 shallowColor,
uniform half4 reflectionColor,
uniform half4 reflectionAmount,
uniform half4 waterAmount,
uniform half fresnelPower,
uniform half fresnelBias,
uniform half hdrMultiplier
) : COLOR
{
// sum normal maps
half4 t0;
half4 t1;
half4 t2;
if(!sparkle)
{
t0 = tex2D(normalMapSampler, IN.bumpCoord0.xy)*2.0-1.0;
t1 = tex2D(normalMapSampler, IN.bumpCoord1.xy)*2.0-1.0;
t2 = tex2D(normalMapSampler, IN.bumpCoord2.xy)*2.0-1.0;
}
else
{
t0 = tex2D(normalMapSamplerHALO, IN.bumpCoord0.xy)*2.0-1.0;
t1 = tex2D(normalMapSamplerHALO, IN.bumpCoord1.xy)*2.0-1.0;
t2 = tex2D(normalMapSamplerHALO, IN.bumpCoord2.xy)*2.0-1.0;
}
half3 N = t0.xyz + t1.xyz + t2.xyz;
// half3 N = t1.xyz;
half3x3 m; // tangent to world matrix
m[0] = IN.TexCoord1;
m[1] = IN.TexCoord2;
m[2] = IN.TexCoord3;
half3 Nw = mul(m, N.xyz);
Nw = normalize(Nw);
// reflection
float3 E = normalize(IN.eyeVector);
half3 R = reflect(-E, Nw);
half4 reflection = texCUBE(EnvironmentMap, R);
// hdr effect (multiplier in alpha channel)
reflection.rgb *= (1.0 + reflection.a*hdrMultiplier);
// fresnel - could use 1D tex lookup for this
half facing = 1.0 - max(dot(E, Nw), 0);
half fresnel = fresnelBias + (1.0-fresnelBias)*pow(facing, fresnelPower);
half4 waterColor = lerp(deepColor, shallowColor, facing);
return waterColor*waterAmount + reflection*reflectionColor*reflectionAmount*fresnel;
// return waterColor;
// return fresnel;
// return reflection;
}
//////////////////// TECHNIQUE ////////////////
technique Main <
string Script = "Pass=p0;";
> {
pass p0 <
string Script = "Draw=geometry;";
> {
VertexShader = compile vs_1_1 BumpReflectWaveVS(wvp, world, viewI,
bumpHeight, textureScale, bumpSpeed, time,
waveFreq, waveAmp);
Zenable = true;
ZWriteEnable = true;
// CullMode = None;
// PixelShader = compile ps_2_0 BumpReflectMain(envMapSampler);
PixelShader = compile ps_2_0 OceanMain(envMapSampler,
deepColor, shallowColor, reflectionColor, reflectionAmount, waterAmount,
fresnelPower, fresnelBias, hdrMultiplier);
}
}
///////////////////////////////// eof ///
So my next post will be on my Fire class and it's shader.