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

        在完成了飞船和天空的两个元素以后,现在要作的是把二者结合起来,让飞船在天空中自由飞翔。

        现在我们面对了如下两个问题:一是空间中的位置关系,二是飞船飞行时与天空盒的碰撞或穿越。

        先来看第一个问题,在画飞船的时候,是画在了原点上,在画天空的时候,同样也是在原点上。飞船的坐标值很大,以至于在画飞船的时候使用-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 ——


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文斌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值