Clayman的专栏

It's all about XNA & GPU Programming

原创 XNA Kick Start (六) 收藏

新一篇: flickr也被封杀了,下一个会是谁........???

 第三章 进入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类型的参数,这个参数就是将要把三角形放到世界坐标中的位置(00-10)。
    CreateLookAt()方法的第一个参数代表观察点或者说摄像机的位置,第二个参数表示观察的方向。由于三角形位于点(00-10),因此从(00-1)方向看过去,三角形刚好位于视野中央。最后一个参数代表一个“向上”的位置,观察物体时,你有可能倒立着看,也有可能偏着脑袋看,(010)表示最常见的观察物体的角度,正立在地面上观察。
    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()方法计算乘机,而不能简单的用“*”,mulHLSL中的内置标准方法之一。
    当然,必须先使用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则是先把物体放到世界坐标中的(00,-10)位置,然后在以世界坐标中的Y轴进行旋转,最后的显示结果将是不同的。当然,你可以尝试混合多种旋转,获得更疯狂一些的效果^^
    好了,再次运行程序。终于看到一个3D三角形了吧。嘿,等等,似乎还有些问题。三角形在旋转时会周期性的消失一阵,是bug吗?当然不是,这就是我马上要提到的背面裁减(backface cull技术。
    默认情况下,当图形背对观察者时,渲染流水线会把这个图形裁减去,即不渲染它。背面裁减是很重要的技术,对减少硬件工作量,提高程序效率有重要作用。大多数情况下,显示背对观察者的图形是没有意义的。那么硬件如何知道一个图形是否背对着观察者呢,如何知道哪个面是正面呢?硬件通过图形的顶点顺序来进行判断,当然确切说应该是通过正面的法线来判断,只不过根据顶点顺序来判断计算量要小很多。回头看看所有创建三角形的代码,都是以顺时针顺序来声明并创建顶点。默认情况下,XNA认为以顺时针方向定义的面是正面。

显然,当三角形旋转到背面时,顶点顺序将颠倒过来,法线也会翻转,因此被硬件裁去。由于目前我们只有一个简单的三角形,所以并不希望硬件对他进行裁减。XNA中可以使用CullMode枚举来选择裁减哪些图形:顺时针,逆时针,或者不裁减。在Draw()方法中绘制图形前添加如下代码:
   
     graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
 
再次运行程序,可以看到这次显示的很正常了。

 ~~~~~~~~~~~~~~~~~~~~~~~~~第三章完~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---____---b, 太久没有写教程,这一部分写的实在是太烂了,大家勉强看吧。。。

 

发表于 @ 2007年05月05日 17:37:00|评论(loading...)|编辑

旧一篇: The ancient sprite has gone

评论

#eXion 发表于2007-05-05 21:15:27  IP: 221.204.194.*
噢耶 刚准备把前五讲刻盘保存 正好6就出来了
不过我刚刚看到第3讲……
#RockShiu 发表于2007-05-08 08:42:15  IP: 58.62.42.*
这教程太好了
#kkk 发表于2007-05-21 11:48:02  IP: 124.78.251.*
為何現在很多方面(包括站長)都力推MDX/XNA,不用C++的Direct3D不明白這比用後者好在何處?
#狂人 发表于2007-06-03 10:04:05  IP: 222.130.199.*
把MDX看作是普通DX的高级封装好了,在安全对象和强类型的支持下,写游戏轻松多了
#bearhunter 发表于2007-06-03 19:49:12  IP: 125.115.10.*
.net平台无关啊, xna可以运行在 xbox上
#mz 发表于2007-07-24 17:26:43  IP: 210.75.252.*
下载的代码用什么编译?
我用,net2005,.net2003编译都有错,
无法加载.
#soilwork 发表于2007-07-24 19:46:34  IP: 58.60.76.*
to mz:
用Visual C# 2005 Express Edition
#konit 发表于2007-09-22 11:10:51  IP: 202.153.49.*
很不錯的教程啊!由淺入深。作者沒時間寫了么?真遺憾....
#chucuole 发表于2008-04-25 11:57:42  IP: 219.137.38.*
decl = new VertexDeclaration(graphics, VertexPositionColor.VertexElements);
这行出错,
The best overloaded method match for 'Microsoft.Xna.Framework.Graphics.VertexDeclaration.VertexDeclaration(Microsoft.Xna.Framework.Graphics.GraphicsDevice, Microsoft.Xna.Framework.Graphics.VertexElement[])' has some invalid arguments F:\Study\XNA\HelloWorld\MainGame.cs
#chucuole 发表于2008-04-25 12:04:14  IP: 219.137.38.*
我的代码运行起来没错了,但是就一个蓝色背景,什么都没有。怎么回事?
我用的 Windows Vista + C# 2005 Express + XNA Game Studio2.0
#soilwork 发表于2008-04-27 01:23:53  IP: 58.60.77.*
这些代码是用xna 1.0写的,原来但我都测试过,应该不会有什么问题。如果你用的2.0请参考文档做相应修改。我最近硬盘炸了,什么也干不了...
#fairyjing 发表于2008-05-13 12:01:19  IP: 210.83.227.*
...
发表评论  


登录
Csdn Blog version 3.1a
Copyright © clayman