OpenGL:为什么我的相机颠倒了?

时间:2018-03-06 19:48:56

标签: c++ opengl math matrix algebra

我的相机在透视模式下向上和向后渲染(例如,应该在相机前面的物体在它后面),而在ortographic模式下,即使它们在相机后面,它也会颠倒并呈现物体。旋转的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类on Github。投影分别在SetPerspective和SetOrtho方法中设置,但问题也可能在于使用Invert方法的视图矩阵。

1 个答案:

答案 0 :(得分:0)

我在看你的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&#39> - 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

主要区别在于你使用了浮点数[16]而GLM正在使用浮点数[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&amp; OrthoRH是围绕3D设计的,其中普通的Ortho是围绕2D设计的,因为它没有考虑透视分度以及深度缓冲区或z缓冲区。

从上面的矩阵中你可以看到我做了什么,现在你可以比较它们;您可以对版本和GLM之间的透视图和视图执行相同的精确方法。我不会在这里做,因为这已经成为一个很长的答案所以,我会把它们留给你作为练习。您必须考虑您正在使用的手性,剪辑空间,平截头体,fov以及正在使用的角度类型(度数或弧度)。

确保你的矩阵是正确的,但不是它的结束。当您开始应用仿射变换(平移,缩放和旋转)或(倾斜)时,完成的顺序很重要,顺序将在坐标系之间变化。您还需要在转移顶点时考虑到这一点。从一个矩阵到下一个矩阵的信息;从模型到世界到剪辑到视图(屏幕 - 相机)空间;特别是在3D设置中工作时,由于透视划分,2D有点不同,因为它们不涉及z分量或深度缓冲。其他需要注意的事项是顶点的缠绕顺序以及是否打开或关闭背面剔除以及混合(透明度)。我希望这可以帮助您找到您的错误。

编辑: - 其中一个GLM Ortho矩阵确实有错误。它位于元素orthoRH()的{​​{1}} #if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE中,而不是[3][2]版本。我最终得到#else这是错误的。现在使用适当的表达式(-(f+n)/(f-n))

进行更正和更新