OpenGL形状无法渲染

时间:2013-09-13 01:25:15

标签: c++ opengl glm-math freeglut

我试图在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;
}

2 个答案:

答案 0 :(得分: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中也不具备,但不能像矩阵代码那样平凡地迁移。

在这里使用顶点数组。

答案 1 :(得分:2)

第一个始终检查是否有任何GL错误!如果是这样,找到原因并修复它。

除此之外,我看到的一些东西似乎要么是因为误解了传统GL中的矩阵操作是如何工作的,要么是你没有写出你想写的东西。在draw()函数中执行:

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

在你打电话的第三行

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

您将标识加载到MODELVIEW堆栈的顶部,然后调用glCallList。此时,没有涉及翻译,即概念上您的相机正好在单位范围内。由于背面剔除,你不会看到任何东西。你真正想要的是将球体转换回5.5个单位,这样你就可以直接看到-Z,直接在球体的中心:

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

重新排列电话不会削减它。 Ship::draw()也必须改变。首先,您可以从设计角度改进函数:不要通过值取矩阵,通过const引用来获取,因为绘制函数根本不会也不应该改变模型矩阵。其次,如果您计划在某个时刻使用非缩进模型矩阵,则需要应用正确的矩阵乘法。为什么函数需要返回你传入的模型矩阵的副本,坦白说我根本无法理解。 :)我的建议是重写如下:

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

之后,还有一件事要做。您将球体沿+ Z平移5.5个单位(即您将相机的视点设置为(0,0,-5.5))。由于您不旋转球体,因此在投影后相机无法最终显示,并且只能完全剪裁。您可能想要的是将眼点设置为(0,0,5.5)。

另一方面,您在绘图功能的不同位置会产生不必要的开销:

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

这种情况很少发生变化,只应在初始时或真正需要更改状态时调用。在渲染循环中调用它几乎是完全没必要的。

所以

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

为了更改视口,像Qt或FreeGLUT这样的窗口框架提供了一个特殊的调整大小回调。将功能放在那里并同时更新您的投影(因为更改尺寸通常会导致宽高比发生变化)。

编辑:请注意datenwolf的评论并远离旧版GL。如果您已经使用了GLM,那么您已经离现代OpenGL更近了一步。 :)

在OpenGL.org上查看Jason McKesson的tutorial和我们的wiki,了解有关现代OpenGL的大量信息。

EDIT2 :修复了丢失的推/弹逻辑。