2

TinyRenderer笔记2:透视投影

 1 year ago
source link: https://www.kirito.info/tinyrenderer-note-2/
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.

TinyRenderer笔记2:透视投影

 2023-04-08  2023-04-08  约 1497 字   预计阅读 3 分钟    次阅读     条评论  

前面渲染模型时候,没有考虑每个点的z坐标,这种方式叫做正交投影Orthographic projection,模型看起来偏胖,因为我们平时在3d世界看到的物体都是近大远小的。透视投影Perspective projection就是用近大远小的方式投影。

两种投影对比:

正交 透视
1-5.png
2-2.png

线性变换Linear transformations

线性变换从几何直观有三个要点:

  • 变换前是直线的,变换后依然是直线
  • 直线比例保持不变
  • 变换前是原点的,变换后依然是原点

说白了就是缩放、裁切和旋转, 不包括平移:可以看这个文章

平面上的线性变换都可以用一个二维矩阵计算:

[abcd][xy]=[ax+bycx+dy] \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax + by \\ cx + dy \end{bmatrix} [ac​bd​][xy​]=[ax+bycx+dy​]

仿射变换affine transformations

说简单点就是线性变换加上平移,用矩阵计算:

[abcd][xy]+[ef]=[ax+by+ecx+dy+f] \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} e \\ f \end{bmatrix} = \begin{bmatrix} ax + by + e\\ cx + dy + f \end{bmatrix} [ac​bd​][xy​]+[ef​]=[ax+by+ecx+dy+f​]

齐次坐标Homogeneous coordinates

把2x2的变换矩阵加上一行一列,变成3x3,并且把等待变换的向量加上一个总是1的坐标:

[abecdf001][xy1]=[ax+by+ecx+dy+f1] \begin{bmatrix} a & b & e \\ c & d & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} ax + by + e\\ cx + dy + f \\ 1 \end{bmatrix} ​ac0​bd0​ef1​​​xy1​​=​ax+by+ecx+dy+f1​​

这样就实现了和仿射变换一样的效果!这个想法非常简单。平移在二维空间中不是线性的。所以我们将2D嵌入到3D空间中(通过简单地为第三个分量加1)。这意味着二维空间是三维空间中z=1的平面。然后我们执行一个3D线性变换,并将结果投影到我们的2D物理平面上。

将3d投射到2d只需要除以3d分量:

[xyz]→[x/zy/z] \begin{bmatrix} x \\ y \\ z \end{bmatrix} \rightarrow \begin{bmatrix} x/z \\ y/z \end{bmatrix} ​xyz​​→[x/zy/z​]

如果z无限逼近0代表被投影后的点在无穷远处:

  • 被投影的点 -> 投影到平面z=?的2d坐标
  • (x,y,1) -> (x,y)
  • (x,y,1/2) -> (2x,2y)
  • (x,y,1/4) -> (4x,4y)
https://blog-1256556944.file.myqcloud.com/public/tinyrenderer/2-0.png

可以看到,随着平面的下降,投影后的点越来越远,所以当z=0时,表示的是一个向量而不是3d空间中的一个点。

3d仿射变换

2d的仿射变换可以通过吧2d嵌入3d,转换成3d中的线性变换,再投影回2d。同样的道理:3d的仿射变换,可以通过吧3d嵌入4d,转换成4d中的线性变换,在投影回3d!

使用齐次坐标: 点(x,y,z) -> (x,y,z,1),用下面的矩阵试着把它在4d空间中进行变换:

[10000100001000r1][xyz1]=[xyzrz+1] \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & r & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ z \\ rz+1 \end{bmatrix} ​1000​0100​001r​0001​​​xyz1​​=​xyzrz+1​​

再投影回3d:

[xyzrz+1]→[xrz+1yrz+1zrz+1] \begin{bmatrix} x \\ y \\ z \\ rz+1 \end{bmatrix} \rightarrow \begin{bmatrix} \frac{x}{rz+1} \\ \frac{y}{rz+1} \\ \frac{z}{rz+1} \end{bmatrix} ​xyzrz+1​​→​rz+1x​rz+1y​rz+1z​​​

先把这个结果放一边。来看一个模拟现实中人眼将3d中一个点投影到平面上的例子:

有一个点P=(x,y,z),我们要把它投影到z=0的平面上,摄像机(也就是人的眼睛)在z轴上(0,0,c)的位置

https://blog-1256556944.file.myqcloud.com/public/tinyrenderer/2-1.png

根据初中还是高中的知识,三角形ABC和ODC是相似三角形,所以ABAC=ODOC\frac{AB}{AC}=\frac{OD}{OC}ACAB​=OCOD​,进而得出xc−z=x′c\frac{x}{c-z}=\frac{x^{'}}{c}c−zx​=cx′​

x′=x1−z/c x^{'}= \frac{x}{1-z/c} x′=1−z/cx​
同理:
y′=y1−z/c y^{'}= \frac{y}{1-z/c} y′=1−z/cy​

回到矩阵,让r=-1/c:

[xyzrz+1]→[xrz+1yrz+1zrz+1]→[x1−z/cy1−z/cz1−z/c] \begin{bmatrix} x \\ y \\ z \\ rz+1 \end{bmatrix} \rightarrow \begin{bmatrix} \frac{x}{rz+1} \\ \frac{y}{rz+1} \\ \frac{z}{rz+1} \end{bmatrix} \rightarrow \begin{bmatrix} \frac{x}{1-z/c} \\ \frac{y}{1-z/c} \\ \frac{z}{1-z/c} \end{bmatrix} ​xyzrz+1​​→​rz+1x​rz+1y​rz+1z​​​→​1−z/cx​1−z/cy​1−z/cz​​​

如果我们想用位于z轴上距离原点为c的摄像机计算一个中心投影,分三步:

  1. 将3d嵌入到4d中
  2. 在4d中进行线性变换
  3. 投影回3d
[xyz]→[xyz1](1)[10000100001000−1/c1][xyz1]=[xyz1−z/c](2)[xyz1−z/c]→[x1−z/cy1−z/cz1−z/c](3) \begin{aligned} \begin{bmatrix} x \\ y \\ z \end{bmatrix} \rightarrow \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} &\qquad(1)\\ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & -1/c & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ z \\ 1-z/c \end{bmatrix} &\qquad(2)\\ \begin{bmatrix} x \\ y \\ z \\ 1-z/c \end{bmatrix} \rightarrow \begin{bmatrix} \frac{x}{1-z/c} \\ \frac{y}{1-z/c} \\ \frac{z}{1-z/c} \end{bmatrix} &\qquad(3) \end{aligned} ​xyz​​→​xyz1​​​1000​0100​001−1/c​0001​​​xyz1​​=​xyz1−z/c​​​xyz1−z/c​​→​1−z/cx​1−z/cy​1−z/cz​​​​(1)(2)(3)​
// 4d投影到3d
fn v4p2v3(v: glm::Vec4) -> glm::Vec3 {
    glm::vec3(v.x / v.w, v.y / v.w, v.z / v.w)
}

// ...
let camera: glm::Vec3 = glm::vec3(0., 0., 3.);
// 投影变换矩阵,注意gml初始化一行是矩阵中的一列
let projection = glm::mat4(
        1., 0., 0., 0., 
        0., 1., 0., 0., 
        0., 0., 1., -1./camera.z, 
        0., 0., 0., 1.);

// ...
// 透视投影
let a = v4p2v3(projection * a.extend(1.));
let b = v4p2v3(projection * b.extend(1.));
let c = v4p2v3(projection * c.extend(1.));
2-2.png

详细代码见这里076b31fc4ea69f00e2cee530e5e3e25445189b67

Powered by Twikoo v1.6.11
Twikoo 评论管理

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK