Я смотрел на ваши функции 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: матричное индексирование, C++ и 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 переходит между используемой системой координат с ручным управлением, где они используют схему с плавающей запятой [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 разработан с учетом 2D. не учитывает разделение перспективы, а также буфер глубины или z-буфер.
Из приведенных выше матриц вы можете увидеть, что я сделал, и теперь вы можете их сравнить; вы можете использовать тот же самый точный подход для перспектив и представлений между вашей версией и GLM. Я не буду делать их здесь, так как это уже становится слишком длинным ответом, поэтому я оставлю их вам в качестве упражнения. Вы должны принять во внимание, какую рукоятку вы используете, клип-спейс, усеченный конус, угол обзора и тип используемых углов (градусы или радианы).
Убедившись, что ваши матрицы верны, это еще не конец. Когда вы начинаете применять аффинные преобразования (перемещение, масштабирование и вращение) или (перекос), порядок выполнения важен, и порядок будет меняться между системами координат. Вы также должны принять во внимание, когда вы переносите информацию о своих вершинах из одной матрицы в другую; из модели в мир в клип в пространство просмотра (экран - камера); особенно при работе в 3D-режиме из-за разделения перспективы, где 2D немного отличается, поскольку в них не задействован z-компонент или буфер глубины. Другими вещами, о которых следует помнить, являются порядок намотки ваших вершин и включено или выключено отсечение задней грани, а также смешивание (прозрачности). Я надеюсь, что это поможет вам найти вашу ошибку.
РЕДАКТИРОВАТЬ: - У меня была ошибка в одной из матриц GLM Ortho. Это было в orthoRH()
в #if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
для элемента [3][2]
, а не в версии #else
. В итоге у меня было (-(f+n)/(f-n))
, что неправильно. Теперь он исправлен и дополнен соответствующим выражением (-(n/(f-n)))
06.03.2018