OpenGL形状不渲染

Posted

技术标签:

【中文标题】OpenGL形状不渲染【英文标题】:OpenGL shape not rendering 【发布时间】: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;

【问题讨论】:

您是冗余状态更改的典型代表。 :) 【参考方案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:修复了缺少的推送/弹出逻辑。

【讨论】:

以上是关于OpenGL形状不渲染的主要内容,如果未能解决你的问题,请参考以下文章

为在 OpenGL 中使用基本物理和渲染寻找一个好的形状类设计

2D OpenGL 场景因大量重叠形状而变慢

OpenGL:渲染具有大量纹理透明度的模型,没有绘制顺序?

Opengl矩形不会渲染

在 OpenGL 中渲染一个*填充的*半透明立方体

在 OpenGL 中渲染 .ply 文件