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形状不渲染的主要内容,如果未能解决你的问题,请参考以下文章