Nano Hash - криптовалюты, майнинг, программирование

OpenGL: Почему моя камера перевернута и перевернута?

Моя камера визуализирует вверх ногами и задом наперед (например, объекты, которые должны быть перед камерой, находятся позади нее) в режиме перспективы, а в ортографическом режиме она перевернута, и объекты отображаются, даже если они находятся за камерой. Оси X и Y для вращения также кажутся перепутанными.

Это краткая версия того, как я строю матрицы:

Matrix model = gameObject->transform->GetMatrix();
Matrix view = camera->transform->GetMatrix();
view.Invert();

Matrix projection;
projection.setOrtho(-aspectRatio * ortographicSize, aspectRatio * ortographicSize, -1 * ortographicSize, 1 * ortographicSize, clipMin, clipMax);
// or
projection.SetPerspective(60, aspectRatio, clipMin, clipMax);

Matrix mvp = model * view * projection;

Вы можете найти мой класс Matrix на Github . Проекция задается в методах SetPerspective и SetOrtho соответственно, но проблема также может заключаться в матрице представления, которая использует метод Invert.


  • Почему data[10] закомментированы в SetPerspective? См. это для проблемы клип-пространства с Z в орфографической проекции: дальние плоскости и z в орфографической растеризации"> stackoverflow.com/questions/33342114/ 06.03.2018
  • @Робинсон Спасибо. Я посмотрю на эту ссылку. Я не уверен, почему я добавил строку данных [10]. Должно быть, это была одна из моих попыток исправить это, и это не имело значения, поэтому я прокомментировал это. 06.03.2018
  • @ Rabbid76 Может быть. Я беру матрицу преобразования камеры и инвертирую ее. Это неправильно? 06.03.2018
  • @RobinvanEe Ну, вам это определенно нужно. 06.03.2018
  • Я считаю, что индекс [10] в одномерном массиве будет таким же, как индекс [2][2] в матрице [4][4]. 07.03.2018
  • Поскольку нет ничего плохого в написании собственного матричного класса; если вы можете, я бы предложил использовать библиотеку GLM, особенно с OpenGL и GLSL, поскольку она работает без проблем и значительно упрощает жизнь. 07.03.2018
  • @RobinvanEe С OpenGL их пространство камеры в LH полностью отличается от DirectX RH, поскольку OpenGL не имеет класса камеры, и вы должны его создать. В OpenGL вы на самом деле не перемещаете камеру по сцене или мировому пространству, в OpenGL вы перемещаете сцену или мировое пространство относительно положения и угла обзора камеры. 07.03.2018

Ответы:


1

Я смотрел на ваши функции 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); 
} 

GLMGLM

// 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
  • Спасибо, Фрэнсис. Я хотел бы использовать левостороннюю модель координат, где Y вверху. Одна вещь, которую я делал неправильно, заключалась в использовании матриц, ориентированных на строки, тогда как OpenGL хочет, чтобы они были ориентированы на столбцы. Я думаю, что ваши примеры и объяснение помогут мне исправить мой код, так что спасибо! 07.03.2018
  • @RobinvanEe Не проблема. Я самоучка на С++, и я изучал как DirectX, так и OpenGL на протяжении многих лет с помощью некоторых книг и онлайн-учебников. Это чрезвычайно сложный процесс, требующий много времени и терпения; но, в конце концов, как только вы во всем разберетесь, это того стоит. Просто освежите свои знания в области алгебры, геометрии, тригонометрии, исчисления, линейной алгебры, векторного исчисления, геометрического и аналитического исчисления, логики и вероятности, аффинных преобразований, физики и т. д., где конечной целью является создание 3D-игрового движка с нуля; все вышеперечисленное нужно. 07.03.2018
  • @RobinvanEe Я внес изменения в свой ответ, исправив небольшую, но серьезную ошибку. Мне пришлось обновить одну из орто-матриц GLM; вы можете увидеть раздел редактирования для обновленной информации. 07.03.2018
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..