第三章 进入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, 太久没有写教程,这一部分写的实在是太烂了,大家勉强看吧。。。