飛船是離不了天空的,雖然上一節我們已經建好了飛船並試飛成功,但還是沒有給飛船更大的背景翱翔。在一個情境中,僅有主體還是不夠的,還需要有周圍環境的渲染。如果我們能為飛船增加藍天白雲的高遠,峰巒疊嶂的蒼翠,那一定才是更貼近現實的。
在這個情境中,我們需要一個天空環境作為背景,不考慮地形地貌的特徵,因此,典型的做法是實現天空盒(sky-box)。天空盒就是做一個大的立方體,為立方體內部的六個面貼上連續的天空和大地的紋理,將飛船置入這個立方體內部,這樣從飛船的角度看出去,周圍就被天空和大地所包圍。
下面,我們就來構造一個這樣的天空盒出來。
還是先定義一個SkyBox類,繼承自DrawableGameCompenent。
既然要使用貼圖和光線,那麼就為類添加一個成員變數box,用於定義立方體的各個頂點:
VertexPositionNormalTexture[] box;
在Initialize()中以原點為中心,邊長為2個座標單位構造box資料,雖然有12個三角形,但這些三角形都會用到立方體的8個頂點,因此,可以先定義好頂點的座標。
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);
在這段代碼中,從變數名字上可以看出,這8個座標分別是立方體z軸正方向和z軸負方向兩個面的頂點。以Front為尾碼的變數代表z軸正方向上的前面,以Back為尾碼的變數代表z軸負方向上的後面。如所示。
然後,利用這8個座標構造立方體各個面的三角形,每個面由兩個三角形拼接而成。
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)), //..........
在上述代碼中,每個頂點的貼圖座標是參照如(圖片來源於網路,解析度低,僅用於示意)所示的貼圖檔案確定的。
這個貼圖如果摺疊起來正好構成一個立方體,所以各個面上三角形的UV座標照此圖進行確定即可。
座標準備好以後,在Draw()方法中進行繪製。
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);
在這段代碼中,texture是Texture2D的執行個體變數,在Initialize()方法中從Context載入了貼圖檔案。
basicEffect是BasicEffect的執行個體變數,用於設定渲染效果選項。
worldMatrix、viewMatrix、projectionMatrix是三個變換矩陣,像上一節Ship類的封裝一樣用於確定變換座標,定義如下:
public Matrix worldMatrix {set;get;} public Matrix viewMatrix { set; get; } public Matrix projectionMatrix { set; get; }
到這裡,SkyBox類就封裝好了,我們在MainScene中對其進行一下測試,看看是否符合我們的預期。
在MainScene中加入SkyBox對象skyBox,並將其加入到Components中。
skyBox = new SkyBox(this); Components.Add(skyBox);
建立一個位於原點並指向z軸負方向的攝像機,並將skyBox的視圖矩陣和投影矩陣設定為攝像機的參數:
camera = new Camera(this, new Vector3(100,100,100), new Vector3(0, 0, -1), Vector3.Up, MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.1f, 500); skyBox.projectionMatrix = camera.projection; skyBox.viewMatrix = Matrix.CreateWorld(new Vector3(0,0,0),new Vector3(0,0,-1),Vector3.Up);
最後,為了能看到效果,給天空盒一個旋轉變換,在Update()中加入如下代碼:
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); TouchPanel.EnabledGestures = GestureType.Tap; if (TouchPanel.IsGestureAvailable) { GestureSample gestureSample = TouchPanel.ReadGesture(); if (gestureSample.GestureType == GestureType.Tap) { skyBox.worldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(5)); ; } } base.Update(gameTime); }
運行程式,點擊螢幕,效果如所示(由於貼圖檔案來源於網路,解析度低,所以有些模糊,僅用來示意吧)。真是一個廣袤的世界!
——歡迎轉載,請註明出處 http://blog.csdn.net/caowenbin ——