I am attempting to render a sphere in OpenGL. Unfortunately, the screen always comes up with only the background. I've tried reorienting the camera to no avail (although I may be doing it wrong). Any insight would be appreciated.

Here is the function OpenGL calls to update the screen:

//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();
}

And the definition of 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;
}
有帮助吗?

解决方案

The main problem lies here:

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 is a state based drawing API, not a scene graph. When you draw something, OpenGL takes the current state (matrices, etc.) and uses those to put points, lines, or triangles to the screen. In the above code, you're drawing the ship before you bother to calculate a view transform.

Add to that that glLoadMatrix simply replaces whatever is on the matrix stack right now. So when you load several transformations in succession, they won't compound. It's actually great that you try to not rely on the OpenGL matrix math functions (they've been deprecated and removed from later versions of OpenGL). So how to change this? You must create a compound modelview matrix. So replace the above with this:

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);

Moment, where'd the matrix mode and matrix load go? Away, we don't need them here. But you must slightly adjust your ship drawing code.

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));

So you went to great length to avoid using old and dusted OpenGL matrix math functions (which is great); you just have to replace all glLoadMatrix calls with glUniform calls in a modern, shader based approach.

    glCallList(sphere_handle);
}

But why are you using the so-old-that-it's-decomposing display lists here? *Yuck* They're not available in modern OpenGL either, but can't be as trivially migrated away from, as with your matrix code.

Use a vertex array here.

其他提示

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top