14

小议斜透视投影矩阵(oblique projection matrix)

 4 years ago
source link: https://blog.csdn.net/tkokof1/article/details/89074993
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

小议斜透视投影矩阵(oblique projection matrix)

tkokof1 2019-04-07 20:59:19 919

本文简介了斜透视投影矩阵(oblique projection matrix)相关的一些知识

Unity 的这篇文档提及了斜透视投影的一些内容,还列出了示例代码:

using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    void SetObliqueness(float horizObl, float vertObl) {
        Matrix4x4 mat  = Camera.main.projectionMatrix;
        mat[0, 2] = horizObl;
        mat[1, 2] = vertObl;
        Camera.main.projectionMatrix = mat;
    }
}

代码挺简单的,但是其中的原理文档中并未提及,本篇文章尝试简单讲解一下~

首先,我们要了解一下 Camera.projectionMatrix 这个矩阵的构成,简单起见,我们这里直接给出结论,有兴趣的朋友可以去看看完整的推导过程(很好的一篇文章,目前似乎还没有译文,有时间自己来翻译一下)(更新:自己简单翻译了一下,在这里):

Unity 中的 Camera.projectionMatrix 遵循 OpenGL 的规范约定,正常的透视投影情况下,该矩阵的构成如下:

[ 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 0 0 − f + n f − n − 2 n f f − n 0 0 − 1 0 ] ⎣⎢⎢⎢⎢⎢⎢⎢⎡​r−l2n​000​0t−b2n​00​r−lr+l​t−bt+b​−f−nf+n​−1​00−f−n2nf​0​⎦⎥⎥⎥⎥⎥⎥⎥⎤​

  • l l l 是左(垂直)裁剪面的坐标
  • r r r 是右(垂直)裁剪面的坐标
  • b b b 是下(水平)裁剪面的坐标
  • t t t 是上(水平)裁剪面的坐标
  • n n n 是近(深度)裁剪面的坐标
  • f f f 是远(深度)裁剪面的坐标

正常的透视投影情况下,我们有:

r = − l    ⟹    r + l = 0    ⟹    r − l = 2 r ( 1 ) t = − b    ⟹    t + b = 0    ⟹    t − b = 2 t ( 2 ) ​r=−l⟹r+l=0⟹r−l=2rt=−b⟹t+b=0⟹t−b=2t​(1)(2)​

所以上面的矩阵可以简化为:

[ n r 0 0 0 0 n t 0 0 0 0 − f + n f − n − 2 n f f − n 0 0 − 1 0 ] ⎣⎢⎢⎢⎢⎢⎡​rn​000​0tn​00​00−f−nf+n​−1​00−f−n2nf​0​⎦⎥⎥⎥⎥⎥⎤​

现在我们需要调整这个矩阵来达到斜透视投影的效果,怎么做呢?拿水平方向的斜透视举例,我们要做的其实就是 偏移(shift) 左(垂直)裁剪面的坐标右(垂直)裁剪面的坐标,即偏移上面矩阵中的 l l l 和 r r r, 假设我们偏移 s s s 个坐标单位,则有:

l ′ = l + s ( 3 ) r ′ = r + s ( 4 ) ​l′=l+sr′=r+s​(3)(4)​

考虑最开始的透视投影矩阵,由于我们变更了其中的 l l l 和 r r r(变更为了 l ′ l' l′ 和 r ′ r' r′),所以新的(斜)透视投影矩阵变为:

[ 2 n r ′ − l ′ 0 r ′ + l ′ r ′ − l ′ 0 0 2 n t − b t + b t − b 0 0 0 − f + n f − n − 2 n f f − n 0 0 − 1 0 ] ⎣⎢⎢⎢⎢⎢⎢⎢⎡​r′−l′2n​000​0t−b2n​00​r′−l′r′+l′​t−bt+b​−f−nf+n​−1​00−f−n2nf​0​⎦⎥⎥⎥⎥⎥⎥⎥⎤​

将之前的 ( 1 ) , ( 2 ) , ( 3 ) , ( 4 ) (1),(2),(3),(4) (1),(2),(3),(4) 这四个等式代入计算,我们得到:

[ n r 0 s r 0 0 n t 0 0 0 0 − f + n f − n − 2 n f f − n 0 0 − 1 0 ] ⎣⎢⎢⎢⎢⎢⎡​rn​000​0tn​00​rs​0−f−nf+n​−1​00−f−n2nf​0​⎦⎥⎥⎥⎥⎥⎤​

注意到相比之前简化的透视投影矩阵,只有一个矩阵元素发生了变化(第一行第三列,即M[0, 2]),从之前的 0 0 0 变为了 s / r s/r s/r,而 s / r s/r s/r 这个数值表示的则是(水平)倾斜度:

  • s / r = 0 s/r = 0 s/r=0 即 s = 0 s = 0 s=0,表示不进行偏移,即(水平)倾斜度为 0 0 0
  • s / r = 1 s/r = 1 s/r=1 即 s = r s = r s=r,表示向右偏移整个右(垂直)裁剪面的坐标,即(水平)倾斜度为 1 1 1
  • s / r = − 1 s/r = -1 s/r=−1 即 s = − r = l s = -r = l s=−r=l,表示向左偏移整个右(垂直)裁剪面的坐标,即(水平)倾斜度为 − 1 -1 −1
  • 其他的一些数值情况即代表不同的(水平)倾斜度

垂直方向的斜透视也同样可以依此分析,假设垂直方向的偏移量为 s ′ s' s′ 坐标单位,我们能够得到:

[ n r 0 0 0 0 n t s ′ t 0 0 0 − f + n f − n − 2 n f f − n 0 0 − 1 0 ] ⎣⎢⎢⎢⎢⎢⎢⎡​rn​000​0tn​00​0ts′​−f−nf+n​−1​00−f−n2nf​0​⎦⎥⎥⎥⎥⎥⎥⎤​

可以看到,相比之前简化的透视投影矩阵,新的(斜)透视投影矩阵也仅有一个矩阵元素发生了变化(第二行第三列,即M[1, 2]),并且该元素的数值同样表示(垂直)倾斜度( s ′ / t s'/t s′/t).

综上,如果我们给定了 (水平)倾斜度(垂直)倾斜度,只要据此改变原透视投影矩阵的两个元素(设置 第一行第三列,即M[0, 2] 为(水平)倾斜度,设置 第二行第三列,即M[1, 2] 为(垂直)倾斜度)即可得到我们想要的斜透视投影矩阵~

讲到这里,如果再看一眼先前的示例代码的话,想必是一目了然了~

using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    void SetObliqueness(float horizObl, float vertObl) {
        Matrix4x4 mat  = Camera.main.projectionMatrix;
        mat[0, 2] = horizObl;
        mat[1, 2] = vertObl;
        Camera.main.projectionMatrix = mat;
    }
}

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK