第三章 进入3D世界
本文版权归我所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。
由于本人水平有限,难免出错,欢迎大家和我交流。
作者:clayman
Blog:http://blog.csdn.net/soilwork
clayman_joe@yahoo.com.cn
三维化三角形
为了让三角形看起来3D一些,所要做的第一步就是在3D空间中重新定义三角形顶点。修改代码:
private void InitTriangle()
{
verts = new VertexPositionColor[3];
verts[0].Position = new Vector3(0, 1f, 0.0f);
verts[0].Color = Color.Red;
verts[1].Position = new Vector3(1f, -1f, 0.0f);
verts[1].Color = Color.Blue;
verts[2].Position = new Vector3(-1f, -1f, 0.0f);
verts[2].Color = Color.Green;
decl = new VertexDeclaration(this.graphics.GraphicsDevice, VertexPositionColor.VertexElements);
}
虽然这里的代码看起来和前面差不多,但这一次是在模型空间中来定义三角形。也就是说当前的坐标系以三角形中点为坐标原点。
接下来创建前面提到的三个坐标变换矩阵。如果你对线性代数不太熟悉也没有关系,xna已经为我们提供了创建大多数常见矩阵的方法,只需要用希望的参数来调用这个方法就能获得相应矩阵。在Draw方法中添加如下代码:
Matrix worldMatrix = Matrix.CreateTranslation(new Vector3(0, 0, -10));
Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(0,0,0), new Vector3(0,0,-1), new Vector3(0,1,0));
Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView((float)Math.PI/4,
(float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height,
1.0f, 1000.0f);
Matrix是一个4x4的矩阵。上面的代码使用Matrix提供的静态方法,分别创建了世界坐标变换矩阵,观察矩阵和投影矩阵。CreateTranslation,接收一个Vector3类型的参数,这个参数就是将要把三角形放到世界坐标中的位置(0,0,-10)。
CreateLookAt()方法的第一个参数代表观察点或者说摄像机的位置,第二个参数表示观察的方向。由于三角形位于点(0,0,-10),因此从(0,0,-1)方向看过去,三角形刚好位于视野中央。最后一个参数代表一个“向上”的位置,观察物体时,你有可能倒立着看,也有可能偏着脑袋看,(0,1,0)表示最常见的观察物体的角度,正立在地面上观察。
CreatePerspectiveFieldOfView()看起来比较长,其实也很简单。他的第一个参数表示视野角度:

如图所示,假设我们从Z轴方向直视,那么上下左右所能观察到的最大角度,就是视野角度的一半(图中角fov/2)。这里用了最常见的值90度也就是Pi/4。第二个参数是视野的高宽比,接下来两个参数则是近裁减平面和远裁减平面的距离。
接下来,把三个矩阵相乘,作为最终的变换矩阵:
Matrix worldViewProj = worldMatrix * viewMatrix * projectionMatrix;
对于矩阵运算来说,相乘时的顺序很重要,矩阵乘法是不满足交换律的,因此上面的相乘顺序不能颠倒。
最后一步,把最终的变换矩阵与顶点坐标相乘,就能显示出这个3D的三角形了。修改HLSL代码:
uniform float4x4 worldViewProj;
void transform(inout float4 pos :POSITION,
inout half4 color : COLOR0)
{
pos = mul(pos,worldViewProj);
color = color;
}
这里,在Shader中添加了一个float4x4类型的变量用来表示最终的变换矩阵。Uniform表示这个这个变量将由外部程序,也就是C#程序来赋值。在transform方法中把三角形的模型坐标位置与矩阵相乘,得到最终的屏幕坐标。注意,这里是矩阵乘法,应次必须使用mul()方法计算乘机,而不能简单的用“*”,mul是HLSL中的内置标准方法之一。
当然,必须先使用Effect把把worldViewProj变量的值从C#代码中传递给Shader:
effect.Parameters["worldViewProj"].SetValue(worldViewProj);
Effect对象的Parameters成员是一个保存了shader中所有参数的列表。通过这些参数在HLSL的名称来进行索引获得相应变量,并使用SetValue方法进行赋值。
好了,现在运行程序,看看结果吧。
嗯,我知道,此时你可能相当失望:结果看起来还是和原来一样。不要急,这只是因为三角形并没有运动,因此看不出它是3D的。让我们立刻做一点点修改,让三角形旋转起来:
Matrix rotation = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds * 8f);
Matrix worldMatrix = rotation * Matrix.CreateTranslation(new Vector3(0, 0, -10));
CreateRotationY将返回一个让物体绕Y轴旋转的矩阵,我们根据时间的变化来增加旋转角度,这样三角形看起来就一直在旋转。接下来,把它与世界坐标变换矩阵相乘,这里,乘法顺序同样重要。Rotation×worldMatrix表示先在模型控件中旋转三角形,再把它放到世界坐标中的(0, 0, -10)位置,而worldMatrix×rotation则是先把物体放到世界坐标中的(0,0,-10)位置,然后在以世界坐标中的Y轴进行旋转,最后的显示结果将是不同的。当然,你可以尝试混合多种旋转,获得更疯狂一些的效果^^。
好了,再次运行程序。终于看到一个3D三角形了吧。嘿,等等,似乎还有些问题。三角形在旋转时会周期性的消失一阵,是bug吗?当然不是,这就是我马上要提到的背面裁减(back-face cull)技术。
默认情况下,当图形背对观察者时,渲染流水线会把这个图形裁减去,即不渲染它。背面裁减是很重要的技术,对减少硬件工作量,提高程序效率有重要作用。大多数情况下,显示背对观察者的图形是没有意义的。那么硬件如何知道一个图形是否背对着观察者呢,如何知道哪个面是正面呢?硬件通过图形的顶点顺序来进行判断,当然确切说应该是通过正面的法线来判断,只不过根据顶点顺序来判断计算量要小很多。回头看看所有创建三角形的代码,都是以顺时针顺序来声明并创建顶点。默认情况下,XNA认为以顺时针方向定义的面是正面。

显然,当三角形旋转到背面时,顶点顺序将颠倒过来,法线也会翻转,因此被硬件裁去。由于目前我们只有一个简单的三角形,所以并不希望硬件对他进行裁减。XNA中可以使用CullMode枚举来选择裁减哪些图形:顺时针,逆时针,或者不裁减。在Draw()方法中绘制图形前添加如下代码:
graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
再次运行程序,可以看到这次显示的很正常了。
~~~~~~~~~~~~~~~~~~~~~~~~~第三章完~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---____---b, 太久没有写教程,这一部分写的实在是太烂了,大家勉强看吧。。。
发表于 @ 2007年05月05日 17:37:00|评论(loading...)|编辑