0

我正在尝试在 OpenGL 中渲染一个球体。不幸的是,屏幕总是只显示背景。我尝试重新定位相机无济于事(尽管我可能做错了)。任何见解将不胜感激。

这是 OpenGL 调用来更新屏幕的函数:

//The draw function - I have confirmed that this is called periodically.
void draw()
{
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glEnable(GL_CULL_FACE);
    glPolygonMode(GL_FRONT_AND_BACK, (globals.wireframe == true) ? GL_LINE : GL_FILL);

    glViewport(0, 0, globals.window_size.x, globals.window_size.y);

    mat4 prj = perspective(globals.fov, float(globals.window_size.x) / float(globals.window_size.y), globals.hither, globals.yon);
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(value_ptr(prj));

    glMatrixMode(GL_MODELVIEW);
    mat4 context = mat4(1.0f);
    globals.ship.draw(context); //the ship only draws a sphere for now
    mat4 mv = lookAt(vec3(0.0f, 0.0f, -5.5f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
    glLoadMatrixf(value_ptr(mv));

    glutSwapBuffers();
}

以及 Ship 的定义:

GLuint Ship::sphere_handle; //static
bool Ship::is_initialized; //static

mat4 Ship::draw(mat4 context) const {
    glLoadMatrixf(value_ptr(context));
    glCallList(sphere_handle);
    return context;
}

//called when program starts, after window created
bool Ship::initialize() {
    if (sphere_handle == BAD_GL_VALUE)
    {
        GLUquadric *q = gluNewQuadric();
        if (q != NULL)
        {
            if ((sphere_handle = glGenLists(1)) == 0)
            {
                cout << "Model::Initialize() - Failed to GenLists()" << endl;
                return false;
            }
            glNewList(sphere_handle, GL_COMPILE);
            gluSphere(q, 1.0, 10, 10);
            glEndList();
            gluDeleteQuadric(q);
            is_initialized = true;
        }
        else
        {
            return false;
        }
    }
    return true;
}
4

2 回答 2

2

The first to always check is if there are any GL errors! If so, find the cause and fix it.

Other than that, I see something that seems to either stem from misunderstanding how matrix manipulation in legacy GL works or you didn't write what you intended to write. In your draw() function you do:

glMatrixMode(GL_MODELVIEW);
mat4 context = mat4(1.0f);
globals.ship.draw(context); //the ship only draws a sphere for now
mat4 mv = lookAt(vec3(0.0f, 0.0f, -5.5f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
glLoadMatrixf(value_ptr(mv));

On the third line you call

mat4 Ship::draw(mat4 context) const 
{
  glLoadMatrixf(value_ptr(context));
  glCallList(sphere_handle);
  return context;
}

You load the identity onto the top of the MODELVIEW stack and then call glCallList. At this point, there is no translation involved, i.e. conceptually your camera is right inside the unit sphere. Due to backface culling you will not see a thing. What you actually want is to translate the sphere back 5.5 units so you are looking straigt down -Z, directly at the center of the sphere:

glMatrixMode(GL_MODELVIEW);
// load the view matrix FIRST! 
mat4 mv = lookAt(vec3(0.0f, 0.0f, -5.5f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
glLoadMatrixf(value_ptr(mv));
// draw the model with an indentity model-matrix - mind the changes to the function below!!
glPushMatrix(); // you don't want to mess up the view matrix for good
globals.ship.draw(mat4(1.0f));
glPopMatrix();

Rearranging calls will not cut it. Ship::draw() will have to be altered as well. First, you can improve the function from a design standpoint: don't take a matrix by value, take it by const reference, since the draw function doesn't and shouldn't alter the model-matrix at all. Second, you need to apply correct matrix multiplication if you're planning to use non-indentity model-matrices at some point. Why the function needs to return a copy of the model-matrix you passed in, I frankly cannot fathom at all. :) My suggestion is to rewrite it as follows:

void Ship::draw(const mat4& context) const
{
  glMultMatrixf(value_ptr(context));
  glCallList(sphere_handle);
}

After that, there's one more thing to do. You translate the sphere 5.5 units along +Z (i.e. you set the eye-point of your camera to (0, 0, -5.5) ). Since you don't rotate the sphere, there is no way the camera will end up visible after projection and will simply be clipped completely. What you probably wanted is to set the eye-point to (0, 0, 5.5).

On another note, you incur unnecessary overhead at various places in your draw function:

glClearColor(1.0f, 0.0f, 0.0f, 1.0f);

this is very infrequently changing state, which should be called only at init time or when you really need to change it. Calling it in the render loop is mostly completely unnecessary.

So are

glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT_AND_BACK, (globals.wireframe == true) ? GL_LINE : GL_FILL);

For changing the viewport, windowing frameworks like Qt or FreeGLUT provide a special resize callback. Put the function in there and update your projection in there as well (since changing the dimensions will often lead to a change in aspect ratio).

EDIT: Please mind datenwolf's remark and move away from legacy GL. If you're using GLM already, you're already one step closer to modern OpenGL. :)

Check out Jason McKesson's tutorial and our wiki at OpenGL.org for a vast collection of information on modern OpenGL.

EDIT2: Fixed missing push/pop logic.

于 2013-09-13T10:18:06.460 回答
2

主要问题在这里:

glMatrixMode(GL_MODELVIEW);
mat4 context = mat4(1.0f);
globals.ship.draw(context); //the ship only draws a sphere for now
mat4 mv = lookAt(vec3(0.0f, 0.0f, -5.5f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
glLoadMatrixf(value_ptr(mv));

OpenGL 是基于状态的绘图 API,而不是场景图。当您绘制某些东西时,OpenGL 会采用当前状态(矩阵等)并使用这些状态将点、线或三角形放在屏幕上。在上面的代码中,您在计算视图变换之前绘制了船。

再加上 glLoadMatrix 现在只是替换矩阵堆栈上的任何内容。因此,当您连续加载多个转换时,它们不会复合。尝试不依赖 OpenGL 矩阵数学函数实际上很棒(它们已被弃用并从更高版本的 OpenGL 中删除)。那么如何改变呢?您必须创建一个复合模型视图矩阵。因此,将上面的内容替换为:

mat4 view = lookAt(
    vec3(0.0f, 0.0f, -5.5f),
    vec3(0.0f, 0.0f, 0.0f),
    vec3(0.0f, 1.0f, 0.0f));
globals.ship.draw(view);

瞬间,矩阵模式和矩阵负载去哪了?离开,我们这里不需要它们。但是你必须稍微调整你的船图代码。

void Ship::draw(mat4 view) const {

    /* transform depending on your ships location and orientation */
    mat4 model = …;

    /* order of operations matters. OpenGL uses column major indexing
     * hence matrix multiplication is right associative, i.e. column
     * vectors (like vertex positions) enter on the right and "go"
     * through the compound toward the left.
     *   Since model is the first transformation to apply, and view
     * after the multiplication for the whole compound transform is */
    mat4 mv = view * model;

    glLoadMatrixf(value_ptr(mv));

因此,您竭尽全力避免使用陈旧且尘土飞扬的 OpenGL 矩阵数学函数(这很棒);您只需要用基于着色器的现代方法中的调用替换所有glLoadMatrix调用。glUniform

    glCallList(sphere_handle);
}

但是你为什么在这里使用这么旧的分解显示列表呢?*Yuck* 它们在现代 OpenGL 中也不可用,但不能像矩阵代码那样简单地迁移出去。

在这里使用顶点数组。

于 2013-09-13T10:15:01.790 回答