投影矩阵推导过程

来源:互联网 发布:excel 3列数据对比 编辑:程序博客网 时间:2024/04/29 00:55
3d程序经常要用到矩阵算法,
比较常见的如旋转矩阵,平移矩阵,以及投影矩阵
opengl与d3d均有对应的api进行相应的操作。

本文主要介绍一下投影矩阵,
(在阅读irricht与ogre代码时碰到了一些问题,发现视截体是根据投影矩阵计算出来的,

其实也可以根据视角与摄影机的位置与朝向,计算出视截体的,quake就是这么做的,

另外处理纹理阴影时,也需要对投影矩阵有一定程度的了解)


程序内存中保存的是x y z这样的三维数据,最终的显示结果,却只有x y这两个维度。

将三维空间的点转换成屏幕(显存)的二维数据,就是通过投影矩阵来完成的.
投影矩阵其实很简单。

想像一下,近平面与远平面,与原点组成了一个金字塔,

所有的视截体内在三维点,全部投影到近平面上,得到一个二维点
(z变成深度值,对点投影在二维平面上没有影响,这点很重要,在后继的阐述中将会发现,z的值是非线性的,
越接近近平面,值被放大,接近远平面的值则放大系数变小)

再来理一下三维点转换成二维点的过程:
1 世界坐标转换成观察坐标
2 投影变换
3 从坐标中去除齐次坐标的成份(表达的不清楚,实际上是除以点的z坐标值,后面会阐述)

经过旋转平移,我们可以直接假定摄影机就位于0,0,0 对着 0,0,-1(即z轴负向)
而视截体则为一个缺顶的金字塔,由6个平面构成(上下左右,远平面z=n,近平面z=f)
我们假设视角为a(在计算中,这个好像没什么用), 近平面宽w 高 h (远平面不用管,只要知道远平面的z=f即可)
则位于视截体内部的一个点 (x, y, z) 它在近平面上的投影 (x1, y1)
可以表示如下(根据相似三角形)
x1 = x * (n/z)
y1 = y * (n/z)

然后x1 y1 和 z(没有z1) 再转换成标准视空间(一个从 -1,-1,0 延伸到 1,1,1的轴对齐矩形)
即最终的数据(x2, y2, z2) x2 y2取值一定是从-1,1之间 z2的取值则为0,1
按比例变换即可
x2 = x1 / w
y2 = y1 / h


x2 = (x * (n/z))/w
y2 = (y * (n/z))/h
所以投影矩阵其实是一个缩放矩阵,
这里分母包含z,也就是上文提到的齐次坐标的问题,投影矩阵里都是常数(w,h给定情况下)
改写上面的的等式
x2 * z = (x*n)/w
y2 * z = (y*n)/w

然后是z2
因为引入了齐次坐标,自然也希望来个 z2 * z = p*z + q,以便投影矩阵就是一个缩放矩阵
由于 z2取值是 0,1
即 z=n 时 z2等于 0
即 z=f 时 z2等于 1
我们假设这样的一个等式
z2 * z = p * z + q (p q是常数)
分别将z2 = 0, z = n  与 z2 = 1, z = f 代入,即可解出这个方程p 与 q 的值
可以得到
p = f / (f - n)
q = - (f * n) / (f - n)

上面提到过 z2是非线性的,即,接近近平面,被放大,接近远平面,放大倍数变小,在远平面上的点无放大(z2=1)
如果按严格的比例变换,z2的真实值应该是
z2_real = (z - n)/(f - n)  (z2_real严格处在0,1之间)
再来看我们假设求得的z2
p q代入
z2 = (f/z) *( (z-n)/ (f-n) )  ---- 这里笔者做了一些变换,以便说明问题

f/z就是放大的倍数(就是上文反复提到的) 而且 z2也一定处于(0,1)之间,并且单调递增
因为z2并不影响投影结果,它只表征深度,所以这样的变换满足3d绘图需求的。

于是有

x2 * z = (x*n)/w
y2 * z = (y*n)/h
z2 * z = pz + q  (p,q已经在上文求出) ---- <标记1>

则点(x,y,z,1) 这里加上w,因为要解决齐次坐标问题,经过这样一个投影矩阵,变成

(x2*z,  y2*z, z2*z,  z),  去除齐次坐标后 (x2, y2, z2, 1) 就变换到标准视空间了(也就是最终绘图的依据)

现在给出投影矩阵

  n/w, 0,   0, 0    --- 此处的w是宽除2
  0,   n/h, 0, 0     ----  此处h同上
  0,   0,   p, q
  0,   0,   1, 0
 
前面的三行都很好理解,根据<标记1>处的三个等式得来的,
最后一行的1,就是为了引入齐次坐标z.
以便能opengl能正确地去除z

这样的一个矩阵,最后计算结果x2 y2受z影响,就能表现出透视的效果,z2也正确地在0,1之间单调递增(非线性)。

ps:
d3d的标准视空间是 -1,-1,0 1,1,1
opengl的标准的视空间好像是 -1,-1,-1 到 1,1,1 (z的取值好像是-1,1)但这不影响相应的公式推导

w, h的值可以用a与n组合得出(还有纵横比),这也是opengl与d3d那个计算投影矩阵函数要传的参数 a, 纵横比, n, f


附上代码片断与注释

void matrix4::gen_pers_proj_matrix(ant_f32 fovy, ant_f32 aspect, ant_f32 z_near, ant_f32 z_far, matrix4& out_mtx){/** 假设视截体内的点(x, y, z) 投影到标准视空间里的(x_p, y_p, z_p)* 需要把视截体里的点,映身到标准视空间里(-1, -1, 0) (1, 1, 1)* 即投影之后 x_p y_p取值为(-1, 1) z_p的取值为(0, 1)* 根据相似三角形,投影形成的 x_p y_p肯定与z有关,所以矩阵中需要包含一个齐次空间因子,以便做矩阵乘法后w取值为z** 2n/w, 0,    0, 0* 0,    2n/h, 0, 0   --- x y只是单纯的缩放(根据相似三角形)* 0,    0,    p, q   --- 这两个参数纯粹为了矩阵运算是造出来的,在几何上不存在意义,它们能保证变换后的z_p在(0,1)区间单调递增,并且z会与比例值放大(f/z)倍,这也是很多3d编程书籍中提到的放大倍数* 0,    0,    1, 0   --- 1添加齐次空间因子,最终(x, y, z)经过矩阵运算会变成(x_p * z, y_p * z, z_p * z, z),去除齐次坐标因子后得到(x_p, y_p, z_p, 1)** p = f / (f - n)* q = - (f * n) / (f - n)** 以上是d3d的情况,opengl的标准视空间是 (-1, -1, -1) (1, 1, 1),即 z_p的取值范围是(-1, 1)* 并且z是负值,因为opengl默认是位于(0, 0, 0)并且看向z负方向,相应的缩放系数会变成*  x_p * (-z) = (2n/w) * x*  y_p * (-z) = (2n/h) * y*  z_p * (-z) = p_o * z + q_o   --- 这里要做一个变通 z虽然是负的,但解的时候,要假设 z在(n,f) 然后z_p对应(-1,1)** 解得 p_o = (f+n)/(f-n)  q_o=(-2fn)(f-n),  然后考虑z实际是处于(-f, -n)这个区间,则 p_o要取 -(f+n)/(f-n)* 对应的矩阵**  2n/w, 0,    0,   0*  0,    2n/h, 0,   0*  0,    0,    p_o, -1  --- 这个取-1,是因为齐次坐标因子此时是 -z;*  0,    0,    q_o, 0   *//* 2n/h 实际上就是tan(fovy/2) */ant_f32 h = tan(fovy * ANT_AR_CONVERT * 0.5f);ant_f32 w = h / aspect;//aspect不可为零ant_f32 p = (z_far + z_near) / (z_near - z_far);//分母不可为零ant_f32 q = (2 * z_far * z_near) / (z_near - z_far);//分母不可为零out_mtx.m_[0] = w;out_mtx.m_[1] = 0;out_mtx.m_[2] = 0;out_mtx.m_[3] = 0;out_mtx.m_[4] = 0;out_mtx.m_[5] = h;out_mtx.m_[6] = 0;out_mtx.m_[7] = 0;/*  */out_mtx.m_[8] = 0;out_mtx.m_[9] = 0;out_mtx.m_[10] = p;out_mtx.m_[11] = -1;out_mtx.m_[12] = 0;out_mtx.m_[13] = 0;out_mtx.m_[14] = q;out_mtx.m_[15] = 0;}
















0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 事故车辆维修和报废怎么办 4.2货车拉缸了怎么办 工作中与同事发生矛盾怎么办 和领导关系闹僵怎么办 内倒窗户卡住了怎么办 支付宝存在安全风险怎么办 地铁车站空调坏了怎么办 面试防汛值班发生灾情你怎么办 怀孕上班路途太远怎么办 硕士错过校招应该怎么办 收银员收多了钱怎么办 商铺贷款批不了怎么办 铁路局的门面乱收房租怎么办 酒店夜审房费多过怎么办 夜审房价录多了怎么办 做工地拿不到钱怎么办 赢了官司拿不到钱怎么办 工地上拿不到钱怎么办 做了工拿不到钱怎么办 高速公路上车没油了怎么办 高铁乘务员年龄大了怎么办 总公司跑路了分公司怎么办 坐车久了耳朵懵怎么办 过完隧道耳朵疼怎么办 护照还在大使馆需要出国怎么办 护照在大使馆不返回怎么办 美国面签迟到了怎么办 成都美签迟到了怎么办 签证电调没人接怎么办 单位没有抬头纸怎么办在职证明 出国签证无银行流水怎么办 铁路职工得癌症后工作怎么办 去泰国不会泰语和英语怎么办 签证状态一直没有更新怎么办 简理财不能身份信息确认怎么办 德国领事馆没有收到预约邮件怎么办 父母一方带孩子英国签证怎么办 去韩国自由行签证怎么办 韩国自由行签证的该怎么办 法院离婚判决书没了怎么办 离婚判决书对方没收到怎么办