学习opengl必须搞懂视图变换
坐标变换的全局图
OpenGL中的坐标处理过程包括模型变换、视变换、投影变换、视口变换等过程,如下图所示:
在上面的图中,注意,OpenGL只定义了裁剪坐标系、规范化设备坐标系和屏幕坐标系,而局部坐标系(模型坐标系)、世界坐标系和照相机坐标系都是为了方便用户设计而自定义的坐标系,它们的关系如下图所示(来自Chapter 7. World in Motion):
图中左边的过程包括模型变换、视变换,投影变换,这些变换可以由用户根据需要自行指定,这些内容在顶点着色器中完成;而图中右边的两个步骤,包括透视除法、视口变换,这两个步骤是OpenGL自动执行的,在顶点着色器处理后的阶段完成。
模型变换——从模型坐标系到世界坐标系
局部坐标系(模型坐标系)是为了方便构造模型而设立的坐标系,建立模型时我们无需关心最终对象显示在屏幕哪个位置。模型的原点定位也可以有所不同,例如下面在模型坐标系定义的模型:
局部坐标系可以理解,就是自身所有顶点之间的关系。
模型变换的主要目的是通过变换使得用顶点属性定义或者3d建模软件构造的模型,能够按照需要,通过缩小、平移等操作放置到场景中合适的位置。通过模型变换后,物体放置在一个全局的世界坐标系中,世界坐标系是所有物体交互的一个公共坐标系。
例如下面的图中在模型坐标系定义的茶壶模型(来自World, View and Projection Transformation Matrices):
茶壶通过模型变换,转换到世界坐标系中(来自World, View and Projection Transformation Matrices):
模型变换包括:旋转、平移、缩放、错切等内容。
例如将物体从一个位置p=(x,y,z),移动到另一个位置p′=(x′,y′,z′)的过程,用矩阵表示为:
应用多个模型变换时,注意变换执行的顺序影响变换的结果,一般按照缩放–》旋转—》平移的顺序执行;
其实这里的这个矩阵T代表这局部坐标系放在世界坐标系原点进行T变换,比如变换,平移,缩放。
视变换——从世界坐标系到相机坐标系
视变换是为了方便观察场景中物体而设立的坐标系,在这个坐标系中相机是个假想的概念,是为了便于计算而引入的。相机坐标系中的坐标,就是从相机的角度来解释世界坐标系中位置。相机和场景的示意图如下所示(来自World, View and Projection Transformation Matrices):
OpenGL中相机始终位于原点,指向-Z轴,而以相反的方式来调整场景中物体,从而达到相同的观察效果。
打个比方,把相机放入世界坐标系原点,朝向始终x轴,放一个立方体 在z轴的正上方。怎么才能看到这个立方体呢?两种方式
1. 立方体不动,让相机绕y轴逆时针旋转90度。看到物体
2.相机不动,让立方体绕着y轴顺时针旋转90度,立方体现在在x轴上了。看到物体
OpenGL中采用方式2的观点来解释视变换。
通过在世界坐标系中指定相机的位置,指向的目标位置,以及viewUp向量来构造一个相机坐标系,通过视变换矩阵将物体坐标由世界坐标系转换到相机坐标系。
单纯知道一个位置是没办法确定相机不动的。(相机可以旋转),所以,我们需要规定一个相机的位置和看的方向。
知道相机的位置和方向还不够,因为相机可以旋转,看到的物体还是不一样的。因此,我们还需要知道相机正视图的法量方向。只有这样才能确定相机不会动弹
GLK_INLINE GLKMatrix4 GLKMatrix4MakeLookAt(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ)
{
GLKVector3 ev = { eyeX, eyeY, eyeZ };
GLKVector3 cv = { centerX, centerY, centerZ };
GLKVector3 uv = { upX, upY, upZ };
GLKVector3 n = GLKVector3Normalize(GLKVector3Add(ev, GLKVector3Negate(cv)));
GLKVector3 u = GLKVector3Normalize(GLKVector3CrossProduct(uv, n));
GLKVector3 v = GLKVector3CrossProduct(n, u);
GLKMatrix4 m = { u.v[0], v.v[0], n.v[0], 0.0f,
u.v[1], v.v[1], n.v[1], 0.0f,
u.v[2], v.v[2], n.v[2], 0.0f,
GLKVector3DotProduct(GLKVector3Negate(u), ev),
GLKVector3DotProduct(GLKVector3Negate(v), ev),
GLKVector3DotProduct(GLKVector3Negate(n), ev),
1.0f };
return m;
}
因此求视图的相机位置是有九个参数的,上面就是公式。
投影变换——从世界坐标系到裁剪坐标系
投影方式决定以何种方式成像,投影方式有很多种,OpenGL中主要使用两种方式,即透视投影(perspective projection)和正交投影( orthographic projection)。
1.正交投影是平行投影的一种特殊情形,正交投影的投影线垂直于观察平面。平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。
2.透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。
示意图
上面的图中,红色和黄色球在视见体内,因而呈现在投影平面上,而绿色球在视见体外,没有在投影平面上成像。指定视见体通过(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6个参数来指定。注意在相机坐标系下,相机指向-z轴,nearVal和farVal表示的剪裁平面分别为:近裁剪平面z=−nearVal,以及远裁剪平面z=−farVal。
怎么计算模型坐标系在视图坐标系的坐标呢?他们之间的唯一关系都是和世界坐标系想关联。世界坐标系其实就是个中间坐标系,方便这两个坐标系之间的相互转换。
其实很简单的,就是将相机和模型看做一个物体,把相机在世界坐标系的位置移动到世界坐标系原点,并且xyz和世界坐标系重叠,物体的世界坐标系就是相对于相机的坐标系。
假设M1 是世界坐标系原点, 相机相对世界坐标系的位置为M0, 那么将相机移动到坐标原点 的矩阵是T1,
M1=T1M0;
物体在世界坐标系S1; 那么物体和相机作为一个整体移动T1, 那么物体的位置就是T1S1。
假设物体的模型坐标系是S0, 物体的模型坐标系到世界坐标系的转换矩阵是T2, 那么S1 = T2S0;
所以物体的模型坐标系相对于相机的坐标系的关系就是M1 = T1T2S0;
相机坐标系到投影坐标系的关系假设是 K1=T3M1;
因此投影坐标系的结果就是K1=T1T2T3S0;
之后的转换都是计算机内部计算了。水平有限不做介绍