多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
飞船是离不了天空的,虽然上一节我们已经建好了飞船并试飞成功,但还是没有给飞船更大的背景翱翔。在一个场景中,仅有主体还是不够的,还需要有周围环境的渲染。如果我们能为飞船增加蓝天白云的高远,峰峦叠嶂的苍翠,那一定才是更贴近现实的。 在这个场景中,我们需要一个天空环境作为背景,不考虑地形地貌的特征,因此,典型的做法是实现天空盒(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轴负方向上的后面。如下图所示。 ![](https://box.kancloud.cn/2016-04-08_570727fb6e816.gif) 然后,利用这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)), //.......... ~~~ 在上述代码中,每个顶点的贴图坐标是参照如下图(图片来源于网络,分辨率低,仅用于示意)所示的贴图文件确定的。 ![](https://box.kancloud.cn/2016-04-08_570727fb822dc.gif) 这个贴图如果折叠起来正好构成一个立方体,所以各个面上三角形的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); } ~~~ 运行程序,点击屏幕,效果如下图所示(由于贴图文件来源于网络,分辨率低,所以有些模糊,仅用来示意吧)。真是一个广袤的世界! ![](https://box.kancloud.cn/2016-04-08_570727fb995ae.gif) ——欢迎转载,请注明出处 [http://blog.csdn.net/caowenbin](http://blog.csdn.net/caowenbin) ——