图形学投影
一、概述
投影可以理解为空间到平面的映射。同现实中阳光将事物投影到地面上一样,投影变换将整个向量空间映射到它的其中一个子空间,并且在这个子空间中是恒等变换。
二、图形学中常见投影
三、投影规范化(projection normalization)
使用平移和缩放变换将照相机坐标系下的顶点变换的默认的视见体(部分教程称为视景体)的内部(
[
−
1
,
1
]
3
[-1, 1]^3
[−1,1]3,即
x
,
y
,
z
x,y,z
x,y,z 的取值范围为
[
−
1
,
1
]
[-1, 1]
[−1,1]),该过程称为投影规范化(projection normalization)。
我们采用这种方法是因为最终由 GPU 执行的投影是固定的,投影规范化使我们能够在同一绘制流水线中执行平行投影和透视投影。
规范化矩阵的数学表示
1、定义:
M
n
o
r
m
a
l
M_{normal}
Mnormal :规范化矩阵
T
T
T:平移矩阵
S
S
S :缩放矩阵
l
l
l:为视见体最左边的坐标(Left)
r
r
r :为最右边的坐标(Right)
t
t
t :为最顶的坐标(Top)
b
b
b :为最底坐标(Botton)
n
n
n :最近坐标(Near),由于相机是看向负 Z 轴,这里的 n > f,且都为负数
f
f
f :最远坐标(Far)
2、操作步骤:
- 平移:把中心移到原点
- 缩放:进行放缩从而使视见体的边长为 2
四、正交投影
正交投影(orthographic projection,亦称正投影)是平行投影的一种特殊情况,正交投影的投影线垂直于投影平面。
这里定义投影平面(projection plane)为
z
=
0
z = 0
z=0,根据平行投影的定义可得 点
p
(
x
,
y
,
z
)
p(x, y, z)
p(x,y,z) 投影后得到的点
p
′
(
x
p
,
y
p
,
z
p
)
p'(x_p, y_p, z_p)
p′(xp,yp,zp),其
x
x
x,
y
y
y 坐标保持不变
z
p
z_p
zp 等于 0,因此正交投影的方程可表示为
可得正交变换矩阵为
转换为齐次坐标为
最终正交投影变换矩阵可表示为
五、透视投影
透视投影(Perspective Projection),透视投影是所有投影线相交于投影中心(COP)(图形学中指单点透视),变换后使得物品呈现出近大远小的效果,更接近人眼观察的结果。
计算透视投影步骤
- 将透视投影的四棱台转换为立方体
- 重复正交投影的变换
根据相似三角形可得
y
′
=
n
z
y
,
x
′
=
n
z
x
y' = \frac{n}{z} y, x' = \frac{n}{z}x
y′=zny,x′=znx
转换为齐次坐标表示,并寻找点p(x, y, z)变换之后的关系,
可得将视锥体转换为立方体的变换矩阵为
由于 近裁剪面上面的点
x
x
x,
y
y
y,
z
z
z 坐标变换后值不变,远裁剪面的
z
z
z 坐标变换后值不变,变换后
x
=
x
′
x = x'
x=x′,
y
=
y
′
y = y'
y=y′,这里简称变换矩阵为
M
M
M,可得
近裁剪面的点变换后位置不变,且有
z
z
z 坐标等于
n
n
n,将
n
n
n 替换
z
z
z 可得
因此变换矩阵的第三行与点
p
p
p 的乘积为
n
2
n^2
n2,且这里
x
x
x,
y
y
y 可为任意值,可得
n
2
n^2
n2 与
x
x
x,
y
y
y 无关
即变换矩阵的第三行可表示为
[
0
,
0
,
A
,
B
]
[ 0, 0, A, B ]
[0,0,A,B]
展开可得
将远平面中心点 (0, 0, f) 代入可以得
展开可得
解方程可得
综上可得变换矩阵
最终透视投影变换矩阵可表示为
六、OpenGL中的实现
1、正交投影
在透视投影中,将截锥形的视锥中的 3D 点(眼坐标)映射到立方体(NDC)。
x
x
x 坐标从
[
l
,
r
]
[l,r]
[l,r] 到
[
−
1
,
1
]
[-1,1]
[−1,1] 的范围,
y
y
y 坐标从
[
b
,
t
]
[b,t]
[b,t] 到
[
−
1
,
1
]
[-1,1]
[−1,1] 的范围,
z
z
z 坐标从
[
−
n
,
−
f
]
[-n,-f]
[−n,−f] 的范围到
[
−
1
,
1
]
[-1,1]
[−1,1] (
z
z
z坐标与上面的映射刚好相反,上面为
n
n
n 映射到 1,
f
f
f 映射到 -1)。
注意:眼坐标是在右手坐标系中定义的,但是NDC使用左手坐标系。也就是说,原点的相机在眼睛空间中沿
−
Z
-Z
−Z 轴看,但在NDC中沿
+
Z
+ Z
+Z 轴看。
为了推导过程与上面的区分,我们定义 − n e a r -near −near 为近裁剪面, − f a r -far −far 为远裁剪面。注意红色的负号,这里将右手系翻转为左手系,即将 [ 1 , − 1 ] [1, -1] [1,−1] 翻转为 [ − 1 , 1 ] [-1, 1] [−1,1],所以需要乘以 -1。
正交投影的规范化矩阵
代码实现
inline mat4 Ortho(const GLfloat left, const GLfloat right,
const GLfloat bottom, const GLfloat top,
const GLfloat zNear, const GLfloat zFar)
{
mat4 c;
c[0][0] = 2.0 / (right - left);
c[1][1] = 2.0 / (top - bottom);
c[2][2] = 2.0 / (zNear - zFar);
c[3][3] = 1.0;
c[0][3] = -(right + left) / (right - left);
c[1][3] = -(top + bottom) / (top - bottom);
c[2][3] = -(zFar + zNear) / (zFar - zNear);
return c;
}
2、透视投影
透视投影的规范化矩阵
推导过程与 第五小节中的 透视矩阵计算 相同
根据相似三角形可得 (注:这里
n
e
a
r
near
near 为正数,
z
z
z 为负数所以需要乘以 -1)
根据齐次坐标特性可得
因此可得变换矩阵为
代入
(
0
,
0
,
−
n
e
a
r
)
(0, 0, -near)
(0,0,−near) 和
(
0
,
0
,
−
f
a
r
)
(0, 0, -far)
(0,0,−far),可得
即
综上可得:
代码实现
inline mat4 Frustum(const GLfloat left, const GLfloat right,
const GLfloat bottom, const GLfloat top,
const GLfloat zNear, const GLfloat zFar)
{
mat4 c;
c[0][0] = 2.0 * zNear / (right - left);
c[0][2] = (right + left) / (right - left);
c[1][1] = 2.0 * zNear / (top - bottom);
c[1][2] = (top + bottom) / (top - bottom);
c[2][2] = -(zFar + zNear) / (zFar - zNear);
c[2][3] = -2.0 * zFar * zNear / (zFar - zNear);
c[3][2] = -1.0;
c[3][3] = 0.0;
return c;
}
源码地址:https://github.com/WarZhan/Graphics-Demo/blob/master/Octahedra/mat.h
参考资料:
[1] GAMES101_Lecture_04:https://sites.cs.ucsb.edu/~lingqi/teaching/resources/GAMES101_Lecture_04.pdf
[2] 中科大图形学教程:http://staff.ustc.edu.cn/~zhuang/cgi/lectures/cg6.pdf
[3] OpenGL投影矩阵:http://www.songho.ca/opengl/gl_projectionmatrix.html
欢迎关注个人公众号,实时推送最新博文!