I was looking at your Matrix::SetOrtho()
& Matrix::SetProjection()
functions and I was comparing them to GLM
's versions.
Your functions:
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's - 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;
}
Before I breakdown the comparison of matrices between your functions and GLM's here is a link to show the difference between indexing a float[16] and a float[4][4].
webstaff: Matrix indexing, C++ and OpenGL
The major difference is you are using a float[16] and GLM is using float[4][4] so the indexing is different but the results should still be the same:
Within your Ortho it appears that you are setting values only down the diagonal and it also appears that you are explicitly working with only one of the handedness but I'm not sure which one you are working with: Is it LH or RH? You are setting the indexes of the 4x4 matrix as such when using a single dimensional array:
// 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) |
Where GLM is branching between which handed coordinate system is being used where they are using a float[4][4] scheme, but they also make another branch decisions based on their #if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
flag so let's see their matrices.
// 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 |
Note: - I'm not 100% certain however I do think that the OrthoLH & OrthoRH are designed around 3D where the normal Ortho is designed around 2D as it doesn't take into consideration the perspective divide as well as the depth buffer or z-buffer.
From the above matrices you can see what I have done and now you can compare them; you can do this same exact approach for the perspectives and views between your version and GLMs. I will not do them here as this is already getting to be to long of an answer so, I'll leave them up to you as an exercise. You have to take into consideration which handedness you are using, the clip-space, the frustum, fov, and the type of angles (degrees or radians) that are being used.
After making sure that your matrices are right that isn't the end of it. When you start to apply Affine Transformations (translation, scaling, and rotation) or (skewing) the Order that is done is important and the order will change between coordinate systems. You also have to take into consideration when you are transferring your vertices' information from one matrix to the next; from Model to World to Clip to View(Screen - Camera) space; especially when working in a 3D setting because of the perspective divide where 2D is a bit different since their is no z-component or depth buffer involved. Other things to be aware of are the winding order of your vertices and whether you have back face culling turned on or off as well as blending (transparencies). I hope this helps you find your bug.
EDIT: - I did have an error in one of the GLM Ortho matrices. It was in the orthoRH()
in the #if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
for element [3][2]
, not the #else
version. I ended up having (-(f+n)/(f-n))
which is wrong. It is now corrected and updated with the appropriate expression (-(n/(f-n)))