【问题标题】:OpenGL shape not renderingOpenGL形状不渲染
【发布时间】:2013-09-13 01:25:15
【问题描述】:

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

【问题讨论】:

  • 您是冗余状态更改的典型代表。 :)

标签: c++ opengl glm-math freeglut


【解决方案1】:

主要问题出在这里:

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

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

这里使用顶点数组。

【讨论】:

    【解决方案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 引用来获取,因为 draw 函数根本不也不应该改变模型矩阵。其次,如果您计划在某个时候使用非标识模型矩阵,则需要应用正确的矩阵乘法。为什么函数需要返回您传入的模型矩阵的副本,坦率地说,我根本无法理解。 :) 我的建议是改写如下:

    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:修复了缺少的推送/弹出逻辑。

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 2021-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-12
      • 2020-04-05
      • 2017-02-04
      相关资源
      最近更新 更多