zoukankan      html  css  js  c++  java
  • [翻译转载]OpenGL投影矩阵(Projection Matrix)构造方法

    OpenGL Projection Matrix

    原文地址:原文

    Overview

    电脑显示器是一个二维平面, 而OpenGL渲染出来的场景却是三维的 , 所以必须要投影到二维的电脑屏幕上. 可以使用 GL_PROJCETION matrix 来进行投影转换. 首先,它把顶点数据从 eye coordinates(视点坐标) 转换到 clip coordinates(裁剪坐标). 在将这些坐标除以(w) 坐标分量来转换到NDC(标准化设备坐标)上

    因此,我们应该清楚 裁剪(可视平截头体的裁剪) 和 NDC 的转换都包含在了 GL_PROJECTION 矩阵中了. 下一段将会说明如何通过l,r,b,t,n,f(左,右,下,上,近和远)这六个边界值来构建透视矩阵.

    注意 可视平截头体的裁剪(裁剪) 是在 clip coordinates(裁剪坐标) 中, 在除以 (w_c) 前执行的.
    clip coordinates 的 (x_c,y_c,z_c) 会通过 (w_c) 来测试, 如果任何一个大于(w_c) 或 小于(-w_c), 那么这个顶点就会被丢弃.

    [-w_c<x_c,y_c,z_c<w_c ]

    当有丢弃时,OpenGL 将会重新构建多边形的边.

    一个被平截头体剪裁的三角形
    一个被平截头体剪裁的三角形

    ## Perspective Projection(透视投影)

    在透视投影中, truncated pyramid frustum (eye coordinates) (截锥体平截头体(视点坐标)) 中 三维的点呗映射到一个立方体(NDC); 三个坐标分量分别映射到 ([-1,1]. x: [l,r] => [-1,1], y:[b,t] => [-1,1], z:[n,f] => [-1,1])

    注意 eye coordinates 是定义在右手坐标中的, 但 NDC 是在左手坐标中的. 因此 源点摄像机在 eye space (视觉空间) 中看向 (z轴) 负向 而在 NDC中 看向(z轴) 正向. 由于 glFrustum() 对由进到远只接受正值, 我们需要在构造 GL_PROJECTION 矩阵时 对他们取反.

    截锥体平截头体和标准化设备坐标
    截锥体平截头体和标准化设备坐标

    在OpenGL在, 在 eye space 中的三维点 会被投影到 near plane(projection plane) (近平面(投影平面)) 上, 下面这张图显示了如何将 (p(x_e,y_e,z_e)) 投影到 (p(x_p,y_p,z_p)) 上.

    视锥体的顶视图

    视锥体的顶视图

    视锥体的侧视图

    视锥体的侧视图

     

    在顶视图中,(x_e),eye space 中的的x坐标, 通过相似三角形的比值的映射到 (x_p);

    (frac{x_p}{x_e} = frac{-n}{z_e})
    (x_p = frac{-n*x_e}{z_e} = frac{n*x_e}{-z_e})

    在侧视图中,(y_p)也是通过相似的方法计算出来的;

    (frac{y_p}{y_e} = frac{-n}{z_e})
    (y_p = frac{-n*y_e}{z_e} = frac{n*y_e}{-z_e})

    注意(x_p和y_p)都与(z_e)有关; 与(-z_e)成反比即除以(-z_e). 这是构造 GL_PROJECTION 矩阵的第一条线索. 在 eye coordinates 与 GL_PROJECTION 矩阵相乘完成变换后, clip coordinates 依然是 homogeneous coordinates(齐次坐标). 最终将其除以他的w分量来得到NDC.(更多细节OpenGL Transformation)

    [egin{pmatrix}x_{clip}\y_{clip}\ z_{clip}\w_{clip}end{pmatrix} = M_{projection} imes egin{pmatrix}x_{eye}\y_{eye}\ z_{eye}\w_{eye}end{pmatrix}, egin{pmatrix}x_{ndc}\y_{ndc}\ z_{ndc}end{pmatrix} = egin{pmatrix}x_{clip}/w_{clip}\y_{ndc}/w_{clip}\ z_{ndc}/w_{clip}end{pmatrix} ]

    因此,我们可以用 (-z_e) 作为 clip coordinates 的 w分量. 于是 GL_PROJECTION 矩阵的第四行变成了 ((0,0,-1,0).)

    [egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} .& . & . & .\ .& . & . &. \ .& . & .&. \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}, herefore w_c = -z_e ]

    接下来,我们通过线性关系把 (x_p,y_p) 映射到 NDC中的(x_n,y_n)上; ([l,r] => [-1,1] 和 [b,t] => [-1,1])

    把$x_p$映射到$x_n$

    (x_p)映射到(x_n)

    [x_n = frac{1-(-1)}{r-l}*x_p + eta ]

    [1 = frac{2r}{r-l}+ eta (用(r,1)代换(x_p,x_n)) ]

    [eta = 1-frac{2r}{r-l} = frac{r-l}{r-l}-frac{2r}{r-l} = frac{r-l-2r}{r-l} = frac{-r-l}{r-l} = -frac{r+l}{r-l} ]

    [ herefore x_n = frac{2x_p}{r-l} - frac{r+l}{r-l} ]

     

    把$y_p$映射到$y_n$

    (y_p)映射到(y_n)

    [y_n = frac{1-(-1)}{t-b}*y_p + eta ]

    [1 = frac{2t}{t-b}+ eta (用(t,1)代换(y_p,y_n)) ]

    [eta = 1-frac{2t}{t-b} = frac{t-b}{t-b}-frac{2t}{t-b} = frac{t-b-2t}{t-b} = frac{-t-b}{t-b} = -frac{t+b}{t-b} ]

    [ herefore y_n = frac{2y_p}{t-b} - frac{t+b}{t-b} ]

     

    然后把(x_p和y_p变量代换到上述式子中)

    (x_n = frac{2x_p}{r-l}-frac{r+l}{r-l} (x_p = frac{nxe}{-z_e}))

    (x_n = frac{2*frac{nx_e}{-z_e}}{r-l} - frac{r+l}{r-l})

    (x_n = frac{2n*x_e}{(r-l)(-z_e)} - frac{r+l}{r-l})

    (x_n = frac{frac{2n}{r-l}*x_e}{-z_e} - frac{r+l}{r-l})

    (x_n = frac{frac{2n}{r-l}*x_e}{-z_e} + frac{frac{r+l}{r-l}*z_e} {-z_e})

    (x_n = (frac{2n}{r-l}*x_e+frac{r+l}{r-l}*z_e)/-z_e = x_c / -z_e)

    同理可得

    (y_n = (frac{2n}{t-b}*y_e+frac{t+b}{t-b}*z_e)/-z_e = y_c/-z_e)

    注意我们使每个方程的两个项都除以(-z_e) 来表示perspective division(透视除法)((x_c/w_c,y_c/w_c)). 而且我们之前已经把(w_c设成了-z_e)了, 所以括号内的项已经是 clip coordinates 的 (x_c和y_c)了.

    于是得到了GL_PROJECTION 矩阵的前两行

    [egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ .& . & .&. \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}]

    现在我们只剩GL_PROJECTION 矩阵的第三行需要解出了. 但解(z_n)并不像其他坐标那样简单,因为 eye space 的(z_e)总是被投影到近平面的-n上. 但我们为了 clipping(裁剪)和 depth test(深度测试)需要保证z坐标的唯一性.,而且还要能够反投影(还原变换). 因为z并不依赖于x和y坐标,我们借用w分量 来找到 (z_n和z_e)之间的关系. 因此我们可以像下面这样来指定 GL_PROJECTION 矩阵的第三行.

    [egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ .& . & A&B \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}, z_n=z_c/w_c=frac{Az_e+Bw_e}{-z_e}]

    在 eye space中, (w_e) 等于1. 因此等式变成
    (z_n = frac{Az_e+B}{-z_e})

    我们用((z_e,z_n))的关系来找到系数A和B;将((-n,-1)和(-f,1))回代到上式中
    (left{egin{matrix} frac{-An+B}{n} = -1\ frac{-Af+B}{f} = 1 end{matrix} ight. ightarrow left{egin{matrix} -An+B = -n ;(1)\ -Af+B = f ;(2) end{matrix} ight.)

    重写等式(1);

    (B=An-n ; (1'))

    将B带入(2);

    (-Af +(An-n) = f ; (2))
    $-(f-n)A = f + n ( )A = -frac{f+n}{f-n}$

    把A回代到(1)中;

    ((frac{f+n}{f-n})n + B = -n ; (1))
    (B = -n - (frac{f+n}{f-n})n = -(1+frac{f+n}{f-n})n = -(frac{f-n+f+n}{f-n})n = -frac{2fn}{f-n})

    解出A和B后就可以得到(Z_e和Z_n)的关系;

    (Z_n = frac{-frac{f+n}{f-n}z_e - frac{2fn}{f-n}}{-z_e} ; (3))

    最终解出了整个 GL_PROJECTION 矩阵

    [egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ 0& 0 & frac{-{f+n}}{f-n}& frac{-2fn}{f-n} \ 0& 0 & -1 & 0 end{pmatrix} ]

    这个投影矩阵是一种通用形式,如果可视平截头体是对称的,即(r=-l 和 t=-b),可以对其化简

    [left{egin{matrix} r+l=0\ r-l=2r ; (宽) end{matrix} ight. , left{egin{matrix} t+b=0 \ t-b=2t ;(高) end{matrix} ight.]

    [egin{pmatrix} frac{n}{r}& 0 & 0 & 0\ 0& frac{n}{t} & 0 &0 \ 0& 0 & frac{-{f+n}}{f-n}& frac{-2fn}{f-n} \ 0& 0 & -1 & 0 end{pmatrix}]

    在继续之前,请观察一下等式(3)中(z_e和z_n)的关系,你会发现他们并不是线性关系而是分式关系,这意味着在近平面会有非常高的精度而远平面的精度很低.如果([-n,-f])的范围很大,就会导致深度精度问题(z-fighting(深度冲突)); 在远平面(z_e)值小改动不会影响到(z_n)的值. 所以(n和f)的距离应该越小越好从而减少深度缓冲的精度问题.

    深度缓冲精度的比较
    深度缓冲精度的比较

    Orthographic Projection(正射投影)

    为正射投影构造 GL_PROJECTION 矩阵 比透视模式下的要简单的多

    正射视锥和标准化设备坐标(NDC)
    正射视锥和标准化设备坐标(NDC)

    eye space 中所有的(x_e,y_e和z_e)分量都线性映射到 NDC. 我们只需要把长方体视锥缩放正方体,然后把它移动到原点. 让我们来通过线性关系来解出 GL_PROJECTION 里的元素吧.

    把$x_e映射到x_n$

    (x_e映射到x_n)

    [x_n = frac{1-(-1)}{r-l}*x_e + eta ]

    [1 = frac{2r}{r-l} + eta ; (用(r,1)代换(x_e,x_n)) ]

    [eta = 1 - frac{2r}{r-l} = - frac{r+l}{r-l} ]

    [ herefore x_n = frac{2}{r-l}*x_e - frac{r+l}{r-l} ]

     

    把$y_e映射到y_n$

    (y_e映射到y_n)

    [y_n = frac{1-(-1)}{t-b}*y_e + eta ]

    [1 = frac{2t}{t-b} + eta ; (用(t,1)代换(y_e,y_n)) ]

    [eta = 1 - frac{2t}{t-b} = - frac{t+b}{t-b} ]

    [ herefore y_n = frac{2}{t-b}*y_e - frac{t+b}{t-b} ]

     

    把$z_e映射到z_n$

    (z_e映射到z_n)

    [z_n = frac{1-(-1)}{-f-(-n)}*z_e + eta ]

    [1 = frac{2f}{f-n} + eta ; (用(-f,1)代换(z_e,z_n)) ]

    [eta = 1 - frac{2f}{f-n} = - frac{f+n}{f-n} ]

    [ herefore z_n = frac{-2}{f-n}*z_e - frac{f+n}{f-n} ]

     

    由于在正射投影中不再需要w分量, GL_PROJECTION 矩阵的第四行 保留成(0,0,0,1), 于是可以得到 正射投影的 GL_PROJECTION 矩阵

    [egin{pmatrix} frac{2}{r-l}& 0 & 0 & -frac{r+l}{r-l}\ 0& frac{2}{t-b} & 0 &-frac{t+b}{t-b} \ 0& 0 & frac{-2}{f-n}& -frac{f+n}{f-n} \ 0& 0 & 0 & 1 end{pmatrix} ]

    同样的,如果视锥是对称的,即(r=-l 和 t=-b),可以对其化简

    [left{egin{matrix} r+l=0\ r-l=2r ; (宽) end{matrix} ight. , left{egin{matrix} t+b=0 \ t-b=2t ;(高) end{matrix} ight.]

    [egin{pmatrix} frac{1}{r}& 0 & 0 & 0\ 0& frac{1}{t} & 0 &0 \ 0& 0 & frac{-{2}}{f-n}& -frac{f+n}{f-n} \ 0& 0 & 0 & 1 end{pmatrix}]

  • 相关阅读:
    Shell脚本:使用rsync备份文件/目录
    如何在 CentOS 中设置 NTP 服务器
    NGINX引入线程池 性能提升9倍
    如何在Quagga BGP路由器中设置IPv6的BGP对等体和过滤
    如何使用Weave以及Docker搭建Nginx反向代理/负载均衡服务器
    在 Ubuntu 上配置高性能的 HHVM 环境
    在 Linux 中怎样将 MySQL 迁移到 MariaDB 上
    如何删除 Ubuntu 上不再使用的旧内核
    如何在 Arch Linux 中安装 DNSCrypt 和 Unbound
    在 CentOS 7 中安装并使用自动化工具 Ansible
  • 原文地址:https://www.cnblogs.com/xxrlz/p/12506060.html
Copyright © 2011-2022 走看看