zoukankan      html  css  js  c++  java
  • 在3D世界中创建不同的相机模式——指定相机的目标

    2.2 指定相机的目标

    问题

    当定义View矩阵时,你需要指定Target向量作为参数,这个向量设置了相机的旋转。

    解决方案

    当选转相机时,相机的位置会保持不变。这意味着旋转取决于相机的Target点和Up向量的变化。你可以通过使用 (0,0,-1)Forward向量并将旋转施加到这个向量上获取Target向量,这会产生一个新的Target点。你可以使用相同的方法获取Up向量。

    工作原理

    如教程2-1所示,一个相机需要Position, Target和Up向量才能唯一确定。如果你想让相机绕着某一点旋转,Position向量保持不变,但Target和Up向量都要发生改变。

    给定绕着三根轴的旋转角度,一个方法是处理Target位置,但这会带来复杂的运算,还有一个更加清晰和快速的方法。

    最简单的例子:相机在初始位置,绕着Up向量

    旋转让我们先来看一下相机在(0,0,0)初始坐标的情况。它朝(0,0,-1) Forward方向观察,以默认的(0,1,0) Up向量作为向上方向。在这个情况中,你可以使用以下代码:

    Vector3 cameraPosition = new Vector3(0, 0, 0); 
    Vector3 cameraTarget = new Vector3(0, 0, -1); 
    Vector3 cameraUpVector = new Vector3(0, 1, 0);
    
    viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector);

    例如你想创建一个绕Up向量旋转45度的View矩阵。如果你的头就是相机,这会让头部向右旋转45度。当计算新View矩阵时,Position向量和Up向量会保持不变,但你需要获取新的Target向量。你可以通过将使用45度旋转“转换”默认的(0,0,-1) Target向量获取新Target向量。这意味着你获取的这个向量是初始Target向量的旋转版本。下面是代码:

    Matrix cameraRotation = Matrix.CreateRotationY(MathHelper.PiOver4);
    Vector3 cameraPosition = new Vector3(0, 0, 0); 
    Vector3 cameraUpVector = new Vector3(0, 1, 0); 
    Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
    
    Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation); 
    viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraRotatedTarget, cameraUpVector);

    注意:矩阵是表示某种变换的有力工具。一个矩阵可以表示旋转,平移,缩放或这些变换的组合。你可以在教程4-2中获取更多的例子。

    第一行代码创建了一个表示绕Up轴旋转45度的矩阵,对应PI/4弧度。下面的代码通过这个旋转矩阵转换初始(0,0,-1) Target向量,并将旋转过的Target向量保存在cameraRotatedTarget变量中,这个变量用来创建一个新的View矩阵。

    注意:这个变换并不神秘;它是向量和矩阵的乘积,只是简单地进行16次乘法和12次加法。

    第二个例子:相机在初始位置,任意旋转

    现在来看一个有点复杂的例子。你想使相机绕着任意轴旋转而不是绕着Up向量。例如,绕着(1,0,0) Right轴旋转45度,如果你的头是相机,这会导致斜向上45度观察。

    因为你仅旋转相机,Position向量保持不变。和前面一个例子一样,Target向量会变化,因为相机需要观察一个不同的位置。

    但是这种情况中Up向量也会发生变化。在前面的例子中你将头部转向右边,Up向量并不会变化。这个例子中,将头部向上旋转,Up向量和Target向量都要发生改变。

    变换Up向量和变换Forward向量的方法是一样的:你将旋转存储在一个矩阵中并定义初始Up向量。然后,通过这个旋转矩阵变换初始向量获取新的Up向量。下面是代码:

    Matrix cameraRotation = Matrix.CreateRotationX(MathHelper.PiOver4);
    Vector3 cameraPosition = new Vector3(0, 0, 0);
    Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
    Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
    
    Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation); 
    Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation); 
    viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraRotatedTarget, cameraRotatedUpVector);

    这里的“任意”旋转矩阵只是一个简单绕x轴的旋转。这个代码也可以处理任何旋转,例如下面的这个情况,它组合了三根轴上的旋转。下面的代码生成一个矩阵,这个矩阵是绕z轴–45度和y轴22.5度,x轴90度旋转的组合:

    Matrix cameraRotation = Matrix.CreateRotationX(MathHelper.PiOver2)* Matrix.CreateRotationY(MathHelper.Pi/8.0f)* Matrix.CreateRotationZ(-MathHelper.PiOver4);

    当获取新的Forward向量和Up向量时,相机的位置保持不变。

    第三个例子:相机在指定位置,任意旋转

    在大多数情况中,你想设置任意旋转并指定相机的位置。例如相机在位置(10,20,30),不旋转。非常简单,相机的Position向量为(10,20,30)。因为没有旋转,所以相机观察(0,0,-1) Forward方向。

    注意:记住需要指定Target而不是Target方向!将(0,0,-1)作为Target向量是错误的,因为这会让相机观察点(0,0,-1)。例如你将相机移动到点(-10,20,30),如果仍然指定(0,0,-1)作为Target向量,相机仍会观察点(0,0,-1),这个相机观察的方向就会发生变化!

    要让位于(10,20,30)相机朝向(0,0,-1)方向,你需要指定(10,20,29)作为Target向量。你可以通过求相机位置和目标方向的和获取这个向量:

    Vector3 cameraPosition = new Vector3(10, 20, 30); 
    Vector3 cameraOriginalTarget = new Vector3(0, 0, -1); 
    cameraTargetPoint = cameraPosition + cameraOriginalTarget;

    现在,可以组合前面所学到的东西了。你将定义一个位于(10,20,30)的相机并可以任意旋转。Position向量保持(10,20,30)不变。对Target向量,首先定义为(0,0,-1)方向。要在旋转后获取Forward方向,你需要使用旋转矩阵变换它。最后,要获取Target向量让位于(10,20,30)的相机看向旋转方向,你需要在这个旋转方向上加上(10,20,30)。Up向量用同样的方法获取,下面是最后的代码:

    Matrix cameraRotation =Matrix.CreateRotationX(MathHelper.PiOver2)*  Matrix.CreateRotationY(MathHelper.Pi/8.0f)*Matrix.CreateRotationZ(-MathHelper.PiOver4); 
    Vector3 cameraPosition = new Vector3(10, 20, 30); 
    Vector3 cameraOriginalTarget = new Vector3(0, 0, -1); 
    Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
    
    Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
    Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
    
    Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
    Vector3 cameraFinalUpVector = cameraPosition + cameraRotatedUpVector;
    
    viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraFinalUpVector);
    将旋转过的相机前后/左右移动

    现在已经实现了指定位置的相机并朝向正确的方向,新的挑战是前后移动相机。如果你想让相机向前移动,简单地将(0,0,-1) Forward向量添加到Position向量上不会成功,因为首先需要获取对应旋转相机的Forward向量。你已经在本教程中的第一个例子中使用过旋转矩阵变换过(0,0,-1) Forward向量了,有了这个变换过的Forward向量,才可以将它加到Position向量中:

    float moveSpeed = 0.5f;
    Vector3 cameraOriginalForward = new Vector3(0,0,-1);
    Vector3 cameraRotatedForward = Vector3.Transform(cameraOriginalForward, cameraRotation);
    cameraPosition += moveSpeed * cameraRotatedForward;

    改变moveSpeed的值可以增加/减少相机移动的速度,因为这个值会乘以旋转过的 Forward方向。

    同样的方法也可以让相机左右移动。处理的是(1,0,0) Right向量而不是(0,0,-1) Forward向量,仍然需要首先进行变换以获取对应当前相机旋转的Right向量。

    float moveSpeed = 0.5f;
    Vector3 cameraOriginalRight = new Vector3(1, 0, 0);
    Vector3 cameraRotatedRight = Vector3.Transform(cameraOriginalRight, cameraRotation); 
    cameraPosition += moveSpeed * cameraRotatedRight;
    代码

    这个方法中相机只需保存当前位置和旋转,位置和旋转任意一个发生变化就要更新View 矩阵。这些变化通常来自于用户输入,可以在教程2-3和2-4中看到具体实现。每个矩阵都需要进行初始化,你首先需将cameraRotation矩阵设置为单位矩阵。

    protected override void Initialize()
    {
        float viewAngle = MathHelper.PiOver4;
        float aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio; 
        float nearPlane = 0.5f;
        float farPlane = 100.0f;
        projectionMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearPlane, farPlane);
    
        cameraPosition = new Vector3(-5, 7, 14);
        cameraRotation = Matrix.CreateRotationX(-MathHelper.Pi/8.0f)* Matrix.CreateRotationY(-MathHelper.Pi/8.0f);
    
        UpdateViewMatrix();
        base.Initialize();
    }
    
    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
    
        MoveCameraForward();
    
        base.Update(gameTime); 
    }
    
    private void MoveCameraForward()
    {
        float moveSpeed = 0.05f;
        Vector3 cameraOriginalForward = new Vector3(0, 0, -1); 
        Vector3 cameraRotatedForward = Vector3.Transform(cameraOriginalForward, cameraRotation); 
        cameraPosition += moveSpeed * cameraRotatedForward; 
        UpdateViewMatrix();
    }
     
    private void UpdateViewMatrix()
    {
        Vector3 cameraOriginalTarget = new Vector3(0, 0, -1); 
        Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
    
        Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
        Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
    
        Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
        viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector); 
    }
    
    protected override void Draw(GameTime gameTime) 
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
        //render coordcross using specified View and Projection matrices 
        cCross.Draw(viewMatrix, projectionMatrix);
    
        base.Draw(gameTime); 
    }
    扩展阅读
    矩阵乘法的顺序

    可参见教程4-2。

    改变坐标系统

    前面使用的Forward (0,0,-1)和Up (0,1,0)方法是“官方的” XNA向量,也可以使用Vector3 . Forward和Vector3 . Up快捷方式获取。但是这只是一个约定,你可以定义一个完全不同的坐标系统。例如也可以使用(0,0,1)作为Up方向,(0,1,0)作为Forward方向,(1,0,0)作为Right方向,这完全取决于你的需要。

    但是这三个向量应该符合一个规则。在XNA中x,y和z轴是右手坐标系。意思是一旦你知道了任意两个坐标轴,就可以知道第三个坐标轴的方向。展开你右手的大拇指和食指,然后弯曲中指使它垂直于食指和大拇指,将这三个手指看成坐标轴,如果是右手坐标系,那么x轴就对应大拇指,y轴对应食指,z轴对应中指。

    本教程第一段的坐标系统,对应大拇指指向右方,中指指向上方 。要代表“官方”XNA坐标系统,将你的大拇指指向右方(正x轴 = 右方),食指指向上方 (y轴=上方)。现在你可以看到中指指向后方(正z轴 =后方), 这就是为什么官方Forward (0,0,-1)向量中有个负号的原因!

  • 相关阅读:
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    64位WIN7系统 下 搭建Android开发环境
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120175.html
Copyright © 2011-2022 走看看