相机矩阵(Camera Matrix)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了相机矩阵(Camera Matrix)相关的知识,希望对你有一定的参考价值。
参考技术A 透视投影属于中心投影。透视投影图简称为透视图或透视,它是从某个投射中心将物体投射到单一投影面上所得到的图形。透视图与人们观看物体时所产生的视觉效果非常接近,所以它能更加生动形象地表现建筑外貌及内部装饰。在已有实景实物的情况下,通过拍照或摄像即能得到透视图;对于尚在设计、规划中的建筑物则作图(手工或计算机)的方法才能画出透视图。透视图以渲染、配景,使之成为形象逼真的效果图。由于是中心投影,因此平行投影中的一些重要性质(如平行性、定比性等)和作图规律,在这里已不适用。
透视投影是用中心投影法将形体投射到投影面上,从而获得的一种较为接近视觉效果的单面投影图。它具有消失感、距离感、相同大小的形体呈现出有规律的变化等一系列的透视特性,能逼真地反映形体的空间形象。
透视投影符合人们心理习惯,即离视点近的物体大,离视点远的物体小,远到极点即为消失,成为灭点。它的视景体类似于一个顶部和底部都被切除掉的棱椎,也就是棱台。这个投影通常用于动画、视觉仿真以及其它许多具有真实性反映的方面。
投影线垂直于投影面的投影属于正交投影 ,也称为平行投影。
我们将一个3维坐标表示为列向量,那么一个 3*3 的矩阵乘以这个列向量就可以得到一个新的列向量。如下,三维笛卡尔坐标与矩阵的乘法只能实现三维坐标的缩放和旋转,而无法实现坐标平移。
也称为相机反切(camera resectioning),主要用于估计图像或者视频摄像机的透镜和图像传感器的相关参数。使用这些参数可以纠正透镜畸变,度量真实世界中物体的大小,或者相机在一个场景中的定位。因而可以被用于机器视觉,去检测或者度量事物,也可用于机器人中,帮助导航系统和3D重建。
相机矩阵分解为两个矩阵的乘积:内参矩阵 K 和外参矩阵 [R|−RC]
内参矩阵是将3D相机坐标变换到2D齐次图像坐标。透视投影的一个理想模型就是针孔相机,有缩放的效果成像为倒影。市面上的相机都是透视投影。如下:
内参矩阵如下:
内参矩阵的每一个参数都有意义:
其它的参数 fy,x0,y0 也可以被转换为对应的世界单元 Fx,X0,Y0 :
第二个等式右边三个矩阵依次是:2D平移、2D缩放、2D切变
另一种等价的分解是将切变放在缩放前面 :
有一点需要注意: 内参不影响可见性 —— 阻隔对象(occluded objects)在图像空间中无法通过简单的2D变换显示出来。这里的 occluded objects 就是那些你希望看到,但是由于某些原因看不到的对象,比如目标跟踪的时候,一个目标被另一个目标遮挡了。
相机的外参矩阵描述的是世界坐标中相机的位置,及其指向方向。有两个成分:旋转矩阵 R 和平移向量 t 。它们并非恰好对应相机的旋转和平移。
外参矩阵以刚体变换矩阵的形式可以记为:左边一个 3∗3 旋转矩阵,右边一个 3∗1 的平移列向量 :
常见的做法是在底部增加一行 (0,0,0,1) ,这使得矩阵为方形的,允许我们进一步将矩阵分解为旋转和平移矩阵:
这个矩阵描述的就是如何将世界坐标系中的点变换到相机坐标系中,向量 t 描述的是世界坐标系原点在相机坐标系中的位置, R 的列代表的是相机坐标系中世界坐标系轴的方向。
从上可以发现,外参主要作用就是描述世界坐标系到相机坐标系的转换。与我们经常想的相机坐标系到世界坐标系的转换刚好相反。
实际中,直接指定相机的姿态比指定世界坐标系中的点如何转换到相机坐标系中更加自然,通过建立一个刚体变换矩阵描述相机姿态,然后对其取逆即可建立相机的外参矩阵。
因而可以这样做:定义一个描述相机中心在世界坐标系中的位置的向量 C ,然后让 Rc 代表相机在世界坐标系旋转到当前姿态需要的旋转矩阵。那么描述相机姿态的变换矩阵就是 (Rc|C) 。同样在底部添加一个行向量 (0,0,0,1) ,那么外参矩阵就是相机姿态矩阵的逆。
倒数第三个等式变换到倒数第二个等式,使用的转置是因为 Rc 是正交阵,此外,平移矩阵的逆就是他的负数平移向量,进而可以得到外参矩阵参数和相机姿态是直接相关:
详解MVP矩阵之ViewMatrix
矩阵推导
ViewMatrix用于直接将World坐标系下的坐标转换到Camera坐标系下。已知相机的坐标系,还有相机在世界空间下的坐标.就可以求出ViewMatrix,下面进行具体推导。
令UVN为相机坐标系下的三个基,,对于一个相机来说,它在开始的时候和世界坐标系是重合的,用户控制相机在世界空间中移动之后,相机的状态可以用两个属性来描述——朝向和位置。也就是说,有了这两个属性,一个相机模型在世界中的状态就确定了。而这两个属性,我们用变换的理论来描述,就是旋转和平移。可以想象,对于世界中的任何一个相机状态,我们都可以把它看成是:相机先围绕自身基原点旋转一定的角度,然后平移到世界空间的某个地方。下图展示了这个过程
图一 . 相机的变换与逆变换
图中,红色是相机的基,而黑色是世界的基,也就是参考系。小人是世界中的一个物体。相机在移动之前,两个基是重合的。当相机在屏幕中定位时,它首先会进行朝向的确定——旋转,然后进行位置的确定——平移。图中的Rotation和Translation两步就是相机定位时所发生的变换。可以看到相机相对于小人的运动。而当进行相机变换的时候,小人应该从世界基变换到相机的基里面。这样,他应该进行一个相机定位的逆定位,先逆平移小人和相机,然后再逆旋转小人和相机,最后相机归位,小人随相机变到了相机空间。这是由Inverse Translation和Inverse Rotation两个步骤完成的,这两个步骤就是相机变换。现在我们推导这个变换。我们把关系写出来,相机本身的变换C包括两个元素
C = TR
其中T是平移变换,R是旋转变换。而相机变换是相机本身变换的逆变换
这个C^-1就是我们要求出的相机变换。其中T^-1很容易求出,即
R^(-1)比较难求出,这里用到了正交基一些知识,可以参考下碰撞检测之Ray-Cylinder检测前面关于正交基的部分。
当相机变换进行完Inverse Translation这一步之后,相机的原点和世界原点就重合了,也就是处理完了关于平移的变换。接下来我们要做的是逆旋转,而其实逆旋转的目的,就是要得到目前世界坐标中经过逆平移的小人在相机坐标系中的坐标。
由坐标转换公式
对于世界坐标中的已经经过逆平移的坐标v’,它在相机坐标系R中的坐标是v’’,而相机坐标系就是
则相机变换的完整公式就是
其中v是小人在世界空间中的坐标,v’’是小人在相机空间中的坐标。则相机变换矩阵就是
常见的求ViewMatrix的情况有三种,一种是用LookAt函数,第二种是类似FPS游戏中通过pitch和yaw来算,还有一种是类似轨迹球的算法。
最后的方法都是转化为求出相机的坐标系的基。
Look At Camera
这里参考是左手坐标系,而且在相机空间,相机的forward是Z轴的负方向,和Unity还有OpenGL一致。
图二 . 相机的左手坐标系
如下图,这种求法需要知道摄像机的位置 eye,一个up向量(global),还有摄像机的观察点at
图三 . LookAt函数模型
Matrix4x4 LookAt(const Vector3& eye, const Vector3& target, const Vector3& up)
{
Vector3 z((eye - target).normalized());
Vector3 x((Vector3::Cross(z, up).normalized()));
Vector3 y(Vector3::Cross(x, z));
Matrix4x4 result;
result[0] = x.x;
result[4] = x.y;
result[8] = x.z;
result[12] = -Vector3::Dot(x, eye);
result[1] = y.x;
result[5] = y.y;
result[9] = y.z;
result[13] = -Vector3::Dot(y, eye);
result[2] = z.x;
result[6] = z.y;
result[10] = z.z;
result[14] = -Vector3::Dot(z, eye);
result[3] = result[7] = result[11] = 0.0f;
result[15] = 1.0f;
return result;
}
算例
//C++
qDebug() << Transform::LookAt(Vector3(1, 2, 3), Vector3(0, 10, 0), Vector3::up);
图四 . LookAt函数计算结果
//Unity
transform.position = new Vector3(1, 2, 3);
transform.LookAt(new Vector3(0, 10, 0), Vector3.up);
Debug.Log(Camera.main.worldToCameraMatrix);
图五 . Unity中的计算结果
(貌似Unity中的Transform.LookAt函数是直接修改的Camera的Rotation?)
FPS Camera
通过之前的结论
假设原始的坐标系为x-(1,0,0), y-(0,1,0), z(0,0,1),这个坐标系经过一个旋转矩阵Matrix旋转之后,新的坐标系就是这个旋转矩阵的转置三个列向量。
其实这里要求的只是旋转矩阵的转置。
注意相机的Z方向并不是相机正对的方向,而是相机的背面。
Matrix4x4 Transform::FPSView(const Vector3& eye, Quaternion rotation)
{
Matrix4x4 rotMatrix = rotation.GetRotMatrix().transpose();
Vector3 x(rotMatrix[0], rotMatrix[4], rotMatrix[8]);
Vector3 y(rotMatrix[1], rotMatrix[5], rotMatrix[9]);
Vector3 z(-rotMatrix[2], -rotMatrix[6], -rotMatrix[10]);
Matrix4x4 result;
result[0] = x.x;
result[4] = x.y;
result[8] = x.z;
result[12] = -Vector3::Dot(x, eye);
result[1] = y.x;
result[5] = y.y;
result[9] = y.z;
result[13] = -Vector3::Dot(y, eye);
result[2] = z.x;
result[6] = z.y;
result[10] = z.z;
result[14] = -Vector3::Dot(z, eye);
result[3] = result[7] = result[11] = 0.0f;
result[15] = 1.0f;
return result;
}
算例
//C++
qDebug() << Transform::FPSView(Vector3(1,2,3), Quaternion::Euler(30, 45, 60));
结果
图六 . FPV函数计算结果
//unity
transform.position = new Vector3(1, 2, 3);
transform.rotation = Quaternion.Euler(30, 45, 60);
Debug.Log(Camera.main.worldToCameraMatrix);
图七 . Unity中运行结果
参考
Understanding the View Matrix - http://www.3dgep.com/understanding-the-view-matrix/
Tutorial 3 : Matrices - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
OpenGL Transformation - http://www.songho.ca/opengl/gl_transform.html
推导相机变换矩阵 - http://blog.csdn.net/popy007/article/details/5120158
链接
以上是关于相机矩阵(Camera Matrix)的主要内容,如果未能解决你的问题,请参考以下文章