Assignment1
前置知识
旋转矩阵(二维)
(
c
o
s
α
−
s
i
n
α
s
i
n
α
c
o
s
α
)
\begin{pmatrix} cos\alpha&-sin\alpha \\ sin\alpha&cos\alpha \end{pmatrix}
(cosαsinα−sinαcosα)
三维的旋转矩阵,保持这四个系数的值和相对位置不变,绕哪个轴转,就将对应列设成1,四个系数补到对应位置上。唯一例外是绕y轴旋转,右上角的符号要移到左下角(相对其他两矩阵转置),因为绕y转时叉积方向和另外两条轴相反(右手拇指向着旋转轴的方向,绕x转是y到z,绕z转是x到y;绕y转若想得到x到z需要拇指背对y轴方向)
View/Transformation
如何定义一个camera 位置、Up方向、Look-at方向
camera坐标系 原点、Y轴(Up)、Z轴(-Look-at)
变换矩阵(从World Space到View Space)
M
v
i
e
w
=
R
v
i
e
w
T
v
i
e
w
M_{view}=R_{view}T_{view}
Mview=RviewTview
涉及坐标系变换,先将原点从World Space的原点移到Camera的位置(-X,-Y,-Z);再旋转(用World Space中的Camera三条轴位置所得矩阵的逆矩阵),由于是纯旋转(正交)矩阵,逆矩阵等于转置矩阵
Perspective Projection
任务 现实中视锥体近平面n比远平面f要小(近大远小),然而我们要将视锥体投影到齐次坐标系中,所以需要将f映射成与n一样的大小并保持对应关系不变(将远平面f挤成近平面n一样大小),再对得到的立方体用正交变换
推导
x轴和y轴不涉及复杂变换,因此用简单投影公式即可(如Ynew = Yold*(Zn/Z) z为视锥体里面的点 n为近平面:将视锥体内挤成和n一样大小)
由此得到变换后结果(同时乘了z,此时z仍未知),由该结果可倒推出部分M
∵
M
P
2
O
(
x
y
z
1
)
=
(
n
x
n
y
u
n
k
n
o
w
n
z
)
∴
M
P
2
O
=
(
n
0
0
0
0
n
0
0
?
?
?
?
0
0
1
0
)
\because M_{P2O} \begin{pmatrix} x \\ y \\ z \\ 1\end{pmatrix} = \begin{pmatrix} nx \\ ny \\ unknown \\ z\end{pmatrix} \therefore M_{P2O} = \begin{pmatrix} n&0&0&0 \\ 0&n&0&0 \\ ?&?&?&? \\ 0&0&1&0\end{pmatrix}
∵MP2O⎝⎜⎜⎛xyz1⎠⎟⎟⎞=⎝⎜⎜⎛nxnyunknownz⎠⎟⎟⎞∴MP2O=⎝⎜⎜⎛n0?00n?000?100?0⎠⎟⎟⎞
接下来从近平面n和远平面分别得到两条性质(相当于代入特殊值n和f解方程)
近平面n上的点全部不变 远平面f上的点Z坐标全部不变
首先运用第一条性质,将n代入Z:
∵
M
P
2
O
(
x
y
n
1
)
=
(
n
x
n
y
n
2
n
)
∴
(
0
0
A
B
)
(
x
y
n
1
)
=
n
2
\because M_{P2O}\begin{pmatrix} x \\ y \\ n \\ 1\end{pmatrix} = \begin{pmatrix} nx \\ ny \\ n^{2} \\ n\end{pmatrix} \therefore \begin{pmatrix} 0&0&A&B \end{pmatrix} \begin{pmatrix} x \\ y \\ n \\ 1\end{pmatrix} = n^2
∵MP2O⎝⎜⎜⎛xyn1⎠⎟⎟⎞=⎝⎜⎜⎛nxnyn2n⎠⎟⎟⎞∴(00AB)⎝⎜⎜⎛xyn1⎠⎟⎟⎞=n2
进一步推导除第三行的形式(最终结果n平方和xy都无关,因此前两项为0)
同理运用第二条性质,将f代入Z(形式完全一致)
联立两条方程,得出完整的投影矩阵:
{
A
n
+
B
=
n
2
A
f
+
B
=
f
2
→
{
A
=
n
+
f
B
=
−
n
f
→
M
P
2
O
=
(
n
0
0
0
0
n
0
0
0
0
n
+
f
−
n
f
0
0
1
0
)
\begin{cases} An+B=n^2 \\Af+B=f^2 \end{cases} \rightarrow \begin{cases} A = n+f \\B=-nf \end{cases} \rightarrow M_{P2O} = \begin{pmatrix} n&0&0&0 \\ 0&n&0&0 \\ 0&0&n+f&-nf \\ 0&0&1&0\end{pmatrix}
{An+B=n2Af+B=f2→{A=n+fB=−nf→MP2O=⎝⎜⎜⎛n0000n0000n+f100−nf0⎠⎟⎟⎞
再乘上正交矩阵,得到完整投影矩阵:
正交矩阵的任务是先将视线立方体移到(View Space)的原点,再缩放到标准大小[-1,1];在右手坐标系中,近平面n的Z坐标比远平面f要大,因此是n-f
M
O
=
(
2
/
(
r
−
l
)
0
0
0
0
2
/
(
t
−
b
)
0
0
0
0
2
/
(
n
−
f
)
0
0
0
0
1
)
(
1
0
0
−
(
r
+
l
)
/
2
0
1
0
−
(
t
+
b
)
/
2
0
0
1
−
(
n
+
f
)
/
2
0
0
0
1
)
M_{O} = \begin{pmatrix} 2/(r-l)&0&0&0 \\ 0&2/(t-b)&0&0 \\ 0&0&2/(n-f)&0 \\ 0&0&0&1\end{pmatrix} \begin{pmatrix} 1&0&0&-(r+l)/2 \\ 0&1&0&-(t+b)/2 \\ 0&0&1&-(n+f)/2 \\ 0&0&0&1\end{pmatrix}
MO=⎝⎜⎜⎛2/(r−l)00002/(t−b)00002/(n−f)00001⎠⎟⎟⎞⎝⎜⎜⎛100001000010−(r+l)/2−(t+b)/2−(n+f)/21⎠⎟⎟⎞
在视锥体内部的点经过变换后的z值比原来小:变换矩阵与(x,y,z,1)相乘后z的值等于(-nf)*z^(-1)+(n+f),是个反比例函数,这个图像恒过(n,n)与(f,f),在n与f之间,是下凹的,所以变小了
code
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
// 将角度转换为弧度
float radian = (rotation_angle * MY_PI)/ 180.0 ;
Eigen::Matrix4f translate;
translate <<
cos(radian), -sin(radian), 0, 0,
sin(radian), cos(radian), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = translate * model;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// 用参数构造正交矩阵
Eigen::Matrix4f OrthographicRS = Eigen::Matrix4f::Identity();
Eigen::Matrix4f OrthographicT = Eigen::Matrix4f::Identity();
float top = zNear * tan((eye_fov / 2.0) * MY_PI / 180.0); // 注意用的是zNear(最终大小以n平面为准)
float right = top * aspect_ratio; // 乘屏幕比得x坐标
OrthographicRS <<
1 / right, 0, 0, 0,
0, 1 / top, 0, 0,
0, 0,1 / (zNear - zFar), 0,
0, 0, 0, 1;
OrthographicT <<
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -(zNear + zFar) / 2,
0, 0, 0, 1;
projection = OrthographicRS * OrthographicT;
// 投影矩阵
Eigen::Matrix4f P2O = Eigen::Matrix4f::Identity();
P2O <<
zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
projection *= P2O;
return projection;
}
注意事项
角度值转弧度制
angle = rad * PI / 180
bug:看不见三角形,且按a或d几下之后提示溢出
矩阵左乘和右乘的问题:写投影矩阵时,将P2O放在正交矩阵的坐标,实际上应该放到右边(变换矩阵应该放在右边)