Ortho 和 Persp 正在反转 Z 深度标志?
Ortho and Persp are reversing Z depth sign?
OpenGL 的 NDC 坐标形成一个立方体,-Z
边紧贴屏幕,+Z
边最远。
当我使用...
// ortho arguments are: left, right, bottom, top, near, far
pos = pos * glm::ortho<float>(-1, 1, -1, 1, -1, 1);
...pos
的 z
分量被反射; -1 变成 1,10 变成 -10,等等
glm::persp 做了类似的事情,这有点奇怪?如果一个位置的 z
等于 near
,我希望它停留在 NDC 立方体面向屏幕的平面上,但它的符号被任意翻转;它甚至没有落在最远的一侧。
这是为什么?
NDC coordinates for OpenGL form a cube, who's -Z side presses against the screen while it's +Z side is farthest away.
我查看了 Song Ho Ahns 关于 OpenGL 转换的教程,以确保不会讲一些愚蠢的事情。
In perspective projection, a 3D point in a truncated pyramid frustum (eye coordinates) is mapped to a cube (NDC); the range of x-coordinate from [l, r] to [-1, 1], the y-coordinate from [b, t] to [-1, 1] and the z-coordinate from [-n, -f] to [-1, 1].
Note that the eye coordinates are defined in the right-handed coordinate system, but NDC uses the left-handed coordinate system. That is, the camera at the origin is looking along -Z axis in eye space, but it is looking along +Z axis in NDC.
(强调是我的。)
他为此提供了以下很好的插图:
所以,我得出的结论是
glm::ortho<float>(-1, 1, -1, 1, -1, 1);
不应生成单位矩阵,而应生成 z 轴镜像的单位矩阵,例如像
| 1 0 0 0 |
| 0 1 0 0 |
| 0 0 -1 0 |
| 0 0 0 1 |
由于我手头没有glm
,所以我从github上的源代码中提取了相关代码行(glm). Digging a while in the source code, I finally found the implementation of glm::ortho()
in orthoLH_ZO()
:
template<typename T>
GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar)
{
mat<4, 4, T, defaultp> Result(1);
Result[0][0] = static_cast<T>(2) / (right - left);
Result[1][1] = static_cast<T>(2) / (top - bottom);
Result[2][2] = static_cast<T>(1) / (zFar - zNear);
Result[3][0] = - (right + left) / (right - left);
Result[3][1] = - (top + bottom) / (top - bottom);
Result[3][2] = - zNear / (zFar - zNear);
return Result;
}
我对这段代码进行了一些改造以制作以下示例:
#include <iomanip>
#include <iostream>
struct Mat4x4 {
double values[4][4];
Mat4x4() { }
Mat4x4(double val)
{
values[0][0] = val; values[0][1] = 0.0; values[0][2] = 0.0; values[0][3] = 0.0;
values[1][0] = 0.0; values[1][1] = val; values[1][2] = 0.0; values[1][3] = 0.0;
values[2][0] = 0.0; values[2][1] = 0.0; values[2][2] = val; values[2][3] = 0.0;
values[3][0] = 0.0; values[3][1] = 0.0; values[3][2] = 0.0; values[3][3] = val;
}
double* operator[](unsigned i) { return values[i]; }
const double* operator[](unsigned i) const { return values[i]; }
};
Mat4x4 ortho(
double left, double right, double bottom, double top, double zNear, double zFar)
{
Mat4x4 result(1.0);
result[0][0] = 2.0 / (right - left);
result[1][1] = 2.0 / (top - bottom);
result[2][2] = - 1;
result[3][0] = - (right + left) / (right - left);
result[3][1] = - (top + bottom) / (top - bottom);
return result;
}
std::ostream& operator<<(std::ostream &out, const Mat4x4 &mat)
{
for (unsigned i = 0; i < 4; ++i) {
for (unsigned j = 0; j < 4; ++j) {
out << std::fixed << std::setprecision(3) << std::setw(8) << mat[i][j];
}
out << '\n';
}
return out;
}
int main()
{
Mat4x4 matO = ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
std::cout << matO;
return 0;
}
编译并启动它提供以下输出:
1.000 0.000 0.000 0.000
0.000 1.000 0.000 0.000
0.000 0.000 -1.000 0.000
-0.000 -0.000 0.000 1.000
咦! z 用 -1 缩放,即 z 值在 x-y 平面上镜像(如预期)。
因此,OP的观察是完全正确和合理的:
...the z component of pos is reflected; -1 becomes 1, 10 becomes -10, etc.
最难的部分:
Why is this?
我个人的猜测:发明所有这些 GL 东西的 SGI 大师之一 her/his 明智地做到了这一点。
另一种猜测:在眼睛space中,x轴指向右侧,y轴指向上方。将其转换为屏幕坐标,y 轴应指向下方(因为像素 usually/technically 从左上角开始寻址)。因此,这引入了另一个镜像轴,它(再次)改变了坐标系的旋向性。
有点不满意,因此我用谷歌搜索并找到了这个(重复?):
SO: Why is the Normalized Device Coordinate system left-handed?
OpenGL 的 NDC 坐标形成一个立方体,-Z
边紧贴屏幕,+Z
边最远。
当我使用...
// ortho arguments are: left, right, bottom, top, near, far
pos = pos * glm::ortho<float>(-1, 1, -1, 1, -1, 1);
...pos
的 z
分量被反射; -1 变成 1,10 变成 -10,等等
glm::persp 做了类似的事情,这有点奇怪?如果一个位置的 z
等于 near
,我希望它停留在 NDC 立方体面向屏幕的平面上,但它的符号被任意翻转;它甚至没有落在最远的一侧。
这是为什么?
NDC coordinates for OpenGL form a cube, who's -Z side presses against the screen while it's +Z side is farthest away.
我查看了 Song Ho Ahns 关于 OpenGL 转换的教程,以确保不会讲一些愚蠢的事情。
In perspective projection, a 3D point in a truncated pyramid frustum (eye coordinates) is mapped to a cube (NDC); the range of x-coordinate from [l, r] to [-1, 1], the y-coordinate from [b, t] to [-1, 1] and the z-coordinate from [-n, -f] to [-1, 1].
Note that the eye coordinates are defined in the right-handed coordinate system, but NDC uses the left-handed coordinate system. That is, the camera at the origin is looking along -Z axis in eye space, but it is looking along +Z axis in NDC.
(强调是我的。)
他为此提供了以下很好的插图:
所以,我得出的结论是
glm::ortho<float>(-1, 1, -1, 1, -1, 1);
不应生成单位矩阵,而应生成 z 轴镜像的单位矩阵,例如像
| 1 0 0 0 |
| 0 1 0 0 |
| 0 0 -1 0 |
| 0 0 0 1 |
由于我手头没有glm
,所以我从github上的源代码中提取了相关代码行(glm). Digging a while in the source code, I finally found the implementation of glm::ortho()
in orthoLH_ZO()
:
template<typename T>
GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar)
{
mat<4, 4, T, defaultp> Result(1);
Result[0][0] = static_cast<T>(2) / (right - left);
Result[1][1] = static_cast<T>(2) / (top - bottom);
Result[2][2] = static_cast<T>(1) / (zFar - zNear);
Result[3][0] = - (right + left) / (right - left);
Result[3][1] = - (top + bottom) / (top - bottom);
Result[3][2] = - zNear / (zFar - zNear);
return Result;
}
我对这段代码进行了一些改造以制作以下示例:
#include <iomanip>
#include <iostream>
struct Mat4x4 {
double values[4][4];
Mat4x4() { }
Mat4x4(double val)
{
values[0][0] = val; values[0][1] = 0.0; values[0][2] = 0.0; values[0][3] = 0.0;
values[1][0] = 0.0; values[1][1] = val; values[1][2] = 0.0; values[1][3] = 0.0;
values[2][0] = 0.0; values[2][1] = 0.0; values[2][2] = val; values[2][3] = 0.0;
values[3][0] = 0.0; values[3][1] = 0.0; values[3][2] = 0.0; values[3][3] = val;
}
double* operator[](unsigned i) { return values[i]; }
const double* operator[](unsigned i) const { return values[i]; }
};
Mat4x4 ortho(
double left, double right, double bottom, double top, double zNear, double zFar)
{
Mat4x4 result(1.0);
result[0][0] = 2.0 / (right - left);
result[1][1] = 2.0 / (top - bottom);
result[2][2] = - 1;
result[3][0] = - (right + left) / (right - left);
result[3][1] = - (top + bottom) / (top - bottom);
return result;
}
std::ostream& operator<<(std::ostream &out, const Mat4x4 &mat)
{
for (unsigned i = 0; i < 4; ++i) {
for (unsigned j = 0; j < 4; ++j) {
out << std::fixed << std::setprecision(3) << std::setw(8) << mat[i][j];
}
out << '\n';
}
return out;
}
int main()
{
Mat4x4 matO = ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
std::cout << matO;
return 0;
}
编译并启动它提供以下输出:
1.000 0.000 0.000 0.000
0.000 1.000 0.000 0.000
0.000 0.000 -1.000 0.000
-0.000 -0.000 0.000 1.000
咦! z 用 -1 缩放,即 z 值在 x-y 平面上镜像(如预期)。
因此,OP的观察是完全正确和合理的:
...the z component of pos is reflected; -1 becomes 1, 10 becomes -10, etc.
最难的部分:
Why is this?
我个人的猜测:发明所有这些 GL 东西的 SGI 大师之一 her/his 明智地做到了这一点。
另一种猜测:在眼睛space中,x轴指向右侧,y轴指向上方。将其转换为屏幕坐标,y 轴应指向下方(因为像素 usually/technically 从左上角开始寻址)。因此,这引入了另一个镜像轴,它(再次)改变了坐标系的旋向性。
有点不满意,因此我用谷歌搜索并找到了这个(重复?):
SO: Why is the Normalized Device Coordinate system left-handed?