透视矩阵背后的数学
The math behind the perspective matrix
我想创建自己的 mvp 矩阵作为练习。
auto lookAt(Vec)(const Vec eye, const Vec center, const Vec up)
if(isVector!Vec && Vec.dimension is 3){
auto z = (eye - center).unit;
auto x = cross(up.unit, z);
auto y = cross(z, x);
alias Mat = Matrix!(Vec.Type, 4, 4);
alias Vec4 = Vector!(Vec.Type, 4);
return Mat(Vec4(x, 0),
Vec4(y, 0),
Vec4(z, 0),
Vec4(0, 0, 0, 1)) * translate(-eye);
}
视图转换相当简单,我将其设置为现在沿着负 Z 轴看。
但是我在透视矩阵方面遇到了一些麻烦。
y' = 1 / (tan(fov/2) * z
x' = 1 / (ar * tan(fov/2) * z
其中 x'
和 y'
是新的扭曲值。所以我想出了这个矩阵
Mat4f projection(float fov, float ar, float near, float far){
import std.math: tan;
auto tanHalfAngle = tan(fov/2.0f);
return Mat4f(
Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
Vec4f(0, 0, 1, 0),
Vec4f(0, 0, -1, 0));
}
我目前只是将其设置为 Z
值始终为 -1
。但是我犯了一个小错误,因为现在我除以 -Z
。
问题是上面的 Matrix 有效。我目前正在渲染一个旋转的立方体,除了深度值之外,一切看起来都是正确的。
但我真的很想要这个矩阵
Mat4f projection(float fov, float ar, float near, float far){
import std.math: tan;
auto tanHalfAngle = tan(fov/2.0f);
return Mat4f(
Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
Vec4f(0, 0, -1, 0),
Vec4f(0, 0, 1, 0));
}
但是立方体不再出现在屏幕上,我不明白为什么。
auto view = lookAt(Vec3f(0, 0, -5), Vec3f(0, 0, 0), Vec3f(0, 1, 0));
auto proj = projection(PI/2, 4.0f/3.0f, 0.1f, 100);
我设置测试值
auto v = proj * view * Vec4f(0.5,0.5,0.5,1);
这是我用第一个矩阵除以 -Z
得到的输出
Vec(-0.375, 0.5, -5.5, 5.5)
Vec(-0.0681818, 0.0909091, -1) // divided by w
这是我用第二个矩阵除以 Z
得到的输出
Vec(-0.375, 0.5, 5.5, -5.5)
Vec(0.0681818, -0.0909091, -1) //divided by w
唯一改变的是标志。
但为什么我能看到第一个矩阵的立方体,而第二个矩阵却什么都没有?
OpenGL 的默认裁剪规则是在裁剪 space 中制定的,因为满足以下不等式的所有点都位于视锥体内:
-w <= x, y, z <= w
请注意,这很简单地排除了 w < 0 的任何点,因为那时 -w 将 > w,因此无法满足。
The view transformation was rather simple, I set it up so that I now look along the negative Z axis.
事实并非如此。视图矩阵不单独定义视图方向,投影矩阵也有影响。当使用标准透视投影时,投影矩阵的最后一行可以简单地解释为投影主轴的方向向量 - 因此它定义了眼睛中的观察方向向量 space。经典 GL 投影使用 (0 0 -1 0)
,因此 -z
将是观察方向。
视图矩阵的工作现在是放置相机并旋转场景,以便将目标世界 space 观察方向映射到眼睛 space 投影矩阵正在使用的观察方向.
当您翻转投影矩阵以除以 z
而不是 -z
时,这当然意味着它会朝那个方向看。由于您没有调整您的视图矩阵以遵循该惯例,您的相机现在正以您的视图矩阵设置的相反方向观看。位于 z_eye < 0
相机 "front" 中的点都具有负值 w_clip
,并被近平面裁剪。
我想创建自己的 mvp 矩阵作为练习。
auto lookAt(Vec)(const Vec eye, const Vec center, const Vec up)
if(isVector!Vec && Vec.dimension is 3){
auto z = (eye - center).unit;
auto x = cross(up.unit, z);
auto y = cross(z, x);
alias Mat = Matrix!(Vec.Type, 4, 4);
alias Vec4 = Vector!(Vec.Type, 4);
return Mat(Vec4(x, 0),
Vec4(y, 0),
Vec4(z, 0),
Vec4(0, 0, 0, 1)) * translate(-eye);
}
视图转换相当简单,我将其设置为现在沿着负 Z 轴看。
但是我在透视矩阵方面遇到了一些麻烦。
y' = 1 / (tan(fov/2) * z
x' = 1 / (ar * tan(fov/2) * z
其中 x'
和 y'
是新的扭曲值。所以我想出了这个矩阵
Mat4f projection(float fov, float ar, float near, float far){
import std.math: tan;
auto tanHalfAngle = tan(fov/2.0f);
return Mat4f(
Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
Vec4f(0, 0, 1, 0),
Vec4f(0, 0, -1, 0));
}
我目前只是将其设置为 Z
值始终为 -1
。但是我犯了一个小错误,因为现在我除以 -Z
。
问题是上面的 Matrix 有效。我目前正在渲染一个旋转的立方体,除了深度值之外,一切看起来都是正确的。
但我真的很想要这个矩阵
Mat4f projection(float fov, float ar, float near, float far){
import std.math: tan;
auto tanHalfAngle = tan(fov/2.0f);
return Mat4f(
Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
Vec4f(0, 0, -1, 0),
Vec4f(0, 0, 1, 0));
}
但是立方体不再出现在屏幕上,我不明白为什么。
auto view = lookAt(Vec3f(0, 0, -5), Vec3f(0, 0, 0), Vec3f(0, 1, 0));
auto proj = projection(PI/2, 4.0f/3.0f, 0.1f, 100);
我设置测试值
auto v = proj * view * Vec4f(0.5,0.5,0.5,1);
这是我用第一个矩阵除以 -Z
Vec(-0.375, 0.5, -5.5, 5.5)
Vec(-0.0681818, 0.0909091, -1) // divided by w
这是我用第二个矩阵除以 Z
Vec(-0.375, 0.5, 5.5, -5.5)
Vec(0.0681818, -0.0909091, -1) //divided by w
唯一改变的是标志。
但为什么我能看到第一个矩阵的立方体,而第二个矩阵却什么都没有?
OpenGL 的默认裁剪规则是在裁剪 space 中制定的,因为满足以下不等式的所有点都位于视锥体内:
-w <= x, y, z <= w
请注意,这很简单地排除了 w < 0 的任何点,因为那时 -w 将 > w,因此无法满足。
The view transformation was rather simple, I set it up so that I now look along the negative Z axis.
事实并非如此。视图矩阵不单独定义视图方向,投影矩阵也有影响。当使用标准透视投影时,投影矩阵的最后一行可以简单地解释为投影主轴的方向向量 - 因此它定义了眼睛中的观察方向向量 space。经典 GL 投影使用 (0 0 -1 0)
,因此 -z
将是观察方向。
视图矩阵的工作现在是放置相机并旋转场景,以便将目标世界 space 观察方向映射到眼睛 space 投影矩阵正在使用的观察方向.
当您翻转投影矩阵以除以 z
而不是 -z
时,这当然意味着它会朝那个方向看。由于您没有调整您的视图矩阵以遵循该惯例,您的相机现在正以您的视图矩阵设置的相反方向观看。位于 z_eye < 0
相机 "front" 中的点都具有负值 w_clip
,并被近平面裁剪。