zoukankan      html  css  js  c++  java
  • 投影矩阵的推导(转载)

    转载自kanego http://www.cnblogs.com/kanego/articles/2346971.html

    [译] - 投影矩阵的推导

    原帖地址:

    http://www.codeguru.com/cpp/misc/misc/math/article.php/c10123__1/Deriving-Projection-Matrices.htm

    译文:

    矩阵变换作为3d图形程序员的基本知识,投影矩阵是其中很复杂的内容。平移和缩放是容易理解的,旋转矩阵只需要掌握了基本的三角几何知识,但是投影 矩阵不一样。如果你看过投影矩阵的形式,你会发现你很难很快知道它是怎么来的。而且,我在网上没有发现很多有关推导投影矩阵的资源。本文就是讲述如何推导 投影矩阵。

    对于新接触3d图形学的人来说,推导投影矩阵需要有一定的数学基础,但不是必须的。你可以直接使用最后的公式,如果你使用一种图形API,如 Direct3D,通常你不需要关心,因为API已经提供了计算投影矩阵的功能。一旦你理解了如何推导它,这对你没有坏处。这篇文章为了那些需要了解推导 投影矩阵细节的人的。

    简介:什么是投影

    计算机显示器是一个2d表面,为了显示3d物体,你需要把3d的物体转化成2d的图片,这个过程就是投影。举一个简单例子,最简单的把3d物体变到2d表面上的方法是去掉z坐标。对于1个立方体,如下图1。


    图1:去掉z坐标投影到xy平面

    当然,图1方法很简单,大多数情况不适用。这里不会投影到一个平面上,相反,这里讲的投影会把物体变换到一个规范的是视空间(canonical view volume这个是叫什么?)。对于规范的视空间的坐标,不同图形API可能不同。作为讨论起见,这里使用Direct3D的,即(-1, -1, 0)到(1, 1, 1)。一旦物体的坐标转换到了规范的视空间里面,其中的x、y坐标就用于映射到屏幕空间,z坐标一般用于z-buffer。

    注意图1使用了左手坐标系。这也是Direct3D的形式,本文将一直使用左手坐标系。对于右手坐标系,本文讲述的知识都是适用的。

    现在可以开始讲投影变换了。这里主要讲述2中最普遍的形式:正投影和透视投影。

    正投影

    正投影是一种简单的投影形式,要求所有的投影射线垂直于投影面。正投影最终的规范视空间是一个AAB(axis-aligned box),如下图2。


    图2:正投影

    从图中可以看出,规范的视空间是由6个面组成的:

    左:x = l

    右:x = r

    下:y = b

    上:y = t

    近:z = n

    远:z = f

    由于正投影的视锥(view volume)和规范视空间(canonical view volume)都是AAB,所以不会有像透视投影那样随距离变化的特性。对于正投影,所有的物体的大小不会变化,而且不会随着距离变化。

    图3:简单的正投影应用(原文里的图 转帖者添加)

    下面开始推导正投影矩阵。一个在view volume中的点的x坐标在[l, r],这需要变换到canonical view volume中的[-1, 1]。

    l <= x <= r

    0 <= x-l <= r-l

    0 <= (x-l)/(r-l) <= 1

    0 <= 2(x-l)/(r-l) <= 2

    -1 <= 2(x-l)/(r-l) - 1 <= 1

    -1 <= (2x-r-l)/(r-l) <= 1

    最后你需要得到px+q的形式,所以分解成:

    -1 <= 2x/(r-l) - (r+l)/(r-l) <= 1

    所以得到了在canonical view volume中的x坐标:

    x' = 2x/(r-l) - (r+l)/(r-l)

    同理得到在canonical view volume中的y坐标:

    y' = 2y/(t-b) - (t+b)/(t-b)

    最后来推导z'的公式。在view volume中的点的z坐标在[n, f],需要变换到canonical view volume中的[0, 1]。

    n <= z <= f

    0 <= z-n <= f-n

    0 <= (z-n)/(f-n) <= 1

    0 <= z/(f-n) - n/(f-n) <= 1

    得到z'的表达式:

    z' = z/(f-n) - n/(f-n)

    总结上面的x', y'和z',

    x' = 2x/(r-l) - (r+l)/(r-l)

    y' = 2y/(t-b) - (t+b)/(t-b)

    z' = z/(f-n) - n/(f-n)

    如果写成矩阵形式:


    在Direct3D中一个函数D3DXMatrixOrthoOffCenterLH()正好提供了这个功能(注意到形式有些不同,是行/类矩阵的区别)。“LH”表示的是左手坐标系,但是“OffCenter”表示的又是什么呢?

    第一,在Camera空间,如果Camera被放置在原点,并且方向沿着z轴;第二,如果r = -l,t = -b,并且定义x轴上的宽度w和y轴上的高度h,那么


    上面的矩阵和Direct3D中的D3DXMatrixOrthoLH()计算的结果一致。

    如果把正投影的矩阵分解成如下:


    注意变换形式是:p'(canonical view volume) = P0 * P(view volume)。

    通过这个分解,可以这样来理解正投影矩阵,首先,吧view volume沿z轴把近平面移到原点,其次,缩放使得view volume变换到canonical view volume。

    透视投影

    透视投影因为可以产生距离的错觉(远方的物体看起来更小),所以可以产生更真实的效果,被广泛使用。它和正投影不同的地方是,透视投影的view volume是一个锥体,如下图4:


    图4:透视投影

    从图中可以看出view frustum的近平面由(l, b, n)延伸到(r, t, n)。

    步骤一:对于view frustum中的一个点(x, y, z),把它投影到近平面z = n。因为 投影点在近平面上,所以x坐标的范围在[l, r],y坐标的范围在[b, t]。如 图5

    步骤二:使用在正投影中学习的知识,把在近平面上x的[l, r]隐射到[-1, 1],y的[b,  t]隐射到[-1, 1]。

             

    图5

    第一步:得到投影点



    所以得到投影点(x*n/z, y*n/z, n)。

    第二步:把第一步中得到的投影点,运用正投影中学习的知识隐射。


    代入x = x*n/z 和 y = y*n/z,


    乘以z,


    把(x, y, z)隐射到(x'z, y'z, z'z),所以需要求z'z

    假设z'z = pz + q,而z坐标的隐射是[n, f]到[0, 1],所以得到:

    0 = pn + q

    f = pf + q

    解方程得:

    p = f/(f-n)

    q = -fn/(f - n)

    所以,

    z'z = fz/(f-n) - fn/(f-n)

    总结在一起,


    写成矩阵形式:


    上面的矩阵可以使得,p * (x, y, z, 1) = (x'z, y'z, z'z, z)。这个也是和Direct3D中的D3DXMatrixPerspectiveOffCenterLH()是一样的(注意行/列区别)。同正投影中, 如果满足一定的条件,r = -l,t = -b,则有下面的矩阵形式:


    这个就和Direct3D中的D3DXMatrixPerspectiveLH()结果一样。

    最后,有必要说一下牵扯到宽高比的矩阵表示形式。

    如图6:


    图6

    cot(a/2) = n / (h/2) = 2n/h

    假设r = w/h,即宽高比

    2n/w = 2n/(rh) = (1/r) * cot(a/2)

    则有以下形式:


    这个形式就和Direct3D中的D3DXMatrixPerspectiveFovLH()结果一样。

    注:

    本文原本准备翻译,后来没耐心了,话说最近我做事一直都没什么耐心了,所以没怎么按照原文翻译,而是把重要的列出来。

    上面说的一些推导公式有些不用那么繁琐,但是总的来看,很值得一读。

    本文版权归作者 kanego 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    get the default proxy by Powershell
    import-module in $profile
    Basic Queries (LINQ to XML)
    xpath中双斜杠的作用// double slash
    Powershell 常见问题
    touch all contents in a folder recursively
    What's the difference between HEAD, working tree and index, in Git?
    beyond compare 比较文本 standard alignment VS unaligned
    bat文件中调用传参的问题
    bacth参数说明 cmd parameter
  • 原文地址:https://www.cnblogs.com/Tigerwang1218/p/7253921.html
Copyright © 2011-2022 走看看