在完成了飛船和天空的兩個元素以后,現在要作的是把二者結合起來,讓飛船在天空中自由飛翔。
現在我們面對了如下兩個問題:一是空間中的位置關系,二是飛船飛行時與天空盒的碰撞或穿越。
先來看第一個問題,在畫飛船的時候,是畫在了原點上,在畫天空的時候,同樣也是在原點上。飛船的坐標值很大,以至于在畫飛船的時候使用-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](http://blog.csdn.net/caowenbin) ——
- 前言
- Windows Phone 7開發環境初體驗
- Windows Phone 7 3D開發中使用紋理貼圖
- 在Windows Phone中進行3D開發之一坐標系
- 在Windows Phone中進行3D開發之二攝像機
- 在Windows Phone中進行3D開發之三空間
- 在Windows Phone中進行3D開發之四三角形
- 在Windows Phone中進行3D開發之五平移縮放
- 在Windows Phone中進行3D開發之六旋轉
- 在Windows Phone中進行3D開發之七紋理
- 在Windows Phone中進行3D開發之八光照
- 在Windows Phone中進行3D開發之九模型
- 在Windows Phone中進行3D開發之十組件
- 在Windows Phone中進行3D開發之十一天空
- 在Windows Phone中進行3D開發之十二飛行
- 在Windows Phone中進行3D開發之十三陽光
- 在Windows Phone中進行3D開發之后記(附源碼)