3.15 将阴影添加到场景

原文地址:http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series3/Real_shadow.php

现在我们已经可以从阴影贴图中采样正确的位置了,就做好了绘制阴影的准备了。我们需要使用真实颜色绘制场景,所以顶点着色器需要将每个顶点的纹理坐标、法线和3D位置传递到插值器和像素着色器中。所以需要在SsceneVertexToPixel中添加3个变量:

float2 TexCoords : TEXCOORD1; 
float3 Normal : TEXCOORD2; 
float4 Position3D : TEXCOORD3; 

在前面的章节中我们已经知道如何生成这些值了。

本章,我们还要根据阴影贴图中的值测试像素和光源间的真实距离。在创建阴影贴图的一章中,这个距离可以从由光源视场中看到的2D位置获取,只需将Z分量除以齐次坐标即可。像素着色器已经可以访问这个2D坐标,因此它存储在了Pos2DasSeenByLight变量中。

别忘了我们再次需要发送到顶点数据流中的所有信息,所以请特别注意第一行代码:

SSceneVertexToPixel ShadowedSceneVertexShader( float4 inPos : POSITION, float2 inTexCoords : TEXCOORD0, float3 inNormal : NORMAL)
{
    SSceneVertexToPixel Output = (SSceneVertexToPixel)0;

    Output.Position = mul(inPos, xWorldViewProjection);    
    Output.Pos2DAsSeenByLight = mul(inPos, xLightsWorldViewProjection);    
    Output.Normal = normalize(mul(inNormal, (float3x3)xWorld));    
    Output.Position3D = mul(inPos, xWorld);
    Output.TexCoords = inTexCoords;    

    return Output;
}

顶点着色器没有改变,所以看一下像素着色器。我们只绘制被头灯照亮的像素。这意味着首先必须检查像素是否在头灯光照范围内。换句话说:投影的x和y坐标是否在[0, 1]范围之内。要做到这点,我们可以使用HLSL saturate方法,这个方法可以将值截取到这个范围之内。所以如果截取后的值不等于截取前的值,我们就知道这个值不在[0, 1]范围之内,即它不在头灯视场之内。在HLSL代码中:

float diffuseLightingFactor = 0;
if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y))
{
}
        
float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords);
if (xSolidBrown == true)
baseColor = float4(0.25f, 0.21f, 0.20f, 1);
                
Output.Color = baseColor*(diffuseLightingFactor + xAmbient);

我们首先将光照因子设为0,if代码块检查当前像素是否在光照范围内,如果是就计算光照因子(下面会讨论到),如果不是,则光照因子保持为0,像素的颜色只有环境光颜色。

因此如果像素成功地进入了if代码块,即这个像素会被光照到。接下来,我们会检查这个像素是否被另一个物体遮挡。要做到这步,我们首先要获取像素和光源间的距离,这个距离存储在了阴影贴图中:

float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r; 

然后,我们计算像素和光源的真实距离,这和创建阴影贴图一章中的做法是一样的:

realDistance = PSIn.Pos2DAsSeenByLight.z/PSIn.Pos2DAsSeenByLight.w; 

现在检查这个真实距离是否大于存储在阴影贴图中的值:

if ((realDistance - 1.0f/100.0f) <= depthStoredInShadowMap) { } 

你可以看到我们减去了一个很小的偏移值(bias) 1/100。其原因是:我们在阴影贴图中将深度信息存储为一个颜色值,但此贴图的颜色通道只有8位,即最小容差是1/256。假设场景的最大距离时40.0f,那么最小的距离差为40/256 = 0.156f。这意味着在即使在最理想的情况中,位于距离在0.156f至0.234f之间的点都会被认为是0.156f。

例如一个点的真实距离是2.0f (对应存储在阴影贴图中的值为0.156f)。你想检查这个真实距离和存储在阴影贴图中的距离是否一样:if (2.0f == 0.156f)结果为FAIL,你会认为这个点是在另一个物体的阴影中。基于这个理由,我们将真实距离减去一个小值用于比较。

如果真实距离等于存储在阴影贴图中的距离,这个像素就应该被照亮,需要改变diffuseLightingFactor变量,这个变量的计算方法就是前几章提到的计算点乘值。下面是像素着色器代码:

SScenePixelToFrame ShadowedScenePixelShader(SSceneVertexToPixel PSIn)
{
    SScenePixelToFrame Output = (SScenePixelToFrame)0;    

    float2 ProjectedTexCoords;
    ProjectedTexCoords[0] = PSIn.Pos2DAsSeenByLight.x/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f;
    ProjectedTexCoords[1] = -PSIn.Pos2DAsSeenByLight.y/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f;
    
    float diffuseLightingFactor = 0;
    if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y))
    {
        float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r;
        float realDistance = PSIn.Pos2DAsSeenByLight.z/PSIn.Pos2DAsSeenByLight.w;
        if ((realDistance - 1.0f/100.0f) <= depthStoredInShadowMap)
        {
            diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal);
            diffuseLightingFactor = saturate(diffuseLightingFactor);
            diffuseLightingFactor *= xLightPower;            
        }
    }
        
    float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords);
            if (xSolidBrown == true)
                baseColor = float4(0.25f, 0.21f, 0.20f, 1);
                
    Output.Color = baseColor*(diffuseLightingFactor + xAmbient);

    return Output;
}

有了HLSL代码,下面是XNA代码。

因为我们只使用一个光源,所以增强了这个光源的强度: lightPower = 2f; 运行这个代码就会获得如下图相似的结果,但不是100%相同,你获得的图像有点粗糙,例如看一下左边灯柱的阴影。

这是因为你在阴影贴图中以Color的形式存储了深度值,这很好,因为这样做可以让我们以灰度值的形式可视化结果。但是,阴影贴图中4个颜色通道的值是一样的。每个像素占据32位,所以深度存储为8位,只有2^8=256级变化。

要获得更好的结果,你可以指定每个像素只使用一个通道而不是4个。这可以使用以下代码实现,这个代码可以让每个距离存储为完整的32位,具有2^32中可能的距离变化!

renderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, SurfaceFormat.Single); 

从这个相机位置你可能看不到很大的不同,但如果将相机移动到靠近阴影的位置,就会发现效果有很大的改进!

程序截图

最终,我们看到了第一个阴影效果!汽车投下了一个完美的阴影,你还能看到灯柱的阴影。注意:只有在头灯视场范围之内的像素被照亮,其他地方只有环境光颜色。

虽然我们获得了阴影,但光线本身的形状还不够漂亮:你可以看到视锥体的顶角。下一章我们会解决这个问题。

HLSL代码如下:

float4x4 xLightsWorldViewProjection;
float4x4 xWorldViewProjection;
float4x4 xWorld;
bool xSolidBrown;
float3 xLightPos;
float xLightPower;
float xAmbient;

Texture xTexture;

sampler TextureSampler = sampler_state { texture =  ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};
Texture xShadowMap;

sampler ShadowMapSampler = sampler_state { texture =  ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = clamp; AddressV = clamp;};
struct VertexToPixel
{
    float4 Position     : POSITION;    
    float2 TexCoords    : TEXCOORD0;
    float3 Normal        : TEXCOORD1;
    float3 Position3D    : TEXCOORD2;
};

struct PixelToFrame
{
    float4 Color        : COLOR0;
};

VertexToPixel SimplestVertexShader( float4 inPos : POSITION0, float3 inNormal: NORMAL0, float2 inTexCoords : TEXCOORD0)
{
    VertexToPixel Output = (VertexToPixel)0;
    
    Output.Position =mul(inPos, xWorldViewProjection);
    Output.TexCoords = inTexCoords;
    Output.Normal = normalize(mul(inNormal, (float3x3)xWorld));    
    Output.Position3D = mul(inPos, xWorld);
    
    return Output;
}

float DotProduct(float3 lightPos, float3 pos3D, float3 normal)
{
    float3 lightDir = normalize(pos3D - lightPos);
    return dot(-lightDir, normal);    
}

PixelToFrame OurFirstPixelShader(VertexToPixel PSIn)
{
    PixelToFrame Output = (PixelToFrame)0;        

    float diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal);
    diffuseLightingFactor = saturate(diffuseLightingFactor);
    diffuseLightingFactor *= xLightPower;
    
    float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords);
    if (xSolidBrown == true)
        baseColor = float4(0.25f, 0.21f, 0.20f, 1);
    
    Output.Color = baseColor*(diffuseLightingFactor + xAmbient);

    return Output;
}

technique Simplest
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 SimplestVertexShader();
        PixelShader = compile ps_2_0 OurFirstPixelShader();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////

struct SMapVertexToPixel
{
    float4 Position     : POSITION;
    float4 Position2D    : TEXCOORD0;
};

struct SMapPixelToFrame
{
    float4 Color : COLOR0;
};

SMapVertexToPixel ShadowMapVertexShader( float4 inPos : POSITION)
{
    SMapVertexToPixel Output = (SMapVertexToPixel)0;

    Output.Position = mul(inPos, xLightsWorldViewProjection);
    Output.Position2D = Output.Position;

    return Output;
}

SMapPixelToFrame ShadowMapPixelShader(SMapVertexToPixel PSIn)
{
    SMapPixelToFrame Output = (SMapPixelToFrame)0;            

    Output.Color = PSIn.Position2D.z/PSIn.Position2D.w;

    return Output;
}

technique ShadowMap
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 ShadowMapVertexShader();
        PixelShader = compile ps_2_0 ShadowMapPixelShader();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////

struct SSceneVertexToPixel
{
    float4 Position             : POSITION;
    float4 Pos2DAsSeenByLight    : TEXCOORD0;

     float2 TexCoords            : TEXCOORD1;
     float3 Normal                : TEXCOORD2;
     float4 Position3D            : TEXCOORD3;

};

struct SScenePixelToFrame
{
    float4 Color : COLOR0;
};

SSceneVertexToPixel ShadowedSceneVertexShader( float4 inPos : POSITION, float2 inTexCoords : TEXCOORD0, float3 inNormal : NORMAL)
{
    SSceneVertexToPixel Output = (SSceneVertexToPixel)0;

    Output.Position = mul(inPos, xWorldViewProjection);    
    Output.Pos2DAsSeenByLight = mul(inPos, xLightsWorldViewProjection);    

     Output.Normal = normalize(mul(inNormal, (float3x3)xWorld));    
     Output.Position3D = mul(inPos, xWorld);
     Output.TexCoords = inTexCoords;    


    return Output;
}

SScenePixelToFrame ShadowedScenePixelShader(SSceneVertexToPixel PSIn)
{
    SScenePixelToFrame Output = (SScenePixelToFrame)0;    

    float2 ProjectedTexCoords;
    ProjectedTexCoords[0] = PSIn.Pos2DAsSeenByLight.x/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f;
    ProjectedTexCoords[1] = -PSIn.Pos2DAsSeenByLight.y/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f;
    

     float diffuseLightingFactor = 0;
     if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y))
     {
         float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r;
         float realDistance = PSIn.Pos2DAsSeenByLight.z/PSIn.Pos2DAsSeenByLight.w;
         if ((realDistance - 1.0f/100.0f) <= depthStoredInShadowMap)
         {
             diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal);
             diffuseLightingFactor = saturate(diffuseLightingFactor);
             diffuseLightingFactor *= xLightPower;            
         }
     }
         
     float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords);
             if (xSolidBrown == true)
                 baseColor = float4(0.25f, 0.21f, 0.20f, 1);
                 
     Output.Color = baseColor*(diffuseLightingFactor + xAmbient);


    return Output;
}

technique ShadowedScene
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 ShadowedSceneVertexShader();
        PixelShader = compile ps_2_0 ShadowedScenePixelShader();
    }
}

在XNA代码中只有一些小变化:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace XNAseries3
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        struct MyOwnVertexFormat
        {
            private Vector3 position;
            private Vector2 texCoord;
            private Vector3 normal;

            public MyOwnVertexFormat(Vector3 position, Vector2 texCoord, Vector3 normal)
            {
                this.position = position;
                this.texCoord = texCoord;
                this.normal = normal;
            }

            public static VertexElement[] VertexElements =
            {
                new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
                new VertexElement(0, sizeof(float)*3, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
                new VertexElement(0, sizeof(float)*5, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0),
            };

            public static int SizeInBytes = sizeof(float) * (3 + 2 + 3);
        }

        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        
        Effect effect;
        Matrix viewMatrix;
        Matrix projectionMatrix;
        VertexBuffer vertexBuffer;
        VertexDeclaration vertexDeclaration;
        Vector3 cameraPos;

        Texture2D streetTexture;
        Model lamppostModel;
        Texture2D[] lamppostTextures;
        Model carModel;
        Texture2D[] carTextures;

        Vector3 lightPos;
        float lightPower;
        float ambientPower;

        Matrix lightsViewProjectionMatrix;

        RenderTarget2D renderTarget;
        Texture2D shadowMap;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);            
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 500;
            graphics.PreferredBackBufferHeight = 500;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();
            Window.Title = "Riemer's XNA Tutorials -- Series 3";

            base.Initialize();
        }

        protected override void LoadContent()
        {
            device = GraphicsDevice;

           effect = Content.Load
effect> ("OurHLSLfile");
           streetTexture = Content.Load ("streettexture");
           carModel = LoadModel("racer", out carTextures);
           lamppostModel = LoadModel("lamppost", out lamppostTextures);

           SetUpVertices();
           SetUpCamera();

           PresentationParameters pp = device.PresentationParameters;

            renderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, SurfaceFormat.Single);
        }

        private Model LoadModel(string assetName, out Texture2D[] textures)
        {

           Model newModel = Content.Load (assetName);
           textures = new Texture2D[7];
           int i = 0;
           foreach (ModelMesh mesh in newModel.Meshes)
               foreach (BasicEffect currentEffect in mesh.Effects)
                   textures[i++] = currentEffect.Texture;

           foreach (ModelMesh mesh in newModel.Meshes)
               foreach (ModelMeshPart meshPart in mesh.MeshParts)
                   meshPart.Effect = effect.Clone(device);

           return newModel;
       }

       private void SetUpVertices()
       {
           MyOwnVertexFormat[] vertices = new MyOwnVertexFormat[18];

           vertices[0] = new MyOwnVertexFormat(new Vector3(-20, 0, 10), new Vector2(-0.25f, 25.0f), new Vector3(0, 1, 0));
           vertices[1] = new MyOwnVertexFormat(new Vector3(-20, 0, -100), new Vector2(-0.25f, 0.0f), new Vector3(0, 1, 0));
           vertices[2] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f), new Vector3(0, 1, 0));
           vertices[3] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f), new Vector3(0, 1, 0));
           vertices[4] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f), new Vector3(-1, 0, 0));
           vertices[5] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f), new Vector3(-1, 0, 0));
           vertices[6] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f), new Vector3(-1, 0, 0));
           vertices[7] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f), new Vector3(-1, 0, 0));
           vertices[8] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f), new Vector3(0, 1, 0));
           vertices[9] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f), new Vector3(0, 1, 0));
           vertices[10] = new MyOwnVertexFormat(new Vector3(3, 1, 10), new Vector2(0.5f, 25.0f), new Vector3(0, 1, 0));
           vertices[11] = new MyOwnVertexFormat(new Vector3(3, 1, -100), new Vector2(0.5f, 0.0f), new Vector3(0, 1, 0));
           vertices[12] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f), new Vector3(0, 1, 0));
           vertices[13] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f), new Vector3(0, 1, 0));
           vertices[14] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f), new Vector3(-1, 0, 0));
           vertices[15] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f), new Vector3(-1, 0, 0));
           vertices[16] = new MyOwnVertexFormat(new Vector3(13, 21, 10), new Vector2(1.25f, 25.0f), new Vector3(-1, 0, 0));
           vertices[17] = new MyOwnVertexFormat(new Vector3(13, 21, -100), new Vector2(1.25f, 0.0f), new Vector3(-1, 0, 0));

           vertexBuffer = new VertexBuffer(device, vertices.Length * MyOwnVertexFormat.SizeInBytes, BufferUsage.WriteOnly);
           vertexBuffer.SetData(vertices);

           vertexDeclaration = new VertexDeclaration(device, MyOwnVertexFormat.VertexElements);
       }

       private void SetUpCamera()
       {
           cameraPos = new Vector3(-25, 13, 18);
           viewMatrix = Matrix.CreateLookAt(cameraPos, new Vector3(0, 2, -12), new Vector3(0, 1, 0));
           projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 5.0f, 100.0f);
       }

       protected override void UnloadContent()
       {
       }

       protected override void Update(GameTime gameTime)
       {            
           if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
               this.Exit();

           UpdateLightData();

           base.Update(gameTime);
       }

       private void UpdateLightData()
       {
           ambientPower = 0.5f;

           lightPos = new Vector3(-18, 5, -2);

            lightPower = 2.0f;

            Matrix lightsView = Matrix.CreateLookAt(lightPos, new Vector3(-2, -3, -10), new Vector3(0, 1, 0));
            Matrix lightsProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, 1f, 1f, 100f);

            lightsViewProjectionMatrix = lightsView* lightsProjection;
        }

        protected override void Draw(GameTime gameTime)
        {
            device.SetRenderTarget(0, renderTarget);

            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            DrawScene("ShadowMap");

            device.SetRenderTarget(0, null);
            shadowMap = renderTarget.GetTexture();

            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            DrawScene("ShadowedScene");

            base.Draw(gameTime);
        }

        private void DrawScene(string technique)
        {
            effect.CurrentTechnique = effect.Techniques[technique];
            effect.Parameters["xWorldViewProjection"].SetValue(Matrix.Identity * viewMatrix * projectionMatrix);
            effect.Parameters["xTexture"].SetValue(streetTexture);
            effect.Parameters["xWorld"].SetValue(Matrix.Identity);
            effect.Parameters["xLightPos"].SetValue(lightPos);
            effect.Parameters["xLightPower"].SetValue(lightPower);
            effect.Parameters["xAmbient"].SetValue(ambientPower);
            effect.Parameters["xLightsWorldViewProjection"].SetValue(Matrix.Identity * lightsViewProjectionMatrix);
            effect.Parameters["xShadowMap"].SetValue(shadowMap);
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.VertexDeclaration = vertexDeclaration;
                device.Vertices[0].SetSource(vertexBuffer, 0, MyOwnVertexFormat.SizeInBytes);
                device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 18);
                pass.End();
            }
            effect.End();

            Matrix car1Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(-3, 0, -15);
            DrawModel(carModel, carTextures, car1Matrix, technique, false);

            Matrix car2Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi * 5.0f / 8.0f) * Matrix.CreateTranslation(-28, 0, -1.9f);
            DrawModel(carModel, carTextures, car2Matrix, technique, false);

            Matrix lamp1Matrix = Matrix.CreateScale(0.05f) * Matrix.CreateTranslation(4.0f, 1, -35);
            DrawModel(lamppostModel, lamppostTextures, lamp1Matrix, technique, true);

            Matrix lamp2Matrix = Matrix.CreateScale(0.05f) * Matrix.CreateTranslation(4.0f, 1, -5);
            DrawModel(lamppostModel, lamppostTextures, lamp2Matrix, technique, true);
        }

        private void DrawModel(Model model, Texture2D[] textures, Matrix wMatrix, string technique, bool solidBrown)
        {
            Matrix[] modelTransforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(modelTransforms);
            int i = 0;
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (Effect currentEffect in mesh.Effects)
                {
                    Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;
                    currentEffect.CurrentTechnique = currentEffect.Techniques[technique];                    
                    currentEffect.Parameters["xWorldViewProjection"].SetValue(worldMatrix * viewMatrix * projectionMatrix);
                    currentEffect.Parameters["xTexture"].SetValue(textures[i++]);
                    currentEffect.Parameters["xSolidBrown"].SetValue(solidBrown);
                    currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
                    currentEffect.Parameters["xLightPos"].SetValue(lightPos);
                    currentEffect.Parameters["xLightPower"].SetValue(lightPower);
                    currentEffect.Parameters["xAmbient"].SetValue(ambientPower);
                    currentEffect.Parameters["xLightsWorldViewProjection"].SetValue(worldMatrix * lightsViewProjectionMatrix);
                    currentEffect.Parameters["xShadowMap"].SetValue(shadowMap);
                }
                mesh.Draw();
            }
        }

    }
}
文件下载(已下载 1147 次)

发布时间:2010/5/28 上午8:06:52  阅读次数:15001

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

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号