opengl es 2.0 3.0 MVP矩阵计算

从2.0开始,opengl es不再提供glRotate()等函数,因此MVP矩阵需要我们自己计算,并赋值给GLSL。
1) 先来看下opengl所用的矩阵的基本知识:
Opengl 使用的是列矩阵,即顶点向量等是用列向量的齐次坐标表示的。
另外其矩阵存储方式是“列主序(column-major order)/列优先”
线性代数意义的同一个矩阵,在d3d 和 opengl 中却有不同的存储顺序

01
02
03
04
05
06
07
08
09
10
11
12
线代:a11,a12,a13,a14
            a21,a22,a23,a24
            a31,a32,a33,a34
            a41,a42,a43,a44
      d3d :  a11,a12,a13,a14
            a21,a22,a23,a24
            a31,a32,a33,a34
            a41,a42,a43,a44
      gl: a11,a21,a31,a41
          a12,a22,a32,a42
          a13,a23,a33,a43
          a14,a24,a34,a44

矩阵x顶点(记住顺序!!矩阵左乘顶点,顶点用列向量表示)= 变换后的顶点
MatrixXVect-300x71
这里有粗略的介绍

gl_anglestoaxes01
下面来分析一下:
如上图,3个元素集(m0, m1, m2),(m4, m5, m6)和(m8, m9, m10) 是用作欧拉变换和仿射变换,例如1.0中提供的函数glRotate(),缩放glScalef().

注意这三个元素集实际上指得是3个正交坐标系:
(m0, m1, m2): +X 轴,向左的向量(left vector)(相对屏幕自己),默认为(1,0,0)
(m4, m5, m6) : +Y轴,向上的向量(up vector),默认为(0,1,0)
(m8, m9, m10): +Z轴,向前的向量,默认为(0,0,1).

因为使用的是左乘,所以变换的顺序是相对于乘法是逆向的,即最后的变换出现在矩阵相乘之前,最先的变换在最后出现。:
gl_transform05
TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector;
这行代码最先执行缩放,接着旋转,最后才是平移。
用GLSL表示:

mat4 transform = projectionMat* viewMat * modeMat;
vec4 out_vec = transform * in_vec;

因为model矩阵变换比较简单。所以在这里不再讲解,主要讲解View(又叫相机)矩阵和投影(projection)矩阵的构建

2)View 矩阵(相机矩阵)
先来看下1.0和1.1里面提供的函数
函数原型
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
该函数定义一个视图矩阵,并与当前矩阵相乘。
第一组eyex, eyey,eyez 相机在世界坐标的位置
第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置
第三组upx,upy,upz 相机向上的方向在世界坐标中的方向
你把相机想象成为你自己的脑袋:
第一组数据就是脑袋的位置
第二组数据就是眼睛看的物体的位置
第三组就是头顶朝向的方向(因为你可以歪着头看同一个物体)。
gl_lookattoaxes01
如图,P1为点(centerx,centery,centerz),P2为点(eyex,eyey,eyez),u’为给定的向上向量(upx,upy,upz).
先求出朝向向量(Lookat Vertor)为P2 – P1.用伪代码表示(下同)
Vertor3 lookat = (eyex-centerx,eyey-centery,eyez-centerz);
朝向坐标(Forward axis)只要将朝向向量转为单位向量(Vectors Normalizing)

Vertex3 forward = lookat.normalize();
左坐标由给定的向上向量与朝向向量的叉乘计算得出。
Vertex3 left = (upx,upy,upz)xforward;
向上坐标由朝向坐标与左坐标的叉乘计算得出:
Vertex3 up = forward x left;
注意left,up计算后都需要进行归一化计算;

VIEWER

eyecamera
我们要把一个世界坐标系点K(Kx, Ky, Kz),表示成(U,V,N)坐标系的点(假设此时,已经经过平移操作,摄像机在世界坐标系的原点),则其公式为:
Px Ux*kx + Uy* ky + Uz*Kz
py = Vx*kx + Vy* ky + Vz*Kz
px Nx*kx + Ny* ky + Nz*Kz
于是我们构建出矩阵
|leftx lefty leftx|
|upx upy upy |
|forwardx forwardy forwardz|
加上eye的平移:
CAMERA

3,投影矩阵 Projection Matrix

gl_frustumclip

投影变换矩阵用来完成从眼睛坐标系向裁剪坐标系的变换,然后裁剪坐标系再除以系数w,变换到单位换设备坐标系(NDC)

值得注意的是 在裁剪坐标中, xc, ycand zc 与wc比较. 如果改坐标 小于-wc, 或大于wc, 则该顶点将被抛弃.

gl_projectionmatrix_eq27

1)透视矩阵

gl_projectionmatrix01

在透视投影情况下,视景体空间是眼睛坐标系下形成的一个台柱,这个台柱最终映射到单位化的NDC坐标系上,即:x坐标由[l,r]到[-1,1],y坐标由[b, t]到[-1,1],x坐标由[n, f]到[-1,1]。

同时我们应该注意一点:视觉坐标系是右手坐标系,但是NDC坐标系是左手坐标系。那么,相机是位于原点,向Z轴负方向观察(在视觉空间中),但是,在NDC空间中变成了Z轴正方向。

在OpenGL中,三维视觉空间中一点(xe,ye,ze)被投影在近平面上(xp, yp, zp)上,如下图。

gl_projectionmatrix03gl_projectionmatrix04

从截锥体上方看, 视野空间(eye space)里的X轴xe 被映射到 xp,可利用相似三角形比例算出:

gl_projectionmatrix_eq01 同理gl_projectionmatrix_eq02

可见xp和yp的计算都依赖于ze,他们都反比-ze。这是构建投影矩阵的第一条线索。在视野坐标通过右乘投影矩阵的变换后(Projection Matrix),裁剪空间仍然是齐次坐标,它最后通过除以自己的-w变成NDC坐标

gl_transform08gl_transform12

所以我们将w变量设置为-ze。于是GL_PROJECTION矩阵的第四行就是(0,0, -1, 0)。

gl_projectionmatrix_eq03

接着,将裁剪空间中的xp和yp轴线性关系映射到NDC坐标系, [l, r] ⇒ [-1, 1] and [b, t] ⇒ [-1, 1]。可以推导出:

mapXptoXn

根据两点求直线方程

两点式:(x-x1)/(x2-x1)=(y-y1)/(y2-y1) 求解。举X计算为例:

加(r,1)(l,-1)带入上式

(Xp-r)/(r-l)=(Xn -1)/2

Xn = 2(Xp-r)/(r-l) +1

Xn = (2Xp-r-l)/(r-l)

然后将裁剪空间中的xp和yp使用视觉空间中的xe和ye代替:

xpyp

由前面我们将w设置为-ze可知道,上面两个方程的分母就是裁剪空间中的坐标xc和yc。于是有下面的透视矩阵:

gl_projectionmatrix_eq08

至此,我们只需要求解第三行了,求解Zn和Ze变换关系和求解x、y略有不同,因为视觉空间中的ze总是会投影到-n的近裁剪平面上。但是,我们需要的z值必须满足唯一性,以便为了裁剪和深度测试,此外还必须满足可逆变换条件。因为很显然,我们知道z的变换,不依赖x和y,所以,我们将GL_PROJECT矩阵假设为如下表示:

gl_projectionmatrix_eq10

在视觉坐标系中We=1,于是:

gl_projectionmatrix_eq11

为了计算A和B,我们使用(ze, zn) 、(-near, -1)和 (-fargl_projectionmatrix_eq13, 1)的关系, 带入上面方程:

gl_projectionmatrix_eq12

gl_projectionmatrix_eq13

gl_projectionmatrix_eq14

gl_projectionmatrix_eq15

求出AB,于是:ze 和zn的方程变成

gl_projectionmatrix_eq17

于是完整的透视投影矩阵就出来了:

gl_projectionmatrix_eq16

通常我们所设置的视景体空间都是对称的:l =-r, b=-t。所以上面的投影矩阵就简化为下面的对称投影变换矩阵

gl_projectionmatrix_eq20

最后,我们再看看ze和zn的关系式:
,可见他俩并非是线性关系,就是说在近平面处精确度高,在远平面处精度低。所以如果[-n,-f]设置过大就会面临精确度问题。ze在远平面的小变化无法影响Zn的值 ,所以n 和f之间的距离尽量小以减少这种深度缓冲精确度问题(the depth buffer precision problem).

gl_projectionmatrix07

2)正交矩阵

gl_projectionmatrix02

zhengjiao

因为w对应正交投影无意义,所以第四行保持(0,0,0,1),因此矩阵为

gl_projectionmatrix_eq24

如果r=-l,t=-b,将进一步简化矩阵:

gl_projectionmatrix_eq25

备注:
向量归一化分两步:
1 计算出向量的模,即长度
2 将它的各个分量(xy or xyz)除以它的模
|a| = sqrt((ax * ax) + (ay * ay) + (az * az))
x = ax/|a|
y = ay/|a|
z = az/|a|

两个向量a和b的叉积写作a×b(有时也被写成a∧b,避免和字母x混淆)。向量积可以被定义为:
|向量a×向量b|=|a||b|sinθ在这里θ表示两向量之间的角夹角(0° ≤ θ ≤ 180°),它位于这两个矢量所定义的平面上。
这个定义有一个问题,就是同时有两个单位向量都垂直于和:若满足垂直的条件,那么也满足。

一个简单的确定满足“右手定则”的结果向量的方向的方法是这样的:若坐标系是满足右手定则的,当右手的四指从a以不超过180度的转角转向b时,竖起的大拇指指向是c的方向。
向量积|c|=|a×b|=|a| |b|sin<a,b>
即c的长度在数值上等于以a,b,夹角为θ组成的平行四边形的面积。
c的方向垂直于a与b所决定的平面,c的指向按右手规则从a转向b来确定。
a×b=(aybz-azby)i+(azbx-axbz)j+(axby-aybx)k,为了帮助记忆,利用三阶行列式,写成
b×a= -a×b右手规则
vertorcross

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值