从固定功能迁移到GLSL OpenGL:我应该如何修改我的视图(lookAt)矩阵和perspection矩阵?

时间:2018-05-28 21:08:40

标签: c++ opengl glsl shader

我正在尝试将我的OpenGL渲染管道从定点迁移到现代OpenGL。我的定点3D管道多年来运作良好。现在,当我切换到GLSL时,最终投影和对象方向看起来像“完全镜像或交换”。我已经排除了所有其他原因(例如未能设置矩阵制服等),因为我可以通过随机拖动鼠标来平移相机来不时渲染3D场景。所以问题缩小到以下几点:

  • 生成视图矩阵的函数(我的函数getViewMat(pos,lookat,upaxis))
  • 生成透视投影矩阵的函数(我的函数getPerspectiveMat(fov,aspect,nearZ,farZ))

由于这两个函数在定点流水线之前生成正确的视图/投影矩阵,现在我想知道是否/如何修改它们以使生成的矩阵适用于着色器管道?

很少stack overflow threads表明对于着色器管道,我们需要手动翻转投影矩阵的Z值,但视图矩阵怎么样?非常感谢任何建议。

更新 Soure代码

/*
    Matrix4f has class members "float m00, m01, m02, m03, m10, m11, ..., m33" representing
    a row-dominant 4x4 matrix, when passed to GLSL, I have remembered to transpose them
*/
void Matrix4f::_getPerspectiveByFov(float fov, float aspectRatio, float nearZ, float farZ, int matrixType)
{
    float halfFov   = MathUtil::degToRad(fov) * 0.5f;
    float width, height;
    //We use the small side out of width and height as the base size, then calculate the other side by aspect ratio.
    float _tanFOV = (float)tan(halfFov);
    if(aspectRatio < 1.f)//width is smaller
    {
        width   = 2.f * nearZ * _tanFOV;
        height  = width / aspectRatio;
    }
    else    //height is smaller
    {
        height  = 2.f * nearZ * _tanFOV;
        width   = height * aspectRatio;
    }

    /*
    Formula from OpenGL reference, see function "glFrustum".
        |w      0       0       0|
        |0      h       0       0|
        |0      0       -C      D|
        |0      0       1       0|

    w = 2.f * nearZ / width
    h = 2.f * nearZ / height
    C = -(farZ + nearZ) / (farZ - nearZ)
    D = -2.f * farZ * nearZ / (farZ - nearZ);
    */
    float w = 2.f * nearZ / width;  // Is equal to: [ 1.f / tan(fov*0.5f) ]
    float h = 2.f * nearZ / height; // Is equal to: [ 1.f / tan(fov*0.5f) / aspectRatio ]
    float C = -(farZ + nearZ) / (farZ - nearZ);
    float D = -2.f * farZ * nearZ / (farZ - nearZ);

    //-----------------------
    m00 = w;
    m01 = 0.f;
    m02 = 0.f;
    m03 = 0.f;

    m10 = 0.f;
    m11 = h;
    m12 = 0.f;
    m13 = 0.f;

    m20 = 0.f;
    m21 = 0.f;
    m22 = -C;
    m23 = D;

    m30 = 0.f;
    m31 = 0.f;
    m32 = 1.f;
    m33 = 0.f;
}

void Matrix4f::_getLookAt(Vector3f& pos, Vector3f& lookat, Vector3f& upAxis)
{
    //Note _forward, _right, _up are working vector of type Vector3f

    _up.set(upAxis);
    _forward.sub(lookat, pos);
    _forward.normalize();
    _right.cross(_up, _forward);
    _right.normalize();
    _up.cross(_forward, _right);
    _up.normalize();

    m00 = _right.x;
    m10 = _right.y;
    m20 = _right.z;

    m01 = _up.x;
    m11 = _up.y;
    m21 = _up.z;

    m02 = _forward.x;
    m12 = _forward.y;
    m22 = _forward.z;

    // Locate the camera
    m03 = pos.x;
    m13 = pos.y;
    m23 = pos.z;

    m30 = 0.f;
    m31 = 0.f;
    m32 = 0.f;

    m33 = 1.f;
}

1 个答案:

答案 0 :(得分:1)

没有看到你在顶点着色器中做了什么,我们只能猜测。但是,您可以在新OpenGL中使用与旧OpenGL中相同的矩阵。你可能没有的唯一矩阵是gluPerspective所以你可以自己实现它(如果你不再使用glu.h)。我是这样做的:

void glPerspective(double fovy,double aspect,double zNear,double zFar)
    {
    double per[16],f;
    for (int i=0;i<16;i++) per[i]=0.0;
    // original gluProjection
//  f=divide(1.0,tan(0.5*fovy*deg))
//  per[ 0]=f/aspect;
//  per[ 5]=f;
    // corrected gluProjection
    f=divide(1.0,tan(0.5*fovy*deg*aspect));
    per[ 0]=f;
    per[ 5]=f*aspect;
    // z range
    per[10]=divide(zFar+zNear,zNear-zFar);
    per[11]=-1.0;
    per[14]=divide(2.0*zFar*zNear,zNear-zFar);
    glLoadMatrixd(per);
//  zNear=divide(-per[11],per[10]);     // get znear from perspective projection matrix
    }

它或多或少地模仿了gluPerspective,但它修复了不准确的tan,这会在将更多的frustrums堆叠在一起时造成问题。注释行显示如何计算原始gluPerspective(同时修复tan),最后一条注释显示如何从矩阵中获取znear(对于光线跟踪器)

粗略而非glLoadMatrixd(per);您可以根据需要使用per ...(例如发送为uniform

现在要实现lookat,你只需构建自己的矩阵。参见:

因此,您只需计算X,Y,Z向量和O原点位置

Z = -forward
X = +right
Y = +up
O = +camera_position - znear

并将它们送到您所需位置的矩阵中......

现在,如果您的lookat是目标对象的位置而不是

Z = normalize(camera_position-lookat)
Y = up
X = cross(Y,Z)

不确定X是否应该+或 - 只是尝试并且如果场景被镜像,则否定它。