OGL(教程13)——摄像机空间

原文地址:http://ogldev.atspace.co.uk/www/tutorial13/tutorial13.html

背景知识:
在前面的几个章节中,我么能看到两种类型的变换。第一种类型的变换是改变位置(平移变换),方向变换(旋转变换),大小(缩放)变换。这些变换允许我们把物体放在3D世界中的任一个位置。第二种类型的变换就是透视变换,这个是3D世界中的点透视到2D空间中去。一旦坐标是2D世界中了,就很容易把他们映射到屏幕空间坐标中去。

到目前所缺失谜题就是摄像机的位置。在之前的章节中,我们假设相机就是简单的放在3D空间的原点位置。事实上,我们想自由的控制摄像机的位置,我们想把它放在3D空间的任意位置,然后把它看到的顶点映射到2D空间。这个会直观的展示摄像机和屏幕上的物体。

下图我们可以看到摄像机的背面。这里有一个虚拟的2D平面在它之前,然后这个球被映射到了平面上。由于我们是从摄像机看世界,所以看到的世界和摄像机的张角有关系。超出边界的会被裁剪掉。把看到的四边形映射到屏幕是我们的目标。

在这里插入图片描述

理论上来说,把摄像机之前的任何物体,通过变换都可以从3D映射到2D。但是,数学变换要很复杂,如前所见。如果把摄像机放在3D世界的原点,并且它是看着z轴方向,这个变换就容易的多了。比如,如果物体放在(0,0,5),摄像机在(0,0,1),朝向z轴方向。如果我们移动摄像机和物体,朝着原点方向移动一个单位,相对距离和朝向保持不变,只是摄像机放在了原点。如果用同样的方式移动所有的物体,我们可以按照之前的方法渲染场景。

上面的例子很简单,因为摄像机已经朝向z轴,并且是放在原点位置。但是如果摄像机的是看着其他方式呢?比如下图:
在这里插入图片描述

摄像机有朝着z,然后顺时针旋转了45度。如你看到的,摄像机定义了自己的坐标系统,和世界坐标系一样。所以事实上有两个独立的坐标系统。其中一个叫做世界坐标系,另外一个就是视口坐标系。

绿色的球放在(0,y,z)世界坐标内。而在摄像机空间此球则处在左边位置,它有负的x和正的z。我们需要找到摄像机空间中这个球的位置。这样我们就可以忘记世界空间,只使用相机空间。在相机空间,相机处在原点,朝着z轴看。哪些以摄像机定义的相对位置可以使用之前的公式进行渲染。

如果说摄像机顺时针旋转了45度,也就是说球逆时针旋转了45度。物体的移动和摄像机的移动是相反的。所以通常,我们需要增加两个新的变换,然后把他们插入到管线中。我们需要移动物体,保持它距离摄像机的距离和摄像机距离原点的距离相同。我们还需要移动物体,和摄像机的移动相反的方向移动。

移动摄像机很简单。如果摄像机处在(x,y,z),那么平移变换则为(-x,-y,-z)。这样变换矩阵就为:
在这里插入图片描述

接下来一步是把摄像机转换到某个目标,此目标是在世界空间。我们想找到新坐标系统下的顶点的位置。所以问题实质为:我们如何把一个点从一个坐标系统转换到另外一个系统?

看上面的图,我们的世界空间坐标系为:(1,0,0),(0,1,0),(0,0,1)。线性无图指的是,我们可以找不到xyz,使得x*(1,0,0)+y*(0,1,0)+z*(0,0,1)=(0,0,0)。从几何术语来说 ,任意一个向量,和另外两个向量构成的屏幕是垂直的。很容易得到上面的摄像机系统为(1,0,-1),(0,1,0),(1,0,1),经过标准化之后,我们得到(0.7071,0,-0.7071), (0,1,0) ,(0.7071,0,0.7071)。

下面的图展示了两个坐标系统的轴信息:
在这里插入图片描述

我们知道如何得到单位向量,它代表了相机的坐标轴(而且是在世界空间内),而且我们知道世界空间的位置(x,y,z)。我么所要找到的是向量(x’,y’,z’)。我们利用点乘的操作,称之为标量透视。标量透视的结构是任意向量A和单位向量B。A在B上的投影。上面的例子中,我们将(x,y,z)和单位向量相乘得到的是X对应x’。同样的方法y对应y’,z得到z’,(x’,y’,z’)就是(x,y,z)在摄像机空间的位置。

我们把这个知识用于解决摄像机的朝向问题。这个方法是uvn摄像机,它是定义摄像机朝向的一种方法。此方法的定义方式如下:

  1. N——摄像机到目标的向量。这个在3D中叫做朝向,这个向量和Z轴相关。
  2. V——如果是垂直站立的,这个向量就是头朝向天空的向量。如果你写的是一个飞机模拟器,那么这个方向就是朝着地面的。此向量和Y轴相关。
  3. U——这个向量指向的是摄像机的右侧,和X轴相关。

为了把世界坐标系点转换到摄像机空间(uvn向量),具体如下:
在这里插入图片描述

本节教程的代码中,全局变量gWorld变为了gWVP。这个变化代表了一系列的变换。WVP代表了World-View-Position。

代码注释:
本节,我们需要做些变换,然后把基础的矩阵操作从Pipeline类中移动到Matrix4f类中。Pipeline告诉Matrix4f,如何初始化自己,然后连接一些矩阵创建最终的变换矩阵。

(pipelin.h:85)

struct { 
    Vector3f Pos; 
    Vector3f Target;
    Vector3f Up;
} m_camera;

Pipeline类中包含了几个方法用来存储摄像机的参数。注意到指向摄像机右侧的丢失了,变为了u。它是通过目标和向上向量叉乘得到。除此之外还有一个SetCamera方法,用来传递这些值。
(math3d.h:21)

Vector3f Vector3f::Cross(const Vector3f& v) const 
{
    const float _x = y * v.z - z * v.y;
    const float _y = z * v.x - x * v.z;
    const float _z = x * v.y - y * v.x;

    return Vector3f(_x, _y, _z);
}

Vector3f有一个新的方法,叉乘的方法。两个向量的叉乘,得到是一个垂直这两个向量的向量。所有向量只有方向有意义,起点没有意义。
(math3d.h:30)

Vector3f& Vector3f::Normalize()
{
    const float Length = sqrtf(x * x + y * y + z * z);

    x /= Length;
    y /= Length;
    z /= Length;

    return *this;
}

为了得到uvn矩阵,我们需要把向量单位化,上面的函数就是单位化函数。
(math3d.cpp:84)

void Matrix4f::InitCameraTransform(const Vector3f& Target, const Vector3f& Up)
{
    Vector3f N = Target;
    N.Normalize();
    Vector3f U = Up;
    U = U.Cross(Target);
    U.Normalize();
    Vector3f V = N.Cross(U);

    m[0][0] = U.x; m[0][1] = U.y; m[0][2] = U.z; m[0][3] = 0.0f;
    m[1][0] = V.x; m[1][1] = V.y; m[1][2] = V.z; m[1][3] = 0.0f;
    m[2][0] = N.x; m[2][1] = N.y; m[2][2] = N.z; m[2][3] = 0.0f;
    m[3][0] = 0.0f; m[3][1] = 0.0f; m[3][2] = 0.0f; m[3][3] = 1.0f;
}

上面的函数得到的摄像机变换矩阵,供后面的管线类使用。uvn向量,在矩阵中用行展示。由于顶点位置是在右边相乘。

(pipeline.cpp:22)


const Matrix4f* Pipeline::GetTrans()
{
    Matrix4f ScaleTrans, RotateTrans, TranslationTrans, CameraTranslationTrans, CameraRotateTrans, PersProjTrans;

    ScaleTrans.InitScaleTransform(m_scale.x, m_scale.y, m_scale.z);
    RotateTrans.InitRotateTransform(m_rotateInfo.x, m_rotateInfo.y, m_rotateInfo.z);
    TranslationTrans.InitTranslationTransform(m_worldPos.x, m_worldPos.y, m_worldPos.z);
    CameraTranslationTrans.InitTranslationTransform(-m_camera.Pos.x, -m_camera.Pos.y, -m_camera.Pos.z);
    CameraRotateTrans.InitCameraTransform(m_camera.Target, m_camera.Up);
    PersProjTrans.InitPersProjTransform(m_persProj.FOV, m_persProj.Width, m_persProj.Height, m_persProj.zNear, m_persProj.zFar);

    m_transformation = PersProjTrans * CameraRotateTrans * CameraTranslationTrans * TranslationTrans * RotateTrans * ScaleTrans;
    return &m_transformation;
}

猜你喜欢

转载自blog.csdn.net/wodownload2/article/details/83032213