您的位置:首页 > 其它

在Windows Phone中进行3D开发之十二飞行

2012-01-06 21:15 309 查看
在完成了飞船和天空的两个元素以后,现在要作的是把二者结合起来,让飞船在天空中自由飞翔。
现在我们面对了如下两个问题:一是空间中的位置关系,二是飞船飞行时与天空盒的碰撞或穿越。
先来看第一个问题,在画飞船的时候,是画在了原点上,在画天空的时候,同样也是在原点上。飞船的坐标值很大,以至于在画飞船的时候使用-0.02的缩放矩阵对其进行缩小处理,而天空盒的边长仅为2个坐标单位,我们需要如何处理这个坐标关系呢?另外,渲染天空盒时摄像机是位于盒子里的,渲染飞船时摄像机是位于飞船外的,因此对于摄像机的位置选择也是一件麻烦事。
另一个问题,假设我们能够把飞船画在天空盒当中,并且正确的放置了摄像机,那么当飞船飞行的时候,天空会越来越近,总会达到天空盒的边界,此时会与天空盒发生碰撞,下一刻就会穿越了,跑到天空盒外面去了,这个问题看起来也不容易解决。
其实,对于这两个问题,可以通过控制渲染器的深度缓冲区来解决。

我们知道,3D和2D的最大区别就是有一个深度关系,因此渲染器会使用深度缓冲区来进行这方面的处理,利用不同的z轴坐标,在深度缓冲区中跟踪多个物体在纵深方向的前后关系。简单点说,就是利用一块内存保存各顶点到摄像机的深度数据,从而决定可见的被渲染,不可见的不渲染,在视觉上产生了遮挡等深度效果。
对于天空盒和飞船,目前二者都会有深度信息参与渲染,因此才会出现位置上的感觉和碰撞的可能。如果能够对这种深度进行控制,让天空盒不存在深度,只保留飞船和摄像机的深度信息,那么前述的两个问题就会迎刃而解了。
我们还是能过代码体现一下。首先在MainScene中为SkyBox和Ship分别生成一个对象,并将其加入到Components中。然后在LoadContent()中确定摄像机及两个物体的坐标信息。

protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            camera = new Camera(this, new Vector3(100,100,100), new Vector3(0, 0, -1), Vector3.Up, MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.1f, 500);

            basicEffect = new BasicEffect(GraphicsDevice);

            skyBox.projectionMatrix = camera.projection;
            skyBox.viewMatrix = Matrix.CreateWorld(new Vector3(0,0,0),new Vector3(0,0,-1),Vector3.Up);

            ship.projectionMatrix = camera.projection;
            ship.viewMatrix = camera.view * Matrix.CreateTranslation(new Vector3(4, 0, 10));
            ship.worldMatrix = Matrix.CreateScale(-0.02f) * Matrix.CreateRotationZ(MathHelper.ToRadians(180))*Matrix.CreateTranslation(new Vector3(20,0,0));
        }

在上述代码中,先创建了摄像机,将其置于(100,100,100)坐标位置,该位置可以更好的观察飞船。

天空盒的变换矩阵中,投影矩阵采用摄像机矩阵,但视图矩阵使用了单独的视图矩阵,即将摄像机放置于原点位置,让其在该视图变换下进行渲染。这与上一节天空盒中的摄像机位置是一样的,所以只对天空盒来说,它不受camera的影响。
飞船的投影变换使用摄像机矩阵,视图变换使用摄像机矩阵,世界矩阵使用了一个矩阵变换,即先对飞船进行缩小,然后在z轴上进行了180度的旋转,再向x轴正向平移20个坐标单位。以期得到合适的飞船方向和比例。
在Update()方法中继续沿用之前通过响应屏幕点击进行天空盒旋转的处理方式。
接下来,打开SkyBox类的代码,这次我们需要修改Draw()方法了,按照刚才的解决方案,要去掉天空盒渲染时的深度信息,因此,现在的代码变为:
public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
            DepthStencilState depthState = new DepthStencilState();
            depthState.DepthBufferWriteEnable = false;
            GraphicsDevice.DepthStencilState = depthState;
            RasterizerState rasterizerState = new RasterizerState();
            rasterizerState.CullMode = CullMode.CullClockwiseFace;
            GraphicsDevice.RasterizerState = rasterizerState;
            basicEffect.TextureEnabled = true;
            basicEffect.Texture = texture;
            basicEffect.World = worldMatrix;
            basicEffect.View = viewMatrix;
            basicEffect.Projection = projectionMatrix;
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                Game.GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, box, 0, box.Length/3);
            }
            base.Draw(gameTime);
            DepthStencilState depthState2 = new DepthStencilState();
            depthState2.DepthBufferWriteEnable = true;
            GraphicsDevice.DepthStencilState = depthState2;
        }

在上述代码中,绘制的部分是与原来相同的,但是多了DepthStencilState对象的使用,在绘制物体之前,通过设置DepthBufferWriteEnable=false可以阻止物体深度信息写入深度缓冲区,在绘制完之后再把该属性设置true以恢复对深度缓冲区的操作。
正是因为这样,渲染器才忽视了天空盒的深度,从而无论飞船飞行的过程中深度如何变化,都不会与天空盒发生碰撞,天空盒只是作为背景了。
程序运行的效果如图所示。



当然,还可以对飞船继续施加其他变换,比如按转弯半径做船身倾斜的变换,以达到更逼真的视觉效果。

附天空盒类的源码:
    public class SkyBox : Microsoft.Xna.Framework.DrawableGameComponent
{
Texture2D texture;
VertexPositionNormalTexture[] box;
BasicEffect basicEffect;
//Game game;
public Matrix worldMatrix {set;get;}
public Matrix viewMatrix { set; get; }
public Matrix projectionMatrix { set; get; }

public SkyBox(Game game)
: base(game)
{
worldMatrix = Matrix.Identity;
viewMatrix = Matrix.Identity;
projectionMatrix = Matrix.Identity;
}

/// <summary>
/// Allows the game component to perform any initialization it needs to before starting
/// to run. This is where it can query for any required services and load content.
/// </summary>
public override void Initialize()
{
Vector3 topLeftFront=new Vector3(-1,1,1);
Vector3 topRightFront = new Vector3(1, 1, 1);
Vector3 bottomLeftFront = new Vector3(-1, -1, 1);
Vector3 bottomRightFront = new Vector3(1, -1, 1);
Vector3 topLeftBack = new Vector3(-1, 1, -1);
Vector3 topRightBack = new Vector3(1, 1, -1);
Vector3 bottomLeftBack = new Vector3(-1, -1, -1);
Vector3 bottomRightBack = new Vector3(1, -1, -1);

box = new VertexPositionNormalTexture[]{
new VertexPositionNormalTexture(topLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.25f)), //front
new VertexPositionNormalTexture(topRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.25f)),
new VertexPositionNormalTexture(bottomLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.5f)),
new VertexPositionNormalTexture(bottomLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.5f)),
new VertexPositionNormalTexture(topRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.25f)),
new VertexPositionNormalTexture(bottomRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.5f)),

new VertexPositionNormalTexture(bottomRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.5f)), //bottom
new VertexPositionNormalTexture(bottomRightBack,new Vector3(0,0,-1),new Vector2(0.66f,0.75f)),
new VertexPositionNormalTexture(bottomLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,0.75f)),
new VertexPositionNormalTexture(bottomLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,0.75f)),
new VertexPositionNormalTexture(bottomLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.5f)),
new VertexPositionNormalTexture(bottomRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.5f)),

new VertexPositionNormalTexture(bottomRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.5f)), //right
new VertexPositionNormalTexture(topRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.25f)),
new VertexPositionNormalTexture(topRightBack,new Vector3(0,0,-1),new Vector2(1.0f,0.25f)),
new VertexPositionNormalTexture(topRightBack,new Vector3(0,0,-1),new Vector2(1.0f,0.25f)),
new VertexPositionNormalTexture(bottomRightBack,new Vector3(0,0,-1),new Vector2(1.0f,0.5f)),
new VertexPositionNormalTexture(bottomRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.5f)),

new VertexPositionNormalTexture(bottomRightBack,new Vector3(0,0,-1),new Vector2(0.66f,0.75f)), //back
new VertexPositionNormalTexture(topRightBack,new Vector3(0,0,-1),new Vector2(0.66f,1.0f)),
new VertexPositionNormalTexture(topLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,1.0f)),
new VertexPositionNormalTexture(topLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,1.0f)),
new VertexPositionNormalTexture(bottomLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,0.75f)),
new VertexPositionNormalTexture(bottomRightBack,new Vector3(0,0,-1),new Vector2(0.66f,0.75f)),

new VertexPositionNormalTexture(bottomLeftBack,new Vector3(0,0,-1),new Vector2(0.0f,0.5f)), //left
new VertexPositionNormalTexture(topLeftBack,new Vector3(0,0,-1),new Vector2(0.0f,0.25f)),
new VertexPositionNormalTexture(topLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.25f)),
new VertexPositionNormalTexture(topLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.25f)),
new VertexPositionNormalTexture(bottomLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.5f)),
new VertexPositionNormalTexture(bottomLeftBack,new Vector3(0,0,-1),new Vector2(0.0f,0.5f)),

new VertexPositionNormalTexture(topLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.25f)), //top
new VertexPositionNormalTexture(topLeftBack,new Vector3(0,0,-1),new Vector2(0.33f,0.0f)),
new VertexPositionNormalTexture(topRightBack,new Vector3(0,0,-1),new Vector2(0.66f,0.0f)),
new VertexPositionNormalTexture(topRightBack,new Vector3(0,0,-1),new Vector2(0.66f,0.0f)),
new VertexPositionNormalTexture(topRightFront,new Vector3(0,0,-1),new Vector2(0.66f,0.25f)),
new VertexPositionNormalTexture(topLeftFront,new Vector3(0,0,-1),new Vector2(0.33f,0.25f))
};
basicEffect = new BasicEffect(Game.GraphicsDevice);

texture = Game.Content.Load<Texture2D>(@"Images\skybox");

base.Initialize();
}

/// <summary>
/// Allows the game component to update itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}

public override void Draw(GameTime gameTime) { GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp; DepthStencilState depthState = new DepthStencilState(); depthState.DepthBufferWriteEnable = false; GraphicsDevice.DepthStencilState = depthState; RasterizerState rasterizerState = new RasterizerState(); rasterizerState.CullMode = CullMode.CullClockwiseFace; GraphicsDevice.RasterizerState = rasterizerState; basicEffect.TextureEnabled = true; basicEffect.Texture = texture; basicEffect.World = worldMatrix; basicEffect.View = viewMatrix; basicEffect.Projection = projectionMatrix; foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Apply(); Game.GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, box, 0, box.Length/3); } base.Draw(gameTime); DepthStencilState depthState2 = new DepthStencilState(); depthState2.DepthBufferWriteEnable = true; GraphicsDevice.DepthStencilState = depthState2; }
}

——欢迎转载,请注明出处 http://blog.csdn.net/caowenbin ——
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: