在Windows Phone中進行3D開發之十二飛行

來源:互聯網
上載者:User

        在完成了飛船和天空的兩個元素以後,現在要作的是把二者結合起來,讓飛船在天空中自由飛翔。

        現在我們面對了如下兩個問題:一是空間中的位置關係,二是飛船飛行時與天空盒的碰撞或穿越。

        先來看第一個問題,在畫飛船的時候,是畫在了原點上,在畫天空的時候,同樣也是在原點上。飛船的座標值很大,以至於在畫飛船的時候使用-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 ——

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.