ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
在完成了飞船和天空的两个元素以后,现在要作的是把二者结合起来,让飞船在天空中自由飞翔。 现在我们面对了如下两个问题:一是空间中的位置关系,二是飞船飞行时与天空盒的碰撞或穿越。 先来看第一个问题,在画飞船的时候,是画在了原点上,在画天空的时候,同样也是在原点上。飞船的坐标值很大,以至于在画飞船的时候使用-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以恢复对深度缓冲区的操作。 正是因为这样,渲染器才忽视了天空盒的深度,从而无论飞船飞行的过程中深度如何变化,都不会与天空盒发生碰撞,天空盒只是作为背景了。 程序运行的效果如图所示。 ![](https://box.kancloud.cn/2016-04-08_570727fbb451d.gif) 当然,还可以对飞船继续施加其他变换,比如按转弯半径做船身倾斜的变换,以达到更逼真的视觉效果。 附天空盒类的源码: ~~~ 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) ——