33.水面WaterSceneNode类

要看懂这个教程,请首先看懂3D系列4.7 水面3D系列4.8 绘制折射贴图3D系列4.9 绘制反射贴图3D系列4.10 完美镜像3D系列4.11 波浪 初级凹凸映射3D系列4.12 使用菲涅尔项混合折射3D系列4.13 让水面移动添加风向3D系列4.14 镜面高光反射-平面凹凸映射

相对于上一个教程的海面,创建水面简单的地方在于水面的起伏没有海面这么大,所以水面实际上是一个平面,水波凹凸感是用一张法线贴图模拟的,而海面上的大波浪是真实有起伏的。但海面通常很辽阔,所以它一般不反射岸边的物体,只反射天空,而且海水通常很深,也无需绘制海面之下的场景,而水面就要考虑绘制反射和折射,这也是难点所在。水面WaterSceneNode类本身很简单,代码如下:

namespace StunEngine.SceneNodes
{
    /// 
    /// 水面
    /// 
    public class WaterSceneNode : Renderable3DSceneNode
    {

        #region 构造函数和成员变量        

        /// 
        /// 水面使用的材质
        /// 
        private SingleTextureMaterial material;
        
        /// 
        /// 默认的水面的长和宽为128*128
        /// 
        int width = 128;
        int height = 128;        

        /// 
        /// 振幅,默认为0.3
        /// 
        float waveHeight = 0.3f;        

        /// 
        /// 波长,默认为0.1
        /// 
        float waveLength = 0.1f;        
        
        /// 
        /// 水流方向,默认为向前
        /// 
        Vector3 windDirection = new Vector3(0, 0, 1);        

        /// 
        /// 风的强度,即水流的移动速度,默认值为0.002
        /// 
        float windForce = 0.02f;        

        /// 
        /// 创建一个默认水面,使用引擎中的水面凹凸贴图,水面高度为5
        /// 
        /// 引擎
        /// 所属场景
        public WaterSceneNode(StunXnaGE engine, Scene setScene)
            : this(engine, setScene,"Textures/waterBump",5)
        {
            
        }

        /// 
        /// 创建一个水面。      
        /// 
        /// 引擎        
        public WaterSceneNode(StunXnaGE engine, Scene setScene, string setBumpTextureName, float setWaterHeight)
            : base(engine, setScene)
        {
            //创建材质
            this.material =new SingleTextureMaterial (engine ,engine.Content .Load ("Effects\\water"));
            this.material .Texture1Name  = setBumpTextureName;

            //不对水面进行剔除操作
            this.DisableCulling = true;
            this.DisableUpdateCulling = true;            

            //设置effect参数的默认值
            material.EffectInstance.Parameters["gTexture1UVTile"].SetValue(1.0f / waveLength);
            material.EffectInstance.Parameters["gWaveHeight"].SetValue(waveHeight);
            material.EffectInstance.Parameters["gWindForce"].SetValue(windForce);
            material.EffectInstance.Parameters["gWindDirection"].SetValue(windDirection);

            // 实现IMaterial接口
            Imaterial = (IMaterial)material;
        }
   
        #endregion

        #region 属性
        
        /// 
        /// 获取或设置风的强度,即水流的移动速度,默认值为0.002。
        /// 
        public float WindForce
        {
            get { return windForce; }
            set 
            { 
                windForce = value;
                material.EffectInstance.Parameters["gWindForce"].SetValue(windForce);
            }
        }

        /// 
        /// 获取或设置振幅,默认为0.3
        /// 
        public float WaveHeight
        {
            get { return waveHeight; }
            set 
            { 
                waveHeight = value;
                material.EffectInstance.Parameters["gWaveHeight"].SetValue(waveHeight);
            }
        }

        /// 
        /// 获取或设置波长,默认为0.1,其实改变的凹凸纹理的平铺次数,若波长为0.1,则平铺次数为倒数10次
        /// 
        public float WaveLength
        {
            get { return waveLength; }
            set 
            {
                waveLength = value;
                material.EffectInstance.Parameters["gTexture1UVTile"].SetValue(1.0f/waveLength);
            }
        }

        /// 
        /// 水流方向,默认为向前
        /// 
        public Vector3 WindDirection
        {
            get { return windDirection; }
            set { 
                windDirection = value;
                material.EffectInstance.Parameters["gWindDirection"].SetValue(windDirection);
            }
        }

        #endregion        

        public override void Initialize()
        {
            this.UpdateOrder = SceneNodeOrdering.Terrain .GetValue();           
            base.Initialize();
            //调用MeshBuilder的静态CreateWater方法创建水面的顶点数据
            this.mesh = MeshBuilder.CreateWater(engine.GraphicsDevice, width, height);            
        }
     
        /// 
        /// 绘制水面。
        /// 
        ///         
        public override int Draw(GameTime gameTime,bool useReflection)
        {
            material.EffectInstance.Parameters["gReflectionView"].SetValue(scene.Camera.ReflectionViewMatrix);
            material.EffectInstance.Parameters["gReflectionMap"].SetValue(scene.ReflectionMap);            
            material.EffectInstance.Parameters["gRefractionMap"].SetValue(scene.RefractionMap);

            return base.Draw(gameTime, useReflection);
        }      

        #region 单元测试

#if DEBUG

        /// 
        /// 测试WaterSceneNode类
        /// 
        public static void TestWaterSceneNode()
        {
            WaterSceneNode water = null;
            SkyBoxSceneNode skybox = null;
            CubeSceneNode cube1 = null;
            SphereSceneNode sphere2 = null;
            SkyDomeSceneNode skyDome = null;
            Vector3 position;

            TestGame.Start("测试WaterSceneNode类",
                delegate
                {
                    //添加一个天空球
                    skyDome = new SkyDomeSceneNode(TestGame.engine, TestGame.scene, "Textures/SkyDome", 16, 32, MathHelper.Pi * 0.6f);
                    TestGame.scene.AddNode(skyDome);
                    skyDome.IsReflected = true;
                    skyDome.Visible = false;

                    //添加一个天空盒
                    skybox = new SkyBoxSceneNode(TestGame.engine, TestGame.scene, "Textures/SkyCubeMap");
                    TestGame.scene.AddNode(skybox);
                    skybox.IsReflected = true;      
                    
                    //添加一个水面
                    water = new WaterSceneNode(TestGame.engine, TestGame.scene);
                    TestGame.scene.AddNode(water);                    

                    //将水面中心放置在坐标原点
                    position = new Vector3(-64, 0, 64);
                    water.Pose.SetPosition(ref position);

                    //在右侧添加一个白色不带纹理的球体,需要反射和折射
                    sphere2 = new SphereSceneNode(TestGame.engine, TestGame.scene, null);
                    TestGame.scene.AddNode(sphere2);
                    position = new Vector3(5f, 0f, 0.0f);
                    sphere2.Pose.SetPosition(ref position);
                    sphere2.Material.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
                    sphere2.Material.SpecularColor = new Vector3(1.0f, 1.0f, 1.0f);
                    sphere2.Material.SpecularPower = 50;
                    sphere2.IsReflected = true;
                    sphere2.IsRefracted = true;

                    //在左侧添加一个覆盖有岩石纹理的正方体,需要反射和折射
                    cube1 = new CubeSceneNode(TestGame.engine, TestGame.scene, "Textures\\Rock");
                    TestGame.scene.AddNode(cube1);
                    Vector3 scale = new Vector3(4.0f, 4.0f, 4.0f);
                    cube1.Pose.SetScale(ref scale);
                    position = new Vector3(-5f, 0f,0f);
                    cube1.Pose.SetPosition(ref position);
                    cube1.Material.SpecularColor = new Vector3(1.0f, 1.0f, 1.0f);
                    cube1.Material.SpecularPower = 50;
                    cube1.IsReflected = true;
                    cube1.IsRefracted = true;

                    // 将地板向下移动并施加折射效果
                    position = new Vector3(0.0f, -2f, 0.0f);
                    TestGame.scene.floor.Pose.SetPosition(ref position);
                    TestGame.scene.floor.IsRefracted = true;

                    TestGame.scene.IsShowMouse = false;
                    TestGame.scene.CanReflect = true;
                    TestGame.scene.CanRefract = true;

                },
                delegate
                {
                    // 按数字1键切换水面的缩放
                    if (Input.KeyboardKeyJustPressed(Keys.D1))
                    {
                        Vector3 scale = Vector3.One;
                        position = new Vector3(-64f, 0f, 64f);
                        if (water.Pose.Scale == scale)
                        {
                            scale = new Vector3(2.0f, 1.0f, 2.0f);
                            water.Pose.SetScale(ref scale);
                            position = new Vector3(-128f, 1f, 128f);
                            water.Pose.SetPosition(ref position);
                            TestGame.scene.WaterHeight = 1.0f;
                        }
                        else
                        {
                            scale = Vector3.One;
                            water.Pose.SetScale(ref scale);
                            water.Pose.SetPosition(ref position);
                            TestGame.scene.WaterHeight = 0.0f;
                        }
                    }

                    // 按数字2键切换波长
                    if (Input.KeyboardKeyJustPressed(Keys.D2))
                    {
                        if (water.WaveLength == 0.1f)
                        {
                            water.WaveLength = 0.2f;
                        }
                        else
                        {
                            water.WaveLength = 0.1f;
                        }
                    }

                    // 按数字3键切换正方体高度
                    if (Input.KeyboardKeyJustPressed(Keys.D3))
                    {
                        position = new Vector3(-5f, -0.0f, 0f);
                        if (cube1.Pose.Position == position )
                        {
                            position = new Vector3(-5f, 4.0f, 0f);
                            cube1.Pose.SetPosition (ref position);
                        }
                        else
                        {
                            cube1.Pose.SetPosition (ref position );
                        }
                    }

                    // 按数字4键切换显示天空盒和天空球
                    if (Input.KeyboardKeyJustPressed(Keys.D4))
                    {
                        if (skybox.Visible)
                        {
                            skybox.Visible = false;
                            skyDome.Visible = true;
                        }
                        else
                        {
                            skybox.Visible = true;
                            skyDome.Visible = false;
                        }
                    }

                },
                delegate
            {
                //在屏幕上绘制反射贴图和折射贴图
                StunXnaGE.SpriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate,
                                  SaveStateMode.SaveState);

                StunXnaGE.SpriteBatch.Draw(TestGame.scene.RefractionMap, new Rectangle(500, 20, 128, 128), Color.White);                
                    StunXnaGE.SpriteBatch.Draw(TestGame.scene.ReflectionMap, new Rectangle(650, 20, 128, 128), Color.White);
                StunXnaGE.SpriteBatch.End();
            });
        }
#endif
        #endregion
    }
}

创建水面顶点的方法位于MeshBuilder类的CreateWater静态方法中,也很简单,就是创建四个顶点,图元类型为TriangleStrip,其实和创建平面的CreateQuad方法是类似的,而且它无需设置法线信息,相比起来更加简单,代码如下:

/// 
/// 创建水面顶点
/// 
/// 图像设备
/// 水面宽度
/// 水面高度
public static Mesh CreateWater(GraphicsDevice g, int Width, int Height)
{
    //定义四个顶点创建覆盖整个水面的两个三角形。
    VertexPositionTexture[] waterVertices = new VertexPositionTexture[4];

    waterVertices[0] = new VertexPositionTexture(new Vector3(0, 0, -Height), new Vector2(0, 0));
    waterVertices[1] = new VertexPositionTexture(new Vector3(Width, 0, -Height), new Vector2(1, 0));
    waterVertices[2] = new VertexPositionTexture(new Vector3(0, 0, 0), new Vector2(0, 1));
    waterVertices[3] = new VertexPositionTexture(new Vector3(Width, 0, 0), new Vector2(1, 1));

    VertexBuffer waterVertexBuffer = new VertexBuffer(g, waterVertices.Length * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);
    waterVertexBuffer.SetData(waterVertices);
    VertexDeclaration waterVertexDeclaration = new VertexDeclaration(g, VertexPositionTexture.VertexElements);

    //创建水面的mesh
    return new Mesh(PrimitiveType.TriangleStrip, waterVertexBuffer, waterVertexDeclaration, 4, 2);
}

根据文章3D系列4.8 绘制折射贴图3D系列4.9 绘制反射贴图,要绘制反射贴图和折射贴图,需要定义渲染目标,剪裁平面并将反射贴图和折射贴图绘制到水面上,这些操作我都放在了场景scene类中了。首先在scene类中添加以下变量:

/// 
/// 是否实现反射
/// 
private bool canReflect = false;

/// 
/// 是否实现折射
/// 
private bool canRefract = false;

/// 
/// 用于折射的渲染目标
/// 
RenderTarget2D refractionRenderTarget;

/// 
/// 折射贴图
/// 
Texture2D refractionMap;

/// 
/// 用于反射的渲染目标
/// 
RenderTarget2D reflectionRenderTarget;

/// 
/// 反射贴图
/// 
Texture2D reflectionMap;

/// 
/// 场景中的水面高度,默认为0
/// 
float waterHeight = 0.0f;

并添加用于创建剪裁平面的CreatePlane方法、用于绘制折射贴图的DrawRefractionMap方法和DrawReflectionMap方法:

/// 
/// 根据参数创建一个平面
/// 
/// 平面离开XZ平面的高度
/// 法线方向
/// 当前视矩阵
/// 如果设为ture,则此平面之上的部分会被剪裁,如果设为false,则此平面之下的部分被剪裁
/// 
private Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, bool clipSide)
{
    // 归一化法线
    planeNormalDirection.Normalize();
    // 创建平面系数
    Vector4 planeCoeffs = new Vector4(planeNormalDirection, -height);

    if (clipSide)
        planeCoeffs *= -1;

    // 剪裁平面的四个系数必须定义在剪裁空间中(这样显卡可以容易地判断哪些物体需要被绘制哪些需要被剪裁)。
    // 要将系数从3D空间映射到剪裁空间,所以需要通过ViewProjection的反置(inverse-transpose)矩阵变换系数
    Matrix worldViewProjection = currentViewMatrix * this.Camera.ProjectionMatrix;
    Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
    inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

    // 将平面系数转换到正确的空间中并创建平面
    planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
    Plane finalPlane = new Plane(planeCoeffs);            
    return finalPlane;
}

/// 
/// 绘制折射贴图
/// 
/// 
private void DrawRefractionMap(GameTime gameTime)
{
    // 创建剪裁平面,设置为只绘制此平面之下的场景
    Plane refractionPlane = CreatePlane(waterHeight, new Vector3(0, 1, 0), this.Camera.ViewMatrix, true);
    // 激活剪裁平面
    engine.GraphicsDevice.ClipPlanes[0].Plane = refractionPlane;
    engine.GraphicsDevice.ClipPlanes[0].IsEnabled = true;

    // 将refractionRenderTarget作为当前渲染目标并绘制需要折射的节点
    engine.GraphicsDevice.SetRenderTarget(0, refractionRenderTarget);
    engine.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

    for (int i = 0; i < renderList.Count; i++)
    {
        Renderable3DSceneNode rsn = (Renderable3DSceneNode)nodes[renderList[i]];

        if (rsn.IsRefracted)
            rsn.Draw(gameTime, false);
    }

    // 关闭剪裁平面
    engine.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
    // 将渲染目标重新设置为后备缓冲
    engine.GraphicsDevice.SetRenderTarget(0, null);
    // 将渲染目标的内容复制到折射贴图中
    refractionMap = refractionRenderTarget.GetTexture();
}

/// 
/// 绘制反射贴图
/// 
/// 
private void DrawReflectionMap(GameTime gameTime)
{
    // 创建剪裁平面,设置为只绘制此平面之上的场景
    Plane reflectionPlane = CreatePlane(waterHeight, new Vector3(0, 1, 0), this.Camera.ReflectionViewMatrix, false );

    // 激活剪裁平面
    engine.GraphicsDevice.ClipPlanes[0].Plane = reflectionPlane;
    engine.GraphicsDevice.ClipPlanes[0].IsEnabled = true;

    // 将reflectionRenderTarget作为当前渲染目标并绘制需要反射的节点
    engine.GraphicsDevice.SetRenderTarget(0, reflectionRenderTarget);
    engine.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

    for (int i = 0; i < renderList.Count; i++)
    {
        Renderable3DSceneNode rsn = (Renderable3DSceneNode)nodes[renderList[i]];

        if (rsn.IsReflected)
        {
            rsn.Draw(gameTime, true);                    
        }                    
    }
    // 关闭剪裁平面
    engine.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
    // 将渲染目标重新设置为后备缓冲
    engine.GraphicsDevice.SetRenderTarget(0, null);
    // 将渲染目标的内容复制到反射贴图中
    reflectionMap = reflectionRenderTarget.GetTexture();
}

并在scene类的draw方法中调用DrawRefractionMap和DrawReflectionMap方法。

有些细节还要参考3.13 创建一面镜子:投影纹理

下面我补充关于平面Plane的创建的内容,这可以参见XNA的帮助文件,有这样一个图:

创建平面

 Plane构造函数有多个重载方法,其中一个是

public Plane (Vector4 value)

Vector4类型的value参数的X, Y和Z分量定义了Plane的法线,W分量定义了沿法线方向上原点离开平面的距离。 因此要定义一个位于XZ平面上方距离为1,正面朝上平面的代码为:

Plane p = new Plane(new Vector4(0,1,0,-1));

Effect文件

比较难的是水面的effect代码,位于water.fx文件内:

//-----------------------------------------------------------------------------------------------
//	Technique:	 Water(水面)
//	
//	作者:		www.riemers.net
//
//	-----------------------------------------------------------------------------------------------

shared uniform extern float4x4	gProjection : PROJECTION;   // 共享的投影矩阵
shared uniform extern float gTime;							// 共享的时间变量
shared uniform extern int		gTotalLights;				// 共享的光源数量

// 包含光源数据的结构
struct Light
{
    float enabled;      //光源是否打开    
    float lightType;	//光源类型
    float3 color;		//光源颜色
    float3 position;	//光源位置
    float3 direction;	//光线方向
    float4 spotData;	//四个分量分别保存range,falloff,theta,phi数据
};

//光源数组
shared Light gLights[8];

uniform extern float3	gCameraPos;	                       // 相机位置 
uniform extern float4x4	gView : VIEW;					   // 视矩阵
uniform extern float4x4 gReflectionView;				   // 反射视矩阵
uniform extern float4x4	gWorld : WORLD;			           // 世界矩阵

uniform extern float gWaveHeight;						   // 振幅
uniform extern float gWindForce;						   // 风力大小,即波速
uniform extern float3 gWindDirection;					   // 风向,即水流方向

uniform extern texture gTexture1;                         // 水面的凹凸贴图
uniform extern float2	gTexture1UVTile;                  // 凹凸纹理的平铺次数
sampler BumpMapSampler = sampler_state { texture =  ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

uniform extern texture gReflectionMap;                   //反射贴图
sampler ReflectionSampler = sampler_state { texture =  ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

uniform extern texture gRefractionMap;                   //折射贴图
sampler RefractionSampler = sampler_state { texture =  ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

struct VS_OUTPUT
{
    float4 Position                  : POSITION;
    float4 ReflectionMapSamplingPos  : TEXCOORD1;
    float2 BumpMapSamplingPos        : TEXCOORD2;
    float4 RefractionMapSamplingPos  : TEXCOORD3;
    float4 WorldPosition             : TEXCOORD4;
}; 


VS_OUTPUT WaterVS(float4 inPos : POSITION, float2 inTex: TEXCOORD)
{    
    VS_OUTPUT Output = (VS_OUTPUT)0;

    float4x4 preViewProjection = mul (gView, gProjection);
    float4x4 preWorldViewProjection = mul (gWorld, preViewProjection);
    Output.Position = mul(inPos, preWorldViewProjection);
    Output.WorldPosition = mul(inPos, gWorld);
    
    // 计算反射纹理的采样坐标
    float4x4 preReflectionViewProjection = mul (gReflectionView, gProjection);
    float4x4 preWorldReflectionViewProjection = mul (gWorld, preReflectionViewProjection);    
    Output.ReflectionMapSamplingPos = mul(inPos, preWorldReflectionViewProjection);          
    
    // 设置纹理的采样坐标
    Output.RefractionMapSamplingPos = mul(inPos, preWorldViewProjection);            
    
    // 归一化水流方向
    float3 windDir = normalize(gWindDirection);    
    // 获取垂直于水流的方向
    float3 perpDir = cross(gWindDirection, float3(0,1,0));
    
    // 获取经水流方向修正的纹理uv坐标
    float ydot = dot(inTex, gWindDirection.xz);
    float xdot = dot(inTex, perpDir.xz);
    float2 moveVector = float2(xdot, ydot);
    // 让纹理的v坐标随时间移动
    moveVector.y += gTime*gWindForce;    
    // 获取最终的凹凸纹理采样坐标
    Output.BumpMapSamplingPos = moveVector*gTexture1UVTile;
    
    return Output;
}

float4 WaterPS(VS_OUTPUT Input):COLOR0
{
    // 采样凹凸纹理颜色
    float4 bumpColor = tex2D(BumpMapSampler, Input.BumpMapSamplingPos);
    float2 perturbation = gWaveHeight*(bumpColor.rg - 0.5f)*2.0f;
    
    // 将反射贴图采样坐标从2D屏幕空间映射到纹理坐标
    float2 ProjectedReflectTexCoords;
    ProjectedReflectTexCoords.x = Input.ReflectionMapSamplingPos.x/Input.ReflectionMapSamplingPos.w/2.0f + 0.5f;
    ProjectedReflectTexCoords.y = -Input.ReflectionMapSamplingPos.y/Input.ReflectionMapSamplingPos.w/2.0f + 0.5f;        
    float2 perturbatedTexCoords = ProjectedReflectTexCoords + perturbation;
    float4 reflectiveColor = tex2D(ReflectionSampler, perturbatedTexCoords);
    
    // 将折射贴图采样坐标从2D屏幕空间映射到纹理坐标
    float2 ProjectedRefrTexCoords;
    ProjectedRefrTexCoords.x = Input.RefractionMapSamplingPos.x/Input.RefractionMapSamplingPos.w/2.0f + 0.5f;
    ProjectedRefrTexCoords.y = -Input.RefractionMapSamplingPos.y/Input.RefractionMapSamplingPos.w/2.0f + 0.5f;    
    float2 perturbatedRefrTexCoords = ProjectedRefrTexCoords + perturbation;    
    float4 refractiveColor = tex2D(RefractionSampler, perturbatedRefrTexCoords);
    
    // 从凹凸贴图获取法线向量
    float3 eyeVector = normalize(gCameraPos - Input.WorldPosition);
    float3 normalVector = (bumpColor.rbg-0.5f)*2.0f;
    
    // 计算菲涅尔系数,并根据这个系数混合反射和折射颜色
    float fresnelTerm = dot(eyeVector, normalVector);    
    float4 combinedColor = lerp(reflectiveColor, refractiveColor, fresnelTerm);
    
    // 在水面再添加蓝灰色让它变得“脏”一点
    float4 dullColor = float4(0.3f, 0.3f, 0.5f, 1.0f);
    
    // 将蓝灰色混合到最终颜色
    float4 color = lerp(combinedColor, dullColor, 0.2f);
    
    // 设置光源方向,为简化起见,只使用场景中的单向光方向
    float3 gLightDirection=float3(-1,-1,-1);
    //----------------------------
	//	遍历所有光源
	//----------------------------   
	for(int i=0; i < gTotalLights; i++)
	{    
		
		//只处理可用的光源
		if(gLights[i].enabled&&gLights[i].lightType==0)
		{
			 gLightDirection=gLights[i].direction;
		}
	}        
    
    //添加水面的镜面反射颜色
    float3 reflectionVector = -reflect(gLightDirection, normalVector);    
    float specular = pow(dot(normalize(reflectionVector), normalize(eyeVector)), 256);        
    color.rgb += specular;
    
    return color;
}

technique Water
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 WaterVS();
        PixelShader = compile ps_3_0 WaterPS();
    }
}

具体解释可参见3D系列4.10 完美镜像3D系列4.11 波浪 初级凹凸映射3D系列4.12 使用菲涅尔项混合折射3D系列4.13 让水面移动添加风向3D系列4.14 镜面高光反射-平面凹凸映射

单元测试截图如下:

单元测试

文件下载(已下载 1616 次)

发布时间:2010/5/10 下午1:06:22  阅读次数:7131

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号