相机这部分比想象当中要难缠。现在也不是特别清楚,只是根据教程XNARecipes中第二章的2、3两节做一些必要的记录。
最简单的例子:相机在初始位置,绕着Up向量
相机在(0,0,0)初始坐标,朝(0,0,-1) Forward方向观察,以默认的(0,1,0) Up向量作为向上方向。在这个情况中,可以使用以下代码:
1: Vector3 cameraPosition = new Vector3(0, 0, 0);
2: Vector3 cameraTarget = new Vector3(0, 0, -1);
3: Vector3 cameraUpVector = new Vector3(0, 1, 0);
4:
5: viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector);
Vector3 direction = camaraTarget – cameraPosition = (0, 0, -1)
这一句虽然在这里很显然,但之后的很多问题就是跟这个direction有关。
如果创建一个绕Up向量旋转45度的View矩阵。假设头就是相机,只要向右旋转45度(向右看)。当计算新View矩阵时,Position向量和Up向量保持不变,但Target向量变了。可以通过将使用45度旋转“转换”默认的(0,0,-1) Target向量获取新Target向量。这意味着新的Target向量是初始Target向量的旋转版本。下面是代码:
1: Matrix cameraRotation = Matrix.CreateRotationY(MathHelper.PiOver4);
2: Vector3 cameraPosition = new Vector3(0, 0, 0);
3: Vector3 cameraUpVector = new Vector3(0, 1, 0);
4: Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
5:
6: Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
7: viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraRotatedTarget, cameraUpVector);
由于Position没变,那么Vector3 direction = camaraTarget – cameraPosition 不再是 (0, 0, -1)。
也就是你把头转过去看旁边的东西了,而不再是正前方的某个东西
第二个例子:相机在初始位置,任意旋转
现在进一步来看。你想使相机绕着任意轴旋转而不是绕着Up向量。例如,绕着(1,0,0) Right轴旋转45度,假设头就是相机,这就相当于抬起头45度看天空。和前面一个例子一样,Target向量会变化,Position向量保持不变。
但是这种情况中Up向量会发生变化(头仰起了嘛!)。在前面的例子中你将头部转向右边,Up向量并不会变化。这个例子中,将头部向上旋转,Up向量和Target向量都要发生改变。
变换Up向量和变换Target向量的方法是一样的:通过旋转矩阵变换初始Up向量获取新的Up向量。下面是代码:
1: Matrix cameraRotation = Matrix.CreateRotationX(MathHelper.PiOver4);
2: Vector3 cameraPosition = new Vector3(0, 0, 0);
3: Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
4: Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
5:
6: Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
7: Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
8: viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraRotatedTarget, cameraRotatedUpVector);
这里的“任意”旋转矩阵只是一个简单绕x轴的旋转。这个代码也可以处理任何旋转,例如下面的这个情况,它组合了三根轴上的旋转。下面的代码生成一个矩阵,这个矩阵是绕z轴–45度和y轴22.5度,x轴90度旋转的组合:
1: Matrix cameraRotation = Matrix.CreateRotationX(MathHelper.PiOver2)* Matrix.CreateRotationY(MathHelper.Pi/8.0f)* Matrix.CreateRotationZ(-MathHelper.PiOver4);
但这时要小心了,别把脖子扭了。
Vector3 direction = camaraTarget – cameraPosition 不是 (0, 0, -1)。为什么强调这个direction,虽然到现在为止还体现不出什么,因为没有涉及到移动。这也是刚开始看教程时,容易忽略的一点,以至于看到后面糊涂了。
这里要说明一点:顺时针转是正,逆时针转是负。还有从人的视觉出发,只要Up向量大致朝上,物体是不会颠倒的。这也是后面Quake式相机只有左右和上下的旋转。也就是抬头,低头,看左,看右,没有歪着脖子的。
第三个例子:相机在指定位置,任意旋转
对于这个例子,教程上的解释不大明白。特别是按照前两个例子的顺序学下来。
现在必须要有一个direction,方向。先看代码:
1: Matrix cameraRotation =Matrix.CreateRotationX(MathHelper.PiOver2)* Matrix.CreateRotationY(MathHelper.Pi/8.0f)*Matrix.CreateRotationZ(-MathHelper.PiOver4);
2: Vector3 cameraPosition = new Vector3(10, 20, 30);
3: Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
4: Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
5:
6: Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
7: Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
8:
9: Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
10: Vector3 cameraFinalUpVector = cameraPosition + cameraRotatedUpVector;
11:
12: viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraFinalUpVector);
Position是在位置(10,20,30)。代码中有一个cameraOriginalTarget,注意这个Target不是一个点,而是一个矢量方向。为什么呢?一般来讲,设置观察矩阵,需要一个视点。假如cameraOriginalTarget代表的是一个点,那么direction = (-10,-20,-31)。因为对于前两个例子,(0,0,-1)既可以表示视点,也可以表示方向(Position在初始位置)。所以当看到Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation); 的时候,也能够理解,视点的位置从(0,0,-1)变成了(x,y,z),方向也从(0,0,-1)改变到(x,y,z)。
但在这个例子中,怎么理解这两行:
1: Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
2: Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
为什么会有cameraRotatedTarget还有cameraFinalTarget。direction=cameraFinalTarget – cameraPosition = cameraRotatedTarget
而cameraRotatedTarget是由cameraOriginalTarget变换过来的。我们知道如果cameraOriginalTarget是视点的话,那么初始的方向是
direction =cameraOriginalTarget - cameraPosition =(-10,-20,-31)。 变换的时候Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation); cameraOriginalTarget参数所在的位置应该是(-10,-20,-31)才对。
从上面的反证中,可以得出结论,cameraOriginalTarget代表一个方向,不代表视点。不像前两个例子中,cameraOriginalTarget既是视点,又是方向。理解了这个,对于 cameraRotatedUpVector和cameraFinalUpVector也能理解了。
到这里,看第二节教程所带的源代码,才能看明白。特别是自己写的时候,出现问题时也能够知道是哪里出了问题。下面是自己写的代码
1: protected override void Initialize()
2: {
3: // TODO: Add your initialization logic her
4: moveSpeed = 0.02f;
5:
6: float fov = MathHelper.PiOver4;
7: float aspect = GraphicsDevice.DisplayMode.AspectRatio;
8: float nearClip = 0.5f;
9: float farClip = 100.0f;
10: projMat = Matrix.CreatePerspectiveFieldOfView(fov, aspect, nearClip, farClip);
11:
12: camPosition = new Vector3(30, 30, 30);
13: cameraRotation = Matrix.CreateRotationX(-MathHelper.Pi / 8.0f) * Matrix.CreateRotationY(-MathHelper.Pi / 8.0f);
14: UpadteViewMatrix();
15: base.Initialize();
16: }
先是Initialize(),设置好投影矩阵,相机的位置,旋转的角度
1: private void UpadteViewMatrix()
2: {
3: Vector3 originalForward = new Vector3(-1.7f, -1, -1);
4: Vector3 originalUp = new Vector3(0, 1, 0);
5:
6: Vector3 rotatedForward = Vector3.Transform(originalForward, cameraRotation);
7: Vector3 rotatedUp = Vector3.Transform(originalUp, cameraRotation);
8:
9: Vector3 finalLook = camPosition + rotatedForward;
10:
11: viewMat = Matrix.CreateLookAt(camPosition, finalLook, rotatedUp);
12: }
这里直接将originalTarget这样容易混淆的名字改成originalForward,而且是(-1.7f,-1,-1),旋转变换之后差不多就是(-1,-1,-1)的方向,也。因为相机的初始位置是(30,30,30),而我的观察方向是要看向坐标源点。
1: private void MoveCameraPosition()
2: {
3: Vector3 originalForward = new Vector3(-1.7f, -1, -1);
4: Vector3 rotatedForward = Vector3.Transform(originalForward, cameraRotation);
5: camPosition += moveSpeed * rotatedForward;
6:
7: UpadteViewMatrix();
8:
9: }
这里camPosition也是朝着源点向前移动变化。