我正在查看您的 Matrix::SetOrtho() 和 Matrix::SetProjection() 函数,并将它们与 GLM 的版本进行比较。
你的职能:
void Matrix::SetOrtho( float left, float right,
float top, float bottom,
float clipMin, float clipMax ) {
memset(data, 0, sizeof(float) * 16);
data[0] = 2 / (right - left);
data[5] = 2 / (top - bottom);
data[10] = -2 / (clipMax - clipMin);
data[15] = 1;
}
void Matrix::SetPerspective( float angle, float aspect,
float clipMin, float clipMax ) {
float tangent = WolfMath::Tan(WolfMath::DegToRad(angle / 2));
memset(data, 0, sizeof(float) * 16);
data[0] = 0.5f / tangent;
data[5] = 0.5f * aspect / tangent;
//data[10] = -(clipMax + clipMin) / (clipMax - clipMin);
data[11] = -1;
data[14] = (-2 * clipMax * clipMin) / (clipMax - clipMin);
}
GLM 的 - GLM
// GLM::ortho
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> ortho( T left, T right,
T bottom, T top,
T zNear, T zFar ) {
#if GLM_COORDINATE_SYSTEM == GLM_LEFT_HANDED
return orthoLH(left, right, bottom, top, zNear, zFar);
#else
return orthoRH(left, right, bottom, top, zNear, zFar);
#endif
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> orthoLH ( T left, T right,
T bottom, T top,
T zNear, T zFar ) {
tmat4x4<T, defaultp> Result(1);
Result[0][0] = static_cast<T>(2) / (right - left);
Result[1][1] = static_cast<T>(2) / (top - bottom);
Result[3][0] = - (right + left) / (right - left);
Result[3][1] = - (top + bottom) / (top - bottom);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = static_cast<T>(1) / (zFar - zNear);
Result[3][2] = - zNear / (zFar - zNear);
#else
Result[2][2] = static_cast<T>(2) / (zFar - zNear);
Result[3][2] = - (zFar + zNear) / (zFar - zNear);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> orthoRH( T left, T right,
T bottom, T top,
T zNear, T zFar ) {
tmat4x4<T, defaultp> Result(1);
Result[0][0] = static_cast<T>(2) / (right - left);
Result[1][1] = static_cast<T>(2) / (top - bottom);
Result[3][0] = - (right + left) / (right - left);
Result[3][1] = - (top + bottom) / (top - bottom);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = - static_cast<T>(1) / (zFar - zNear);
Result[3][2] = - zNear / (zFar - zNear);
#else
Result[2][2] = - static_cast<T>(2) / (zFar - zNear);
Result[3][2] = - (zFar + zNear) / (zFar - zNear);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> ortho( T left, T right,
T bottom, T top ) {
tmat4x4<T, defaultp> Result(static_cast<T>(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);
Result[3][0] = - (right + left) / (right - left);
Result[3][1] = - (top + bottom) / (top - bottom);
return Result;
}
// GLM::perspective (This is a little more involved
// due to the frustum & fov components)
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> frustum( T left, T right,
T bottom, T top,
T nearVal, T farVal ) {
#if GLM_COORDINATE_SYSTEM == GLM_LEFT_HANDED
return frustumLH(left, right, bottom, top, nearVal, farVal);
#else
return frustumRH(left, right, bottom, top, nearVal, farVal);
#endif
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> frustumLH( T left, T right,
T bottom, T top,
T nearVal, T farVal ) {
tmat4x4<T, defaultp> Result(0);
Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
Result[2][0] = (right + left) / (right - left);
Result[2][1] = (top + bottom) / (top - bottom);
Result[2][3] = static_cast<T>(1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = farVal / (farVal - nearVal);
Result[3][2] = -(farVal * nearVal) / (farVal - nearVal);
#else
Result[2][2] = (farVal + nearVal) / (farVal - nearVal);
Result[3][2] = - (static_cast<T>(2) * farVal * nearVal) / (farVal - nearVal);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> frustumRH( T left, T right,
T bottom, T top,
T nearVal, T farVal ) {
tmat4x4<T, defaultp> Result(0);
Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
Result[2][0] = (right + left) / (right - left);
Result[2][1] = (top + bottom) / (top - bottom);
Result[2][3] = static_cast<T>(-1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = farVal / (nearVal - farVal);
Result[3][2] = -(farVal * nearVal) / (farVal - nearVal);
#else
Result[2][2] = - (farVal + nearVal) / (farVal - nearVal);
Result[3][2] = - (static_cast<T>(2) * farVal * nearVal) / (farVal - nearVal);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspective( T fovy, T aspect,
T zNear, T zFar ) {
#if GLM_COORDINATE_SYSTEM == GLM_LEFT_HANDED
return perspectiveLH(fovy, aspect, zNear, zFar);
#else
return perspectiveRH(fovy, aspect, zNear, zFar);
#endif
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspectiveRH( T fovy, T aspect,
T zNear, T zFar ) {
assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
T const tanHalfFovy = tan(fovy / static_cast<T>(2));
tmat4x4<T, defaultp> Result(static_cast<T>(0));
Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
Result[2][3] = - static_cast<T>(1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = zFar / (zNear - zFar);
Result[3][2] = -(zFar * zNear) / (zFar - zNear);
#else
Result[2][2] = - (zFar + zNear) / (zFar - zNear);
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspectiveLH( T fovy, T aspect,
T zNear, T zFar ) {
assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
T const tanHalfFovy = tan(fovy / static_cast<T>(2));
tmat4x4<T, defaultp> Result(static_cast<T>(0));
Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
Result[2][3] = static_cast<T>(1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = zFar / (zFar - zNear);
Result[3][2] = -(zFar * zNear) / (zFar - zNear);
#else
Result[2][2] = (zFar + zNear) / (zFar - zNear);
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspectiveFov( T fov, T width, T height,
T zNear, T zFar ) {
#if GLM_COORDINATE_SYSTEM == GLM_LEFT_HANDED
return perspectiveFovLH(fov, width, height, zNear, zFar);
#else
return perspectiveFovRH(fov, width, height, zNear, zFar);
#endif
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspectiveFovRH( T fov, T width, T height,
T zNear, T zFar ) {
assert(width > static_cast<T>(0));
assert(height > static_cast<T>(0));
assert(fov > static_cast<T>(0));
T const rad = fov;
T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?
tmat4x4<T, defaultp> Result(static_cast<T>(0));
Result[0][0] = w;
Result[1][1] = h;
Result[2][3] = - static_cast<T>(1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = zFar / (zNear - zFar);
Result[3][2] = -(zFar * zNear) / (zFar - zNear);
#else
Result[2][2] = - (zFar + zNear) / (zFar - zNear);
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
#endif
return Result;
}
template <typename T>
GLM_FUNC_QUALIFIER tmat4x4<T, defaultp> perspectiveFovLH( T fov, T width, T height,
T zNear, T zFar ) {
assert(width > static_cast<T>(0));
assert(height > static_cast<T>(0));
assert(fov > static_cast<T>(0));
T const rad = fov;
T const h = glm::cos(static_cast<T>(0.5) * rad) / glm::sin(static_cast<T>(0.5) * rad);
T const w = h * height / width; ///todo max(width , Height) / min(width , Height)?
tmat4x4<T, defaultp> Result(static_cast<T>(0));
Result[0][0] = w;
Result[1][1] = h;
Result[2][3] = static_cast<T>(1);
#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
Result[2][2] = zFar / (zFar - zNear);
Result[3][2] = -(zFar * zNear) / (zFar - zNear);
#else
Result[2][2] = (zFar + zNear) / (zFar - zNear);
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
#endif
return Result;
}
在我分解您的函数和 GLM 之间的矩阵比较之前,这里有一个链接,显示索引 float[16] 和 float[4][4] 之间的区别。
webstaff: Matrix indexing, C++ and OpenGL
主要区别在于您使用的是 float[16] 而 GLM 使用的是 float[4][4],因此索引不同但结果应该仍然相同:
在您的 Ortho 中,您似乎只在对角线下方设置值,而且您似乎明确地只使用一种惯用手,但我不确定您使用的是哪一种:是 LH 还是 RH ?当使用一维数组时,您正在设置 4x4 矩阵的索引:
// I'll be using (l = left, r = right, t = top, b = bottom, f = zFar, n = zNear)
// f = (clipMax), n = (clipMin)
| 0, 4, 8, 12 | | (2/(r-l)), 4, 8, 12 |
| 1, 5, 9, 13 | = | 1, (2/(t-b)), 9, 13 |
| 2, 6, 10, 14 | | 2, 6, (-2/(f-n)), 14 |
| 3, 7, 11, 15 | | 3, 7, 1, (1) |
GLM 在使用 float[4][4] 方案的地方在使用哪个手坐标系之间进行分支,但他们也根据他们的#if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE 标志做出另一个分支决策,所以让我们看看他们的矩阵。
// I'll be using (l = left, r = right, t = top, b = bottom, f = zFar, n = zNear)
// Ortho LH : IF DEPTH_CLIP_SPACE == ZERO_TO_ONE
| 00, 01, 02, 03 | | (2/(r-l)), 01, 02, 03 |
| 10, 11, 12, 13 | = | 10, (2/(t-b)), 12, 13 |
| 20, 21, 22, 23 | | 20, 21, (1/(f-n)), 23 |
| 30, 31, 32, 33 | | (-(r+l)/(r-l)), (-(t+b)/(t-b)), (-(n/(f-n))), 33 |
// Ortho LH : ELSE
| 00, 01, 02, 03 | | (2/(r-l)), 01, 02, 03 |
| 10, 11, 12, 13 | = | 10, (2/(t-b)), 12, 13 |
| 20, 21, 22, 23 | | 20, 21, (2/(f-n)), 23 |
| 30, 31, 32, 33 | | (-(r+l)/(r-1)), (-(t+b)/(t-b)), (-(f+n)/(f-n)), 33 |
// Ortho RH : IF DEPTH_CLIP_SPACE == ZERO_TO_ONE
| 00, 01, 02, 03 | | (2/(r-l)), 01, 02, 03 |
| 10, 11, 12, 13 | = | 10, (2/(t-b)), 12, 13 |
| 20, 21, 22, 23 | | 20, 21, (-(1/(f-n)), 23 |
| 30, 31, 32, 33 | | (-(r+l)/(r-l)), (-(t+b)/(t-b)), (-(n/(f-n))), 33 |
// Ortho RH : ELSE
| 00, 01, 02, 03 | | (2/r-l)), 01, 02, 03 |
| 10, 11, 12, 13 | = | 10, (2/(t-b)), 12, 13 |
| 20, 21, 22, 23 | | 20, 21, (-(2/(f-n)), 23 |
| 30, 31, 32, 33 | | (-(r+l)/(r-l)), (-(t+b)/(t-b)), (-(f+n)/(f-n)), 33 |
// However they do have a basic Orhto function that doesn't consider
// the handedness nor the clip space and you can see their matrix here:
// Ortho
| 00, 01, 02, 03 | | (2/r-l)), 01, 02, 03 |
| 10, 11, 12, 13 | = | 10, (2/(t-b)), 12, 13 |
| 20, 21, 22, 23 | | 20, 21, (1), 23 |
| 30, 31, 32, 33 | | (-(r+l)/(r-l)), (-(t+b)/(t-b)), 32, 33 |
注意: - 我不是 100% 确定,但我确实认为 OrthoLH 和 OrthoRH 是围绕 3D 设计的,而普通 Ortho 是围绕 3D 设计的2D,因为它没有考虑透视分割以及深度缓冲区或 z 缓冲区。
从上面的矩阵你可以看到我做了什么,现在你可以比较它们;您可以对您的版本和 GLM 之间的视角和视图执行相同的方法。我不会在这里做它们,因为这已经是一个很长的答案了,所以我会把它们留给你作为练习。您必须考虑您使用的惯用手、剪辑空间、截锥体、fov 以及正在使用的角度类型(度数或弧度)。
在确保您的矩阵正确之后,这还没有结束。当您开始应用仿射变换(平移、缩放和旋转)或(倾斜)时,完成的顺序很重要,并且顺序将在坐标系之间发生变化。当您将顶点信息从一个矩阵转移到下一个矩阵时,您还必须考虑到这一点;从模型到世界到剪辑到视图(屏幕 - 相机)空间;尤其是在 3D 设置中工作时,因为其中 2D 有点不同,因为它们不涉及 z 分量或深度缓冲区。其他需要注意的是顶点的缠绕顺序以及是否打开或关闭背面剔除以及混合(透明度)。我希望这可以帮助你找到你的错误。
编辑: - 我在 GLM Ortho 矩阵之一中确实有错误。它在元素 [3][2] 的 #if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE 中的 orthoRH() 中,而不是 #else 版本中。我最终得到了(-(f+n)/(f-n)),这是错误的。现在已使用适当的表达式 (-(n/(f-n)))
对其进行更正和更新