
cobra
striver
-
个人空间
相册
- 性别:
- 来自:拼吾爱
- 积分:7097
- 帖子:7009
- 注册:
2007-04-09
|
XNA水面效果
几乎所有的室外场景游戏都少不了水面效果的渲染,基中主要包括波浪效果、反射效果、折射效果等,在这之前我对它可是一无所知,现在总算是有了初步的了解,主要是归功于XNA的快速。当然,我的主要目的还是对算法进行学习。 原理我还是不说,Google一下,讲的很好的。我这里主要有1个Water类和一个VertexMultitextured结构体,还有一个Effect。  附件: 您所在的用户组无法下载或查看附件用它做法线 Water类是实现水面效果的主要类  Code
public class Water:DrawableGameComponent { #region 字段
Camera camera;
private VertexBuffer vb; private IndexBuffer ib; VertexMultitextured[] myVertices; private int myHeight = 128; private int myWidth = 128;
private Vector3 myPosition; private Vector3 myScale; private Quaternion myRotation;
Effect effect;
private Vector3 basePosition;
public Vector3 Position { get { return basePosition; } set { basePosition = value; } }
private string EnvAsset;
float bumpHeight = 0.5f; Vector2 textureScale = new Vector2(4, 4); Vector2 bumpSpeed = new Vector2(0, .05f); float fresnelBias = .025f; float fresnelPower = 1.0f; float hdrMultiplier = 1.0f; Color deepWaterColor = Color.Black; Color shallowWaterColor = Color.SkyBlue; Color reflectionColor = Color.White; float reflectionAmount = 0.5f; float waterAmount = 0f; float waveAmplitude = 0.5f; float waveFrequency = 0.1f;
/// <summary> /// 水面凹凸纹理高度,最小0,最大2,默认0.5 /// </summary> public float BumpHeight { get { return bumpHeight; } set { bumpHeight = value; } } /// <summary> /// 凹凸纹理缩放倍数 /// </summary> public Vector2 TextureScale { get { return textureScale; } set { textureScale = value; } } /// <summary> /// 水流速度 /// </summary> public Vector2 BumpSpeed { get { return bumpSpeed; } set { bumpSpeed = value; } } /// <summary> /// 0到1, 默认 0.025 /// </summary> public float FresnelBias { get { return fresnelBias; } set { fresnelBias = value; } } /// <summary> ///0到10, 默认 1.0; /// </summary> public float FresnelPower { get { return FresnelPower; } set { fresnelPower = value; } } /// <summary> /// 0到100, 默认1.0 /// </summary> public float HDRMultiplier { get { return hdrMultiplier; } set { hdrMultiplier = value; } } /// <summary> /// 深水处颜色; /// </summary> public Color DeepWaterColor { get { return deepWaterColor; } set { deepWaterColor = value; } } /// <summary> ///浅水处颜色 /// </summary> public Color ShallowWaterColor { get { return shallowWaterColor; } set { shallowWaterColor = value; } } /// <summary> /// 反射默认颜色 /// </summary> public Color ReflectionColor { get { return reflectionColor; } set { reflectionColor = value; } } /// <summary> /// 0.0 到2.0 /// </summary> public float ReflectionAmount { get { return reflectionAmount; } set { reflectionAmount = value; } }
public float WaterAmount { get { return waterAmount; } set { waterAmount = value; } }
public float WaveAmplitude { get { return waveAmplitude; } set { waveAmplitude = value; } }
public float WaveFrequency { get { return waveFrequency; } set { waveFrequency = value; } }
public int Height { get { return myHeight; } set { myHeight = value; } }
public int Width { get { return myWidth; } set { myWidth = value; } }
#endregion
public Water(Game game, string Environment,Camera camera): base(game) { myWidth = 256; myHeight = 256;
myPosition = new Vector3(0, 0, 0); myScale = new Vector3(80,1,80); myRotation = new Quaternion(0, 0, 0, 1);
EnvAsset = Environment;
this.camera = camera; }
protected override void LoadContent() { effect = Game.Content.Load<Effect>("Water");
effect.Parameters["tEnvMap"].SetValue(Game.Content.Load<TextureCube>(EnvAsset)); effect.Parameters["tNormalMap"].SetValue(Game.Content.Load<Texture2D>("waves"));
myPosition = new Vector3(basePosition.X - (myWidth / 2), basePosition.Y, basePosition.Z - (myHeight / 2));
// 顶点 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; }
// 计算切线法线 for (int x = 0; x < myWidth; x++) for (int y = 0; y < myHeight; y++) { // 切线数据. if (x != 0 && x < myWidth - 1) myVertices[x + y * myWidth].Tangent = myVertices[x - 1 + y * myWidth].Position - myVertices[x + 1 + y * myWidth].Position; else if (x == 0) myVertices[x + y * myWidth].Tangent = myVertices[x + y * myWidth].Position - myVertices[x + 1 + y * myWidth].Position; else myVertices[x + y * myWidth].Tangent = myVertices[x - 1 + y * myWidth].Position - myVertices[x + y * myWidth].Position;
// 法线数据. if (y != 0 && y < myHeight - 1) myVertices[x + y * myWidth].BiNormal = myVertices[x + (y - 1) * myWidth].Position - myVertices[x + (y + 1) * myWidth].Position; else if (y == 0) myVertices[x + y * myWidth].BiNormal = myVertices[x + y * myWidth].Position - myVertices[x + (y + 1) * myWidth].Position; else myVertices[x + y * myWidth].BiNormal = myVertices[x + (y - 1) * myWidth].Position - myVertices[x + y * myWidth].Position; }
vb = new VertexBuffer(Game.GraphicsDevice, VertexMultitextured.SizeInBytes * myWidth * myHeight, BufferUsage.WriteOnly); vb.SetData(myVertices);
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(Game.GraphicsDevice, typeof(short), (myWidth - 1) * (myHeight - 1) * 6, BufferUsage.WriteOnly); ib.SetData(terrainIndices);
Game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(Game.GraphicsDevice, VertexMultitextured.VertexElements);
base.LoadContent(); }
public override void Update(GameTime gameTime) { effect.Parameters["fTime"].SetValue((float)gameTime.TotalRealTime.TotalSeconds);
if (Keyboard.GetState().IsKeyDown(Keys.D1)) WaterAmount = 0;
if (Keyboard.GetState().IsKeyDown(Keys.D2)) { WaterAmount = 1; ShallowWaterColor = Color.DarkSeaGreen; DeepWaterColor = Color.Navy; ReflectionColor = Color.DarkGray; }
if (Keyboard.GetState().IsKeyDown(Keys.D3)) { WaterAmount = 1; ShallowWaterColor = Color.Gold; DeepWaterColor = Color.Red; ReflectionColor = Color.White; }
if (Keyboard.GetState().IsKeyDown(Keys.D4)) { WaveFrequency = .5f; WaveAmplitude = .3f; BumpHeight = 1f; }
if (Keyboard.GetState().IsKeyDown(Keys.D5)) { WaveFrequency = .1f; WaveAmplitude = .1f; BumpHeight = .1f; }
if (Keyboard.GetState().IsKeyDown(Keys.D6)) { WaveFrequency = .1f; WaveAmplitude = .5f; BumpHeight = .1f; }
if (Keyboard.GetState().IsKeyDown(Keys.D7)) { WaveFrequency = .1f; WaveAmplitude = 2f; BumpHeight = 1f; }
if (Keyboard.GetState().IsKeyDown(Keys.D8)) { WaveFrequency = 0; WaveAmplitude = 0; BumpHeight = 0; }
if (Keyboard.GetState().IsKeyDown(Keys.D9)) { WaveFrequency = 0; WaveAmplitude = 0; BumpHeight = 1; } if (Keyboard.GetState().IsKeyDown(Keys.D0)) SetDefault();
base.Update(gameTime); }
public override void Draw(GameTime gameTime) { Matrix World = Matrix.CreateScale(myScale) * Matrix.CreateFromQuaternion(myRotation) * Matrix.CreateTranslation(myPosition);
Matrix WVP = World * camera.View * camera.Projection; Matrix WV = World * camera.View; Matrix viewI = Matrix.Invert(camera.View);
effect.Parameters["matWorldViewProj"].SetValue(WVP); effect.Parameters["matWorld"].SetValue(World); effect.Parameters["matWorldView"].SetValue(WV); effect.Parameters["matViewI"].SetValue(viewI);
effect.Parameters["fBumpHeight"].SetValue(bumpHeight); effect.Parameters["vTextureScale"].SetValue(textureScale); effect.Parameters["vBumpSpeed"].SetValue(bumpSpeed); effect.Parameters["fFresnelBias"].SetValue(fresnelBias); effect.Parameters["fFresnelPower"].SetValue(fresnelPower); effect.Parameters["fHDRMultiplier"].SetValue(hdrMultiplier); effect.Parameters["vDeepColor"].SetValue(deepWaterColor.ToVector4()); effect.Parameters["vShallowColor"].SetValue(shallowWaterColor.ToVector4()); effect.Parameters["vReflectionColor"].SetValue(reflectionColor.ToVector4()); effect.Parameters["fReflectionAmount"].SetValue(reflectionAmount); effect.Parameters["fWaterAmount"].SetValue(waterAmount); effect.Parameters["fWaveAmp"].SetValue(waveAmplitude); effect.Parameters["fWaveFreq"].SetValue(waveFrequency);
Game.GraphicsDevice.Vertices[0].SetSource(vb, 0, VertexMultitextured.SizeInBytes); Game.GraphicsDevice.Indices = ib;
//Game.GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
effect.Begin(SaveStateMode.SaveState); for (int p = 0; p < effect.CurrentTechnique.Passes.Count; p++) { effect.CurrentTechnique.Passes[p].Begin();
Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, myWidth * myHeight, 0, (myWidth - 1) * (myHeight - 1) * 2);
effect.CurrentTechnique.Passes[p].End(); } effect.End();
//Game.GraphicsDevice.RenderState.FillMode = FillMode.Solid;
base.Draw(gameTime); }
public void SetDefault() { bumpHeight = 0.5f; textureScale = new Vector2(4, 4); bumpSpeed = new Vector2(0, .05f); fresnelBias = .025f; fresnelPower = 1.0f; hdrMultiplier = 1.0f; deepWaterColor = Color.Black; shallowWaterColor = Color.SkyBlue; reflectionColor = Color.White; reflectionAmount = 0.5f; waterAmount = 0f; waveAmplitude = 0.5f; waveFrequency = 0.1f; } }
而VertexMultitextured结构体主要是水面的数据  Code
public struct VertexMultitextured { public Vector3 Position; public Vector3 Normal; public Vector2 TextureCoordinate; public Vector3 Tangent; public Vector3 BiNormal;
public static int SizeInBytes = (3 + 3 + 2 + 3 + 3) * 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.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ), new VertexElement( 0, sizeof(float) * 8, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Tangent, 0 ), new VertexElement( 0, sizeof(float) * 11, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Binormal, 0 ), };
}
最重要的是Shader  Code
float fTime : Time;
//-------------------------------------------------- // 全局变量 //-------------------------------------------------- float4x4 matWorldViewProj : WorldViewProjection; float4x4 matWorld : World; float4x4 matWorldView : WorldView; float4x4 matViewI : ViewInverse;
float fBumpHeight < string UIName = "Bump Height"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 2.0f; float UIStep = 0.1f; > = 0.5f;
float2 vTextureScale < string UIName = "Texture Scale"; string UIWidget = "Vector"; > = { 4.0f, 4.0f };
float2 vBumpSpeed < string UIName = "Bump Speed"; string UIWidget = "Vector"; > = { -0.0f, 0.05f };
float fFresnelBias < string UIName = "Fresnel Bias"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 1.0f; float UIStep = 0.1f; > = 0.025f;
float fFresnelPower < string UIName = "Fresnel Exponent"; string UIWidget = "Slider"; float UIMin = 1.0f; float UIMax = 10.0f; float UIStep = 0.1f; > = 1.0f;
float fHDRMultiplier < string UIName = "HDR Multiplier"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 100.0f; float UIStep = 1.0f; > = 1.0f;
float4 vDeepColor : Diffuse < string UIName = "Deep Water Color"; string UIWidget = "Vector"; > = { 0.0f, 0.40f, 0.50f, 1.0f };
float4 vShallowColor : Diffuse < string UIName = "Shallow Water Color"; string UIWidget = "Vector"; > = { 0.55f, 0.75f, 0.75f, 1.0f };
float4 vReflectionColor : Diffuse < string UIName = "Reflection Color"; string UIWidget = "Vector"; > = { 1.0f, 1.0f, 1.0f, 1.0f };
float fReflectionAmount < string UIName = "Reflection Amount"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 2.0f; float UIStep = 0.1f; > = 0.5f;
float fWaterAmount < string UIName = "Water Color Amount"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 2.0f; float UIStep = 0.1f; > = 0.5f;
float fWaveAmp < string UIName = "Wave Amplitude"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 10.0f; float UIStep = 0.1f; > = 0.5f;
float fWaveFreq < string UIName = "Wave Frequency"; string UIWidget = "Slider"; float UIMin = 0.0f; float UIMax = 1.0f; float UIStep = 0.001f; > = 0.1f;
texture tNormalMap : Normal < string UIName = "Normal Map"; string ResourceName = "waves2.dds"; string TextureType = "2D"; >;
texture tEnvMap : Environment < string UIName = "Environment Map"; string ResourceName = "CloudyHillsCubemap2.dds"; string TextureType = "Cube"; >;
sampler2D s0 = sampler_state { Texture = (tNormalMap); MipFilter = Linear; MinFilter = Linear; MagFilter = Linear; };
samplerCUBE s1 = sampler_state { Texture = (tEnvMap); MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; };
//-------------------------------------------------- // Vertex shader //-------------------------------------------------- struct VSOUTPUT { float4 vPos : POSITION; float2 vTex : TEXCOORD0; float3 vTanToCube[3] : TEXCOORD1; float2 vBump0 : TEXCOORD4; float2 vBump1 : TEXCOORD5; float2 vBump2 : TEXCOORD6; float3 vView : TEXCOORD7; };
// 水波 struct Wave { float fFreq; // 频率 (2PI / 波长) float fAmp; // 振幅 float fPhase; // 相伴 (速度 * 2PI / 波长) float2 vDir; // 方向 };
#define NUMWAVES 3 Wave Waves[NUMWAVES] = { { 1.0f, 1.00f, 0.50f, float2( -1.0f, 0.0f ) }, { 2.0f, 0.50f, 1.30f, float2( -0.7f, 0.7f ) }, { .50f, .50f, 0.250f, float2( 0.2f, 0.1f ) }, };
float EvaluateWave( Wave w, float2 vPos, float fTime ) { return w.fAmp * sin( dot( w.vDir, vPos ) * w.fFreq + fTime * w.fPhase ); }
float EvaluateWaveDifferential( Wave w, float2 vPos, float fTime ) { return w.fAmp * w.fFreq * cos( dot( w.vDir, vPos ) * w.fFreq + fTime * w.fPhase ); }
float EvaluateWaveSharp( Wave w, float2 vPos, float fTime, float fK ) { return w.fAmp * pow( sin( dot( w.vDir, vPos ) * w.fFreq + fTime * w.fPhase )* 0.5 + 0.5 , fK ); }
float EvaluateWaveSharpDifferential( Wave w, float2 vPos, float fTime, float fK ) { return fK * w.fFreq * w.fAmp * pow( sin( dot( w.vDir, vPos ) * w.fFreq + fTime * w.fPhase )* 0.5 + 0.5 , fK - 1 ) * cos( dot( w.vDir, vPos ) * w.fFreq + fTime * w.fPhase ); }
VSOUTPUT VS_Water( float4 inPos : POSITION, float3 inNor : NORMAL, float2 inTex : TEXCOORD0, float3 inTan : TANGENT, float3 inBin : BINORMAL ) { VSOUTPUT OUT = (VSOUTPUT)0; // 产生水波 Waves[0].fFreq = fWaveFreq; Waves[0].fAmp = fWaveAmp;
Waves[1].fFreq = fWaveFreq * 2.0f; Waves[1].fAmp = fWaveAmp * 0.5f; Waves[2].fFreq = fWaveFreq * 3.0f; Waves[2].fAmp = fWaveAmp * 1.0f;
// 水波总数 inPos.y = 0.0f; float ddx = 0.0f, ddy = 0.0f; for( int i = 0; i < NUMWAVES; i++ ) { inPos.y += EvaluateWave( Waves, inPos.xz, fTime ); float diff = EvaluateWaveDifferential( Waves, inPos.xz, fTime); ddx += diff * Waves.vDir.x; ddy += diff * Waves.vDir.y; }
// 输出position OUT.vPos = mul( inPos, matWorldViewProj ); // 门生法线图texture coordinates OUT.vTex = inTex * vTextureScale;
fTime = fmod( fTime, 100.0 ); OUT.vBump0 = inTex * vTextureScale + fTime * vBumpSpeed; OUT.vBump1 = inTex * vTextureScale * 2.0f + fTime * vBumpSpeed * 4.0; OUT.vBump2 = inTex * vTextureScale * 4.0f + fTime * vBumpSpeed * 8.0;
// 计算切线 float3 vB = float3( 1, ddx, 0 ); float3 vT = float3( 0, ddy, 1 ); float3 vN = float3( -ddx, 1, -ddy );
// Compute the tangent space to object space matrix float3x3 matTangent = float3x3( fBumpHeight * normalize( vT ), fBumpHeight * normalize( vB ), normalize( vN ) ); OUT.vTanToCube[0] = mul( matTangent, matWorld[0].xyz ); OUT.vTanToCube[1] = mul( matTangent, matWorld[1].xyz ); OUT.vTanToCube[2] = mul( matTangent, matWorld[2].xyz );
// 计算世界空间向量 float4 vWorldPos = mul( inPos, matWorld ); OUT.vView = matViewI[3].xyz - vWorldPos; return OUT; }
//-------------------------------------------------- // Pixel shader //-------------------------------------------------- float3 Refract( float3 vI, float3 vN, float fRefIndex, out bool fail ) { float fIdotN = dot( vI, vN ); float k = 1 - fRefIndex * fRefIndex * ( 1 - fIdotN * fIdotN ); fail = k < 0; return fRefIndex * vI - ( fRefIndex * fIdotN + sqrt(k) )* vN; }
float4 PS_Water( VSOUTPUT IN ) : COLOR0 {
// Fetch the normal maps (with signed scaling) float4 t0 = tex2D( s0, IN.vBump0 ) * 2.0f - 1.0f; float4 t1 = tex2D( s0, IN.vBump1 ) * 2.0f - 1.0f; float4 t2 = tex2D( s0, IN.vBump2 ) * 2.0f - 1.0f;
float3 vN = t0.xyz + t1.xyz + t2.xyz;
// Compute the tangent to world matrix float3x3 matTanToWorld; matTanToWorld[0] = IN.vTanToCube[0]; matTanToWorld[1] = IN.vTanToCube[1]; matTanToWorld[2] = IN.vTanToCube[2]; float3 vWorldNormal = mul( matTanToWorld, vN ); vWorldNormal = normalize( vWorldNormal );
// 计算反射向量 IN.vView = normalize( IN.vView ); float3 vR = reflect( -IN.vView, vWorldNormal ); // Sample the cube map float4 vReflect = texCUBE( s1, vR.zyx ); vReflect = texCUBE( s1, vR ); // Exaggerate the HDR effect vReflect.rgb *= ( 1.0 + vReflect.a * fHDRMultiplier );
// Compute the Fresnel term float fFacing = 1.0 - max( dot( IN.vView, vWorldNormal ), 0 ); float fFresnel = fFresnelBias + ( 1.0 - fFresnelBias ) * pow( fFacing, fFresnelPower);
// 计算最终颜色 float4 vWaterColor = lerp( vDeepColor, vShallowColor, fFacing );
return vWaterColor * fWaterAmount + vReflect * vReflectionColor * fReflectionAmount * fFresnel; }
//-------------------------------------------------- // Techniques //-------------------------------------------------- technique techDefault { pass p0 { CullMode = None; VertexShader = compile vs_2_0 VS_Water(); PixelShader = compile ps_2_0 PS_Water(); } }
好了,代码出来了,下面可以看下实现后的效果。  附件: 您所在的用户组无法下载或查看附件有波浪的水面  附件: 您所在的用户组无法下载或查看附件平静的水面 这里也只算是一个比较基本的水面效果,还不够真实,可以去完善一下。 (文/desmend 出处/博客园) | 感谢原创者的辛勤劳动,希望对您有所帮助,转载请注明原出处。 |
|