[翻译]XNA 3.0 Game Programming Recipes之one
PS:自己翻译的,转载请著明出处
2-1 调好摄像机的:地位,目标, 视景体
问题
在您绘制你的3D世界到屏幕前,你需要设置你的摄象机。通过设置视角和对象矩阵。在绘制之前,2个矩阵都是需要的以至于图形卡能正确的将你的3D世界转化成2D屏幕中。
解决方案
在你的3D世界里设置你的摄象机归根揭底是2个特别的矩阵。
你也可以保存摄象机的位置和方向用一个单独的矩阵。这个矩阵叫做View matrix.为了建立View matrix,XNA需要知道摄象机的位置,目标和摄象机的上部方向。
你同样可以保存view frustum,这是3D世界可以被摄象机能真实的看见。这个矩阵称为Projection matrix.
它是如何工作的
View matrix存有摄象机的位置和摄象机面向的方向。你可以建立它门用一个简单的Matrix.CreateLokAt方法:
这个方法有3个向量:摄象机的位置,目标,向上的方向。位置向量有应该很容易察觉因为它声明在3D世界里你的摄象机的位置。在3D世界里你还要定义你的摄象机朝向的方向。这个定义了视角的方向。那么你需要向上的方向做什么呢?
思考接下来的例子:你的头(好,应该是你的眼睛)是一个摄象机。你想定义假设用你的头一个摄象机用相同的位置和方向第一个向量很容易找到:在3D场景中你的头的位置就是摄象机的位置向量。同样,接下来的目标向量也不是很难定义。假设你正在看X在图1。这种情况下,这本书中的X的位置可能就是你摄象机的目标向量。越远越好,但是在现在,你需要保持的头在原来的位置上和目标的一定距离观察X。
仅仅只有位置和目标向量被定义,你可以旋转你的头围绕着两眼之间的点。例如,在固定点的颠倒混乱。同时你又准备做这个,你头的位置和目标的一直保持相同的位置。你看到的结果图象变的完全不同了是因为任何事物都旋转了。这就是为什么要定义摄象机的向上方向的位置。
一旦你知道你的摄象机的位置,哪里是你用它要观察的。哪个方面被认为是摄象机的上部方向,摄象机被认为是"独特的自定义者"
View matrix,这3个独特的载体,可以用Matrix.CreateLookAt方法来构造:
2 Vector3 camPosition=new Vector3(10,0,0);
3 Vector3 camTarget=new Vector3(0,0,0);
4 Vector3 camUpVector=new Vector3(0,1,0);
5 viewMatrix=Matrix.CreateLookAt(camPosition,camTarget,camUpVector);
注意:尽管现在的位置和目标向量相机指向真正点在三维空间,向上矢量显示向上的方向。例如,说是在相机的位置( 300,0,0 )和面向点( 200,0,0 ) 。如果你想表明相机的更新向量就是指着向上,你通过( 0,1,0 )向上的向量表示,而不是点在相机在三维空间上面的,这将是( 300,1,0 )点也在这个例子中。
注意:XNA提供快捷最常用的向量,如Vector3.Up点(0,1,0),Vector3.Forward点(0,0,-1)和Vector3.Right点(1,0,0).让你更熟悉三维矢量, 所有媒介,您可以找到在本章书面的第一节看到写的很全。
XNA要求的另外一个矩阵是Projection matrix.你可以这样认为矩阵(是一个很奇妙的东西)是从3D空间转化为2Dwindow图片上所有的点但是我宁愿你把它看作矩阵持有摄像机镜头的相关信息。
图片现在3D场景的左半部分,图片在摄象机的视角内。这正如你所看到的,形状的金字塔。在右边,你可以找到二维截面这个金字塔。
减去金字塔顶端左侧的图像被称为视景体。所有XNA结构中的对象都被绘制,只有在其体积中的物体才会被实实在在绘制在场景中
XNA可以为你创建一个景体,它保存在Projection matrix中。你可以用Matrix.CreatePerpectiveFieldOfView方法创建一个:
第一个参数你需要传递给Matrix.CreatePerspectiveFieldOfView方法,这个参数是一个视角的角度。这是金字塔开度角的一半在2-2右边的图。如果您尝试找出视角可以用自己的眼睛是你的手放在你的眼睛旁边,你可以在90度范围内找到一个值。PI是180度,90度是PI的一半。你需要指定视角角度的一半,你只能用PI/4作为第一个参数。
注意:通常情况下,当绘制三维场景到屏幕上你将要使用的视角相对应的视角人类视线。然而,在某些情况下,您可能要指定其他浏览角度。这是通常情况下,您渲染现场成纹理,例如,光源。如果是光源,更大的视角可能意味着更大的范围被点亮。例如,见图3-13
下一个论点您需要指定没有任何关系“源” ,这是梯形体场景,用"目标单元格"替换了。2D的window的纵横比转化为3D场景时将被绘制。理想状态下,这将响应后台buffer的比率,你可以用这个方法实现:
当使用一个宽和高都是300正方形窗口时,例如,用1来均分它。虽然,用800*600的全屏绘制看起来很大,绘制到宽屏幕和HDTV上看起来会更大,图象最后被水平拉伸。
图2-2的视景体是更值得讨论的。想象一下一个物体非常近的靠近摄象机。这个物体可能将阻挡正个视域,改变是整个窗口将包括一个纯色。为了避免这种情况下,XNA允许您定义一个平面的金字塔式,接近顶端。在金字塔顶尖和这个平面之间的物体将不会被绘制。这被称为"剪除"。这个平面是被称为近裁剪平面,您可以指定您之间的距离摄像头和其附近的裁剪平面的第三个参数的用CreatePerspectiveFieldOfView方法。
注意:这个词是用来裁剪表明,有什么不需要被绘制这样来改进的程序的帧速率
物体放置的离摄象机很远的话,这个物体看起来会很细小,但是这还是需要图形卡使用同样的进程去绘制它。如果一个物体离第2平面太远也将被剪除。这第二平面被称为远景裁剪平面,你的视景体的最终边界 ,大量的三维场景将绘制在您的二维窗口。 您可以指定摄像机与远景裁剪平面之间的距离,使用Matrix.CreatePerspectiveFieldOfView 方法
注意:即使在同一个非常简单的三维场景,不要设置远裁剪平面太远。 设置裁剪平面的距离远一些像疯了的值100000将导致严重的视觉效果。 卡带有16位深度缓冲(见“ z缓冲区(或深度缓冲) ”一节)将2 ^ 16 = 65535尽可能深入的值。即使深度分布是线性的,如果两个物体竞争某像素和两个物体之间的距离小于100k/65 , 535 = 1.53单位,您的图形卡将无法确定哪些对象是接近相机。
在现实中,这是多少,更糟糕,因为规模是二次方,导致在过去3/4您的整个场景似乎有相同的距离到相机。因此,您之间的距离远裁剪平面应保持低于几百。这一距离甚至应该较小,如果深度缓冲少于16位分辨率。 这一问题的一个典型的症状将是您所有的物体似乎锯齿状边缘。
使用方法
如果您要查看矩阵根据你的申请正在进行更新或者在更新阶段,因为摄象机的位置和朝向都依靠于用户的输入。Projection matrix需要在窗口的纵横比变换时改变,例如,将窗口该成全屏幕模式。
一旦你要计算View或者Projection matrices时,你需要传递它们到一个你经常绘制的确定对象。在接下来的代码里你可以看到Draw方法。这使得把您的所有三维现场的顶点着色在您的图形卡上,在您的窗口显示其相应的像素。
代码
下面的例子将展示怎么样去创建一个View矩阵和一个Projection矩阵。也就是说你有一个本地的对象在世界坐标里的点是(0,0,0)你想要摄象机的位置是X坐标轴正方向上的10单位,并且以Y轴正方向作为向上的矢量。此外,你想用800*600来呈现你的3D场景并希望 所有三角形接近相机大于0.5f和小于于100.0f否则将剪裁掉。 这是您的代码将如下所示:
课外阅读
前两个矩阵都是XNA需要正确在你的屏幕上提供您的三维场景的二维窗口 。从三维到二维涉及某些挑战,这是所有由XNA所考虑的。但是,如果您希望能够创建和调试中到大型的3D 应用,清楚地了解正在引擎盖下发生的事情是必要的。
然而,如何对象B先画,在帧缓冲响应象素先把对象B的颜色标记了。下一刻,对象A被绘画,现在,图形卡设备不管象素是否需要被对象A的颜色覆盖。
为了解决这个问题,在第二个图形卡存储图像,以同样的大小的窗口。目前,一种颜色被派往像素的帧缓冲,
摄象机和对象的距离被存储在在第二副图象中,距离值是0到1之间,0是摄象机到近剪裁面板而1是摄象机到远剪裁面板。
由于这一原因,第2副图片被称为深度缓冲区或Z轴缓冲。
所以,它是怎么解决着个问题的呢?现在对象B被绘制了,Z轴缓冲被选中。因为对象B是第一个被绘制的,Z轴缓冲将清空。
结果,所有帧缓冲相应的象素获得对象B的颜色,同一像素中的Z轴缓冲获得值根据摄像机与对象B之间的距离。
下一刻,对象A被绘制。每一个象素可能被对象A所占用,Z轴缓冲先被锁定。Z轴缓冲区将已经包含了一种值的像素与对象B重叠。保存在Z轴缓冲区的距离大于摄象机和对象A之间的
距离,所以图形卡知道用对象A的象素覆盖它。