利用控制飞船的变换矩阵,现在它已经在天空中任由我们摆布了,但是总觉得还是差了点什么,真实性不够。到底缺少了是什么呢?
通过仔细观察,我们可以看到,原来无论天空怎么旋转,在飞船船体上都没有体现光影的变化,没能随着太阳的移动产生必要的光线反射。
这块看起来是一个大问题,飞船的模型我们已经封装了Ship类,在Draw()方法中通过BasicEffect来渲染输出。天空我们也是封装了类,同样也有它自己的BasicEffect来控制渲染效果。现在要做的就是把两者结合起来,让天空的旋转能够在船身上形成光线的变化。
回顾前文中学到的光线的知识,在BasicEffect中提供了环境光、高光、有向光等光源处理,如果能为飞船施加一个有向光,并且该光的方向与天空盒中太阳光的方向相同,那么就可以先实现真的让图片中的太阳发光。而光线的变化,就转变成让这速有向光的方向始终保持与旋转的天空盒中太阳光的方向相一致就可以了。
根据这个思路,首先来为Ship类增加有向光方向这一属性。
public Vector3 lightDirection { set; get; }
然后改变Ship类中的Draw()方法,为其中的BasicEffect增加有向光参数。代码如下:
~~~
public override void Draw(GameTime gameTime)
{
model.CopyAbsoluteBoneTransformsTo(modelTransforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix;
effect.View = viewMatrix;
effect.Projection = projectionMatrix;
effect.LightingEnabled = true;
effect.AmbientLightColor = new Vector3(0.35f, 0.35f, 0.35f);
effect.DirectionalLight0.Enabled = true;
effect.DirectionalLight0.Direction = Vector3.Normalize(lightDirection);
effect.DirectionalLight0.DiffuseColor = Color.White.ToVector3();
}
mesh.Draw();
}
base.Draw(gameTime);
}
~~~
这样一来,只要为飞船对象设置光的方向,就可以在船身上做出正确的反射效果了。
接下来再来确定阳光的方向,根据天空盒的图片和顶点参数,在构建天空盒各三角形的顶点数据时,我们假设是在z轴正方向的盒外看盒子,因此,贴图中有太阳的部分被贴到了天空盒的面向观察者这一面,即前面。现在,渲染时我们观察者的位置是在天空盒的盒子里,视线指向了z轴的负方向,因此,太阳是在我们的身后,阳光方向可以近似认为指向了z轴的负方向。
这样确定了阳光的方向以后,在MainScene代码中修改LoadContent()方法,就可以为ship对象设置光线的方向为(0,0,-1)了,代码如下:
~~~
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;
ship.worldMatrix = Matrix.CreateScale(-0.02f) * Matrix.CreateRotationZ(MathHelper.ToRadians(180));
ship.lightDirection = skyBox.worldMatrix.Forward;
ship2.projectionMatrix = camera.projection;
ship2.viewMatrix = camera.view;
ship2.worldMatrix = Matrix.CreateScale(-0.02f) * Matrix.CreateRotationZ(MathHelper.ToRadians(180))*Matrix.CreateTranslation(new Vector3(50, 0, 0));
ship2.lightDirection = skyBox.worldMatrix.Forward;
}
~~~
执行程序,会看到飞船船身的光线已经符合天空的环境了。
如果让光线也能变,那还是要在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));
ship.lightDirection = skyBox.worldMatrix.Forward;
ship2.lightDirection = skyBox.worldMatrix.Forward;
}
}
base.Update(gameTime);
}
~~~
从这段代码中可以看出,每当天空盒旋转的时候,需要将天空盒新的视点坐标的向方向量作为有向光的方向,从而让天空盒的旋转与光线的旋转同步,飞船也就会按照对应的光线方向渲染出阳光照射的效果了。
运行结果如图所示。
![](https://box.kancloud.cn/2016-04-08_570727fbccce7.gif)
到此为止,我们已经能初步运用XNA提供的3D功能开发应用了,通过运用矩阵可以进行复杂的变换,通过运用向量可以控制方向,这些都是计算机图形学理论的基础。其实,前面关于物体的变换也同样可以应用于摄像机,对摄像机施加平移、旋转的变换就可以形成电影中最基本的镜头推拉摇移效果,从而也让我们的思想在天空中飞翔。
——欢迎转载,请注明出处 [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开发之后记(附源码)