我试图在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;
}
答案 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 :修复了丢失的推/弹逻辑。