OpenGL - 快速纹理四边形?

5

我希望能在三维空间中随机位置显示尽可能多的纹理四边形。但是根据我的经验,即使只显示几千个四边形,fps也会明显下降到30以下(我的相机移动脚本变得很卡)。

目前我正在遵循一份古老的教程。在初始化OpenGL之后:

glEnable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

我设置了视点和透视:

glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

接下来我加载一些纹理:

glGenTextures(TEXTURE_COUNT, &texture[0]);
for (int i...){
    glBindTexture(GL_TEXTURE_2D, texture[i]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    gluBuild2DMipmaps(GL_TEXTURE_2D,3,TextureImage[0]->w,TextureImage[0]->h,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0]->pixels);
}

最后,我使用以下代码绘制GL_QUADS:
glBindTexture(GL_TEXTURE_2D, q);
glTranslatef(fDistanceX,fDistanceZ,-fDistanceY); 
glBegin(GL_QUADS);
    glNormal3f(a,b,c);
    glTexCoord2f(d, e); glVertex3f(x1, y1,  z1);
    glTexCoord2f(f, g); glVertex3f(x2, y2,  z2);
    glTexCoord2f(h, k); glVertex3f(x3, y3,  z3);
    glTexCoord2f(m, n); glVertex3f(x4, y4,  z4);
glEnd();
glTranslatef(-fDistanceX,-fDistanceZ,fDistanceY);

我认为所有的代码都非常自解释。不幸的是,这种做法已经被弃用了,据我所知。我在互联网上读到一些关于PBO和vertexArrays的模糊信息,但我没有找到任何有关如何使用它们的教程。我甚至不知道这些对象是否适合实现我在这里尝试做的事情(在屏幕上显示十亿个四边形而没有延迟)。也许这里的任何人都可以给我一个明确的建议,告诉我应该使用什么来实现结果?如果你恰好还有一分钟的空闲时间,能否给我一个简短的摘要,介绍一下这些函数是如何使用的(就像我上面介绍过的被弃用的函数)?

你可以考虑学习显示列表和顶点缓冲对象。 - Alexandre C.
1
@AlexandreC。这些都不会有所帮助。他的问题是状态变化,缓冲对象和显示列表都无法解决。 - Nicol Bolas
2个回答

8

也许这里的任何人都可以给我一个明确的建议,我应该使用什么来实现结果?

什么是“结果”?您没有很好地解释您想要实现的确切内容。您所说的只是您正在尝试绘制许多纹理四边形。您想用这些纹理四边形做什么?

例如,您似乎正在创建相同的纹理,具有相同的宽度和高度,给定相同的像素数据。但是,您将它们存储在不同的纹理对象中。OpenGL不知道它们包含相同的数据。因此,当您渲染四边形时,您浪费了大量时间交换纹理。

如果您只是随机绘制它们以测试性能,则问题毫无意义。这样的测试是无意义的,因为它们完全是人为的。它们仅测试您每次渲染四边形时更改纹理的人为情况。

如果不知道您最终要渲染什么,那么我唯一能做的就是提供一般的性能建议。按顺序(即:先执行前面的操作):

  1. 停止为每个四边形更改纹理。您可以将多个图像打包到同一纹理中,然后使用该纹理渲染所有使用该纹理的四边形,只需一次glBindTexture调用即可。四边形的纹理坐标指定其在纹理中使用的图像。

  2. 停止使用glTranslate来定位每个单独的四边形。您可以使用它来定位四边形组,但应自己计算四边形的顶点位置。一旦这些glTranslate调用消失,您就可以将多个四边形放置在单个glBegin/glEnd对的空间内。

  3. 假设您的四边形是静态的(在模型空间中固定位置),请考虑使用缓冲区对象存储和渲染您的四边形数据。

我在互联网上读到了一些关于PBO和vertexArrays的模糊信息,但我没有找到任何关于如何使用它们的教程。

你试过OpenGL Wiki吗?它有一个非常好的教程列表(以及关于OpenGL的一般信息)。为了充分披露,我写了其中一个。


哇!使用不同的图像区域在一张图片中存储多个纹理的想法太棒了!我会尝试的。 - Fejwin
此外,正如您建议的那样,应尽可能由 CPU 处理任何功能,以便 GPU 只需进行渲染本身,因此应该摆脱 glTranslate。 - Fejwin
非常感谢教程列表!在谷歌上直接找不到它们,而且对于经验不足的搜索者来说,OpenGL维基百科又太庞大而难以理解。 - Fejwin
@Fejwin:摆脱glTranslate的原因更多是你不必为每个四边形做一次glBegin/glEnd。你可以在单个glBegin/glEnd调用中渲染许多四边形。此外,Wiki上有一个“入门”页面,直接从Wiki的主页链接过来。它有我链接到的教程列表。所以并不难找到。 - Nicol Bolas
哦,好的,现在我明白了! 我想知道的是,我的性能问题是否集中在我展示四边形的最后一部分代码中,或者我在初始化中使用了一些已弃用的函数?或者我的加载(和存储)纹理的方式已经过时了吗?有什么需要改变的吗? - Fejwin

7
我听说现代游戏中实时渲染的多边形数目在百万级别。
实际上是百万级别。我猜你是德国人:“Milliarde”在英语中翻译为“Billion”。
你现在遵循的是一份古老的教程。
这是你的主要问题。当代OpenGL应用程序不使用古老的渲染方法。你正在使用立即模式,这意味着你需要通过多个函数调用来提交一个单独的顶点。这是非常低效的。现代应用程序(如游戏)可以达到那么高的三角形数量,因为他们不浪费CPU时间调用太多的函数,他们也不会浪费CPU→GPU带宽与数据流。
要实现实时渲染那么高数量的三角形,你必须将所有几何数据放置在“快速内存”中,即放在显卡RAM中。OpenGL提供的这种技术称为“顶点缓冲对象”。使用VBO,您可以使用单个绘制调用(glDrawArrays、glDrawElements及其相关函数)绘制大批量的几何图形。
在处理完几何图形之后,你必须对GPU友好。GPU不喜欢经常切换纹理或着色器。切换纹理会使缓存内容无效,而切换着色器则意味着停止GPU流水线,更糟糕的是,它意味着无效化执行路径预测统计数据(GPU会记录哪些着色器执行路径最可能被执行以及其内存访问模式,这用于迭代优化着色器执行)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接