【问题标题】:Am I creating look at and perspective matrices correcly?我是否正确地创建了观察矩阵和透视矩阵?
【发布时间】:2016-04-24 08:47:09
【问题描述】:

我已经编写了 OpenGL 超级圣经的例子 https://github.com/openglsuperbible/sb7code/blob/master/src/grass/grass.cpp 与 C 和 freeglut。结果是点在视图之外。

我是否正确计算和使用观察矩阵和透视矩阵?

部分渲染功能:

Matrix proj=CreateProjectionMatrix(45.0f,CurrentWidth/CurrentHeight,0.1f,1000.0f);

float t=glutGet(GLUT_ELAPSED_TIME)*0.002f;
float eye[]={sinf(t)*550.0f,25.0f,cosf(t)*550.0f};
float center[]={0.0f,-50.0f,0.0f};
float up[]={0.0f,1.0f,0.0f};
Matrix look=lookAt(eye,center,up);

Matrix final=MultiplyMatrices(&proj,&look);

glUniformMatrix4fv(UniformsMvpMatrix,1,GL_FALSE,final.m);

计算:

typedef struct Matrix{
  GLfloat m[16];
}Matrix;

Matrix CreateProjectionMatrix(float fovy,float aspect,float Znear,float Zfar){
    // Array index in the matrix
    //  _          _
    // | 0 4  8  12 |
    // | 1 5  9  13 |
    // | 2 6 10  14 |
    // |_3 7 11  15_|
    //
    Matrix out;
    float tanhalffovy = tanf(DegreesToRadians(fovy/2));
    out.m[ 0]=1/(aspect*tanhalffovy);
    out.m[ 5]=1/(tanhalffovy);
    out.m[10]=(Zfar+Znear)/(Znear-Zfar);
    out.m[11]=(2*Zfar*Znear)/(Znear-Zfar);
    out.m[14]=-1;
    out.m[ 1]=0;
    out.m[ 2]=0;
    out.m[ 3]=0;
    out.m[ 4]=0;
    out.m[ 6]=0;
    out.m[ 7]=0;
    out.m[ 8]=0;
    out.m[ 9]=0;
    out.m[12]=0;
    out.m[13]=0;
    out.m[15]=0;
    return out;
}

Matrix lookAt(float eye[3],float center[3],float up[3]){
    // Array index in the matrix
    //  _          _
    // | 0 4  8  12 |
    // | 1 5  9  13 |
    // | 2 6 10  14 |
    // |_3 7 11  15_|
    //
    double help=sqrt(up[0]*up[0]+up[1]*up[1]+up[2]*up[2]);
    float upN[3];
    upN[0]=up[0]/help;
    upN[1]=up[1]/help;
    upN[2]=up[2]/help;

    float f[3];
    f[0]=(center[0]-eye[0]);
    f[1]=(center[1]-eye[1]);
    f[2]=(center[2]-eye[2]);
    help=sqrt(f[0]*f[0]+f[1]*f[1]+f[2]*f[2]);
    f[0]/=help;
    f[1]/=help;
    f[2]/=help;

    float s[3];
    s[0]=f[1]*upN[2]-upN[1]*f[2];
    s[1]=f[2]*upN[0]-upN[2]*f[0];
    s[2]=f[0]*upN[1]-upN[0]*f[1];

    float u[3];
    u[0]=s[1]*f[2]-f[1]*s[2];
    u[1]=s[2]*f[0]-f[2]*s[0];
    u[2]=s[0]*f[1]-f[0]*s[1];

    Matrix out={{s[0],s[1],s[2],0, u[0],u[1],u[2],0, f[0],f[1],f[2],0, -eye[0],-eye[1],-eye[2],1}};
   return out;
}

Matrix MultiplyMatrices(const Matrix* m1, const Matrix* m2){    
    Matrix out;
    // First column
    out.m[ 0]=(m1->m[0])*(m2->m[0])+(m1->m[4])*(m2->m[1])+(m1->m[ 8])*(m2->m[2])+(m1->m[12])*(m2->m[3]);
    out.m[ 1]=(m1->m[1])*(m2->m[0])+(m1->m[5])*(m2->m[1])+(m1->m[ 9])*(m2->m[2])+(m1->m[13])*(m2->m[3]);
    out.m[ 2]=(m1->m[2])*(m2->m[0])+(m1->m[6])*(m2->m[1])+(m1->m[10])*(m2->m[2])+(m1->m[14])*(m2->m[3]);
    out.m[ 3]=(m1->m[3])*(m2->m[0])+(m1->m[7])*(m2->m[1])+(m1->m[11])*(m2->m[2])+(m1->m[15])*(m2->m[3]);
    // Second column
    out.m[ 4]=(m1->m[0])*(m2->m[4])+(m1->m[4])*(m2->m[5])+(m1->m[ 8])*(m2->m[6])+(m1->m[12])*(m2->m[7]);
    out.m[ 5]=(m1->m[1])*(m2->m[4])+(m1->m[5])*(m2->m[5])+(m1->m[ 9])*(m2->m[6])+(m1->m[13])*(m2->m[7]);
    out.m[ 6]=(m1->m[2])*(m2->m[4])+(m1->m[6])*(m2->m[5])+(m1->m[10])*(m2->m[6])+(m1->m[14])*(m2->m[7]);
    out.m[ 7]=(m1->m[3])*(m2->m[4])+(m1->m[7])*(m2->m[5])+(m1->m[11])*(m2->m[6])+(m1->m[15])*(m2->m[7]);
    // Third column
    out.m[ 8]=(m1->m[0])*(m2->m[8])+(m1->m[4])*(m2->m[9])+(m1->m[ 8])*(m2->m[10])+(m1->m[12])*(m2->m[11]);
    out.m[ 9]=(m1->m[1])*(m2->m[8])+(m1->m[5])*(m2->m[9])+(m1->m[ 9])*(m2->m[10])+(m1->m[13])*(m2->m[11]);
    out.m[10]=(m1->m[2])*(m2->m[8])+(m1->m[6])*(m2->m[9])+(m1->m[10])*(m2->m[10])+(m1->m[14])*(m2->m[11]);
    out.m[11]=(m1->m[3])*(m2->m[8])+(m1->m[7])*(m2->m[9])+(m1->m[11])*(m2->m[10])+(m1->m[15])*(m2->m[11]);
    // Fourth
    out.m[12]=(m1->m[0])*(m2->m[12])+(m1->m[4])*(m2->m[13])+(m1->m[ 8])*(m2->m[14])+(m1->m[12])*(m2->m[15]);
    out.m[13]=(m1->m[1])*(m2->m[12])+(m1->m[5])*(m2->m[13])+(m1->m[ 9])*(m2->m[14])+(m1->m[13])*(m2->m[15]);
    out.m[14]=(m1->m[2])*(m2->m[12])+(m1->m[6])*(m2->m[13])+(m1->m[10])*(m2->m[14])+(m1->m[14])*(m2->m[15]);
    out.m[15]=(m1->m[3])*(m2->m[12])+(m1->m[7])*(m2->m[13])+(m1->m[11])*(m2->m[14])+(m1->m[15])*(m2->m[15]);

    return out;
}

我 99.9% 确定着色器与示例中的相同。

【问题讨论】:

    标签: c opengl matrix


    【解决方案1】:

    我发现您的代码有两个问题:

    1. 目前还不清楚您使用的是哪种矩阵存储布局约定。

      您的lookAt 代码将-eye 转换为元素12 到14,这意味着您在着色器中使用列主要布局和标准GL 的matrix * vector 乘法顺序。但是,您的投影矩阵被定义为转换为该约定。元素 11 和 14 应该交换。

    2. 您的lookAt 函数错误:

      视图矩阵应将世界空间转换为相机位于中心并朝向-z 方向的眼睛空间。这可以解释为相对于固定相机移动所有对象,或者定义为相对于世界坐标的新视图坐标系。在这两种情况下,您最终都会得到公式 R * T(-eye)。在第一种解释中,这意味着我们首先将所有对象移动-eye(使点eye 最终到达中心),然后围绕新中心旋转,使得lookAt 方向旋转到@987654329 @ 轴,以及 up 向量到 +y

      但是,您的 lookAt 矩阵不会这样做。您的矩阵可以分解为T(-eye) * R^-1。所以首先,您的R 不是您需要的旋转:如果您将矩阵乘以-z 轴向量(0,0,-1,0)^T,您只需选择第三列并将其取反,因此您最终会得到-f。但是你的正向向量应该是f,所以你在把它放入矩阵之前忘记了否定f

      第二个问题是旋转实际上是反转。如果你否定第三列,你最终会得到V * (0 0 -1 0)^T = (f 0)^T,但这是你需要的相反的转换方向。您必须构建一个导致V * (f 0)^T = (0 0 -1 0) 的轮换。现在旋转矩阵的好处是它们是正交的,所以R^-1 = R^T。这意味着你可以简单地转置你的旋转矩阵来反转它。

      但是,您不能只转置视图矩阵。这将我们带到最后一个问题:您只需将翻译部分-eye 写入第四列。从概念上讲,这意味着您执行V=T(-eye) * R,也就是说,您将翻译应用为last 步骤。但是,正如我们之前所见,我们必须首先应用翻译。所以要完全修复你的矩阵,你首先构建R^T,通过转置旋转部分,即将su-f向量作为rows写入矩阵,最后你将该矩阵乘以带有T(-eye) 的简单平移矩阵。

    【讨论】: