【问题标题】:Migrate from fixed-function to GLSL OpenGL: How should I modify my view (lookAt) matrix and perspection matrix?从固定功能迁移到 GLSL OpenGL:我应该如何修改我的视图 (lookAt) 矩阵和透视矩阵?
【发布时间】:2025-11-27 22:25:06
【问题描述】:

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

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

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

很少有stack overflow threads建议对于shader pipeline,我们需要手动翻转投影矩阵的Z值,但是视图矩阵呢?非常感谢您的任何建议。

更新: 源代码

/*
    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.我的 4x4 矩阵是行主导的,我在发送到着色器时已将它们转置(如果我忘记了,渲染结果是黑屏) 2. 当我在着色器中乘以矩阵时,我这样做了:mat4 mat_vm = mat_view * mat_model; mat4 mat_pvm = mat_proj * mat_vm;
  • 按照其他帖子的建议,在我计算投影矩阵后,我否定 z 值:m02*-1、-m12*-1、-m22*-1、m32*-1,所以只是喜欢你的建议。但是我开始看到黑屏,直到我随机否定视图矩阵中的一些值以使其出现。但是我不知道调整视图矩阵的正确方法?
  • 没有看到你在顶点着色器中做什么,我们只能猜测......

标签: c++ opengl glsl shader


【解决方案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,它在将更多的截锥体堆叠在一起时会导致问题。注释行显示如何计算原始gluPerspective(也修复了tan),最后一条注释显示如何从矩阵获取znear(用于光线追踪器)

你可以根据需要使用per 来代替glLoadMatrixd(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 应该是 + 还是 - 只是尝试,如果场景被镜像然后否定它。

【讨论】:

  • 虽然在看到您的回答之前我已经解决了我的问题,但您的信息非常有用,谢谢!我的问题是在我的lookAt()函数中生成的pos-lookat-axis矩阵是相机的世界变换矩阵,我应该使用它的逆矩阵将顶点着色器作为“视图”矩阵传递。
最近更新 更多