如何gluLookAt工作?(How does gluLookAt work?)

2019-08-02 04:21发布

从我的理解,

gluLookAt(
        eye_x, eye_y, eye_z,
        center_x, center_y, center_z,   
        up_x, up_y, up_z
    );

相当于:

glRotatef(B, 0.0, 0.0, 1.0);
glRotatef(A, wx, wy, wz);
glTranslatef(-eye_x, -eye_y, -eye_z);

但是,当我打印出来的ModelView矩阵,调用glTranslatef()似乎不正常工作。 以下是代码片段:

#include <stdlib.h>
#include <stdio.h>
#include <GL/glut.h>

#include <iomanip>
#include <iostream>
#include <string>

using namespace std;

static const int Rx = 0;
static const int Ry = 1;
static const int Rz = 2;

static const int Ux = 4;
static const int Uy = 5;
static const int Uz = 6;

static const int Ax = 8;
static const int Ay = 9;
static const int Az = 10;

static const int Tx = 12;
static const int Ty = 13;
static const int Tz = 14;

void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    GLfloat lmodel_ambient[] = { 0.8, 0.0, 0.0, 0.0 };
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
}

void displayModelviewMatrix(float MV[16]) {
    int SPACING = 12;
    cout << left;
    cout << "\tMODELVIEW MATRIX\n";
    cout << "--------------------------------------------------" << endl;
    cout << setw(SPACING) << "R" << setw(SPACING) << "U" << setw(SPACING) << "A" << setw(SPACING) << "T" << endl;   
    cout << "--------------------------------------------------" << endl;
    cout << setw(SPACING) << MV[Rx] << setw(SPACING) << MV[Ux] << setw(SPACING) << MV[Ax]  << setw(SPACING) << MV[Tx] << endl;
    cout << setw(SPACING) << MV[Ry] << setw(SPACING) << MV[Uy] << setw(SPACING) << MV[Ay]  << setw(SPACING) << MV[Ty] << endl;
    cout << setw(SPACING) << MV[Rz] << setw(SPACING) << MV[Uz] << setw(SPACING) << MV[Az] << setw(SPACING)  << MV[Tz] << endl;
    cout << setw(SPACING) << MV[3] << setw(SPACING) << MV[7] << setw(SPACING) << MV[11] << setw(SPACING) << MV[15] << endl;
    cout << "--------------------------------------------------" << endl;
    cout << endl;
}

void reshape(int w, int h) {
    float ratio = static_cast<float>(w)/h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, ratio, 1.0, 425.0);
}

void draw() {
    float m[16];
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glGetFloatv(GL_MODELVIEW_MATRIX, m);
    gluLookAt(
        300.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f
    );
    glColor3f(1.0, 0.0, 0.0);
    glutSolidCube(100.0);
    glGetFloatv(GL_MODELVIEW_MATRIX, m);
    displayModelviewMatrix(m);
    glutSwapBuffers();
}


int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(400, 400);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Demo");
    glutReshapeFunc(reshape);
    glutDisplayFunc(draw);
    init();
    glutMainLoop();
    return 0;
} 

不管我用什么值的eye矢量:
300, 0, 0
0, 300, 0
0, 0, 300
平移矢量是相同的,这并没有任何意义,因为代码的顺序是在向后的顺序,以便glTranslatef应该首先运行,则2个旋转。 另外,旋转矩阵,是完全独立的翻译列(模型视图矩阵),那么什么会导致此怪异的行为? 下面是与眼睛矢量的输出(0.0f, 300.0f, 0.0f)

        MODELVIEW MATRIX
--------------------------------------------------
R           U           A           T
--------------------------------------------------
0           0           0           0
0           0           0           0
0           1           0           -300
0           0           0           1
--------------------------------------------------

我期望T列是(0, -300, 0) 所以,任何人都可以帮我解释一下吗?

实施gluLookAt从http://www.mesa3d.org

void GLAPIENTRY
gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx,
      GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy,
      GLdouble upz)
{
    float forward[3], side[3], up[3];
    GLfloat m[4][4];

    forward[0] = centerx - eyex;
    forward[1] = centery - eyey;
    forward[2] = centerz - eyez;

    up[0] = upx;
    up[1] = upy;
    up[2] = upz;

    normalize(forward);

    /* Side = forward x up */
    cross(forward, up, side);
    normalize(side);

    /* Recompute up as: up = side x forward */
    cross(side, forward, up);

    __gluMakeIdentityf(&m[0][0]);
    m[0][0] = side[0];
    m[1][0] = side[1];
    m[2][0] = side[2];

    m[0][1] = up[0];
    m[1][1] = up[1];
    m[2][1] = up[2];

    m[0][2] = -forward[0];
    m[1][2] = -forward[1];
    m[2][2] = -forward[2];

    glMultMatrixf(&m[0][0]);
    glTranslated(-eyex, -eyey, -eyez);
}

Answer 1:

如果我们让一个旋转和平移矩阵喜欢你的模型视图矩阵

Rxx Rxy Rxz Tx 
Ryx Ryy Ryz Ty 
Rzx Rzy Rzz Tz 
 0   0   0   1 

行为上的任意矢量

x
y
z
1

我们得到

Rxx x + Rxy y + Rxz z  +  Tx 
Ryx x + Ryy y + Ryz z  +  Ty
Rzx x + Rzy y + Rzz z  +  Tz
1

(我在写东西,所以向量获得左侧乘以矩阵)。

这表明矩阵的平移组件给做旋转应用翻译。 这就是为什么他们是不一样的(-eye_x,-eye_y,-eye_z)载体,因为正如你所指出正被旋转以前做过翻译。

之所以说翻译总是沿着-z方向是因为在视图帧-z方向分朝向中心。 因为你总是有中心从眼睛300台,所有你的眼睛的位置把在(0,0,-300)中心在视图帧。 因此,由于中心开始在原点,我们做任何翻译之前,翻译给它正确的合作orindates必须为(0,0,-300)。

此外,您可能已经注意到了这一点,但你展示模型视图矩阵是病理性的,因为你有向上向量沿着视线方向指向(从眼睛到中心)。 这也解释了为什么它具有零中的两个全行。



Answer 2:

“我对旋转如何使用向前,向上和侧面载体在此代码...进行非常困惑:”我想你应该知道一些有关“UVN相机”。还有一些理论关于坐标两个坐标系之间进行转换。在上述examle,这两个坐标是世界坐标系和摄像机坐标。 其结果是:X

N - 从目标到相机的向量。 也被称为在一些3D文献载体“在外表”。 这个矢量对应于-Z斧。

N - 在笔直站立时,这是从你的头向天空的载体。 如果你正在写一个飞行模拟器和平面扭转了矢量很可能指向地面。 这个矢量对应于Y斧。

ü - 从相机到它的“右”侧”这个向量指向它对应于X斧。



Answer 3:

@Andon M.科尔曼 - 如何上图中行专业? 具有行或列矩阵主要是关于2D结构的一维存储器中的存储器内表示和无关与4×4变换矩阵的上面的图。

如果向量U,V,N是你似乎暗示写成列,那么你将有一个摄像头,空间,世界空间变换。

然而,输入到矩阵是世界空间位置和所述输出是基于相机的空间中的位置,因此所述矩阵是变换世界空间到摄像机空间。

U,V,N对调的原因是因为这是通过使用正交矩阵,其中他们反过​​来也是它的转置的属性,您建议矩阵和逆。 也就是说,我们写的U,V,N向量行获得世界空间与摄像机空间变换和U,V,N的列来获得相机的空间世界空间变换。

此外,与右侧的世界位置相乘的选择是因为图是使用列向量。 如果我们用行向量,我们会留下大量繁殖。 它无关,与我们如何矩阵存储在内存中,有一切都与我们如何将两个矩阵相乘在一起,就是做我们我们的矢量转换成1X4或4X1矩阵,我们用4x4矩阵相乘之前。

总之,上面的图是好的,它只是从一个空间到另一个的转换,请不要谈内存布局这是一种编程细节混淆的问题。



文章来源: How does gluLookAt work?