【问题标题】:Is there a limit of object in openGL?openGL中的对象有限制吗?
【发布时间】:2021-06-05 22:39:44
【问题描述】:

我想在 Visual C++ 中使用 OpenGL 绘制 2000 个球体。 以下代码绘制了 1000 个球体,结果看起来不错。

但是当我增加 2000 的球体数量时(参见下面的部分代码并用 ** 突出显示),它失败了。 出现以下错误信息。

“freeglut : fgInitGL2 : fghGenBuffers 为 NULL”

你能帮我解决这个问题吗?

    for (int j = 0; j < 10; j++) {
        for (int k = 0; k < 10; k++) {
            **for (int l = 0; l < 20; l++) { \\ for (int l = 0; l < 10; l++)**
                glPushMatrix();
                glTranslatef(j, k, l);
                gluSphere(myQuad, 0.5, 100, 100);
                glPopMatrix();
            }
        }
    }

这是一个完整的测试代码。

#include <GL/glew.h>
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fstream>
#include <string>
#include <cstring>
#include <cmath>
#include <iostream>

int selectedObject = 1;
bool drawThatAxis = 0;
bool lightEffect = 1;

float fovy = 60.0, aspect = 1.0, zNear = 1.0, zFar = 100.0;

float depth = 8;
float phi = 0, theta = 0;
float downX, downY;
bool leftButton = false, middleButton = false;

GLfloat white[3] = { 1.0, 1.0, 1.0 };

void displayCallback(void);

GLdouble width, height;
int wd;

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH);
    glutInitWindowSize(800,600);

    wd = glutCreateWindow("3D Molecules");

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
    glClearColor(0.0, 0.0, 0.0, 0.0);

    GLuint id;
    id = glGenLists(1);

    GLUquadric* myQuad;
    myQuad = gluNewQuadric();

    glNewList(id, GL_COMPILE);
    
    for (int j = 0; j < 10; j++) {
        for (int k = 0; k < 10; k++) {
            for (int l = 0; l < 10; l++) {
                glPushMatrix();
                glTranslatef(j, k, l);
                gluSphere(myQuad, 0.5, 100, 100);
                glPopMatrix();
            }
        }
    }
    
    glEndList();

    glutDisplayFunc(displayCallback);
    glutMainLoop();
    return 0;
}
void displayCallback(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(fovy, aspect, zNear, zFar);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(0, 0, 40, 0, 0, 0, 0, 1, 0);

    glTranslatef(0.0, 0.0, -depth);
    glRotatef(-theta, 1.0, 0.0, 0.0);
    glRotatef(phi, 0.0, 1.0, 0.0);

    if (lightEffect) {
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
    }
    else
    {
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
    }

    switch (selectedObject)
    {
    case (1):
        glCallList(1);
        break;
    default:
        break;
    }

    glFlush();
}

【问题讨论】:

  • 这取决于你的 VRAM。
  • @AllMightyGoat 我认为这不太可能;显示列表早在硬件 TnL 出现之前就已经不常用了。作者会达到其他一些限制,可能是任意的。

标签: c++ opengl freeglut


【解决方案1】:

openGL 中的对象有限制吗?

OpenGL 没有定义“最大对象数”之类的限制。 应用程序能够在 CPU 内存 中尽可能多地绘制对象,但通常在应用程序达到内存限制之前,绘制速度会变得非常慢。即使所有纹理和顶点数据都无法放入 GPU 内存,OpenGL 仍然不会失败并通过在每一帧上不断上传 CPU->GPU 内存来继续绘制。

因此,如果我们回到 OpenGL 限制的问题 - 确实存在内存限制,正如您可能从另一个 similar question 看到的那样。您的代码实际上并未使用glGetError() 检查任何OpenGL 错误,因此您关于fghGenBuffers() 是根本原因的结论具有误导性。我希望 GL_OUT_OF_MEMORY 错误会出现在您的案例中。现代 OpenGL 还定义了一种更复杂的错误报告机制 - ARB_debug_output

显示列表是 OpenGL 世界中一种非常古老的机制,旨在通过将一系列 OpenGL 命令“记住”到一些内部驱动程序管理的缓存中来优化大量数据的绘制。这种机制在Vertex Buffer Objects 被广泛采用之前普遍使用,它已被添加到 OpenGL 1.5 作为控制顶点数据内存的更直接和有效的方法,并且在VulkanGL_NV_command_list 重新发明命令缓冲区之前 作为缓存一系列 GPU 命令的更可靠接口。

显示列表机制的一个大设计问题是不可预测的内存管理和不同供应商之间极其不同的实现(从非常差到极度优化)。现代图形驱动程序在编译显示列表时尝试将顶点数据隐式上传到 GPU 内存,但它们实际执行的操作仍然是隐藏的。

古老的 GLU 库是代码中的另一个谜团,因为很难估计 gluSphere() 使用的内存。一个悲观的计算表明:

size_t aNbSpheres = 10 * 10 * 20;
size_t aNbSlices, aNbStacks = 100;
size_t aNbTriangles = aNbSlices * aNbSlices * 2;
size_t aNbNodes = aNbSpheres * aNbTriangles * 3; // non-indexed array
size_t aNodeSize = (3 * sizeof(GLfloat)) * 2; // position + normal
size_t aMemSize = aNbNodes * aNodeSize;
size_t aMemSizeMiB = aMemSize / 1024 / 1024;

仅 2000 个球体的顶点数据可能会占用大约 2.746 GiB 的内存! 如果您的应用程序是在 32 位模式下构建的,那么它确实达到 32 位地址空间内存限制也就不足为奇了。但即使在 64 位应用程序的情况下,OpenGL 驱动程序实现也可能会遇到一些内部限制,这将由相同的GL_OUT_OF_MEMORY 错误报告。

不管内存限制如何,您的代码都会尝试绘制大约 40M 的三角形。这对于快速的现代 GPU 硬件来说并非不可能,但在低端嵌入式图形上可能真的很慢。

那么接下来可以做什么呢?

  • 学习 OpenGL 调试实践 - 使用 glGetError() 和/或 ARB_debug_output 定位此问题和其他问题的位置和根本原因。
  • 减少gluSphere() 镶嵌参数。
  • 生成单个球体的显示列表并多次绘制。 实例化 极大地减少了内存消耗。但是,这可能比一次绘制所有球体更慢(但 2000 次绘制调用对于现代 CPU 来说并没有那么大)。
  • 用直接生成顶点数据替换过时的 GLU 库 - 球体细分并不难实现,并且网络上有很多示例。
  • 了解 Vertex Buffer Objects 并使用它们来代替过时的显示列表。
  • 学习 GLSL 和现代 OpenGL,以便您可以最有效地实现 hardware instancing 来绘制球体。

另一方面,fghGenBuffers 错误看起来很奇怪,因为 glGenBuffers() 应该出现在每个现代 OpenGL 实现中。通过glGetString(GL_VENDOR)/glGetString(GL_RENDERER)/glGetString(GL_VERSION) 打印驱动程序信息,以查看您的系统是否安装了正确的 GPU 驱动程序并且没有使用过时的 OpenGL 1.1 Microsoft 软件实现。

【讨论】:

    【解决方案2】:

    当然,缓冲区分配可能会失败 - 计算机中的一切都是有限的 - 但您的错误消息与您的问题无关。

    您收到错误:

    freeglut : fgInitGL2 : fghGenBuffers is NULL

    这是来自freeglut 的错误,它不是 OpenGL 的一部分。所以请查找the implementation of freeglut's fgInitGL2

    如果fghGenBuffers 失败,则意味着以下行失败:

    CHECK("fghGenBuffers", fghGenBuffers = (FGH_PFNGLGENBUFFERSPROC)glutGetProcAddress("glGenBuffers"));
    

    即GLUT 无法获得glGenBuffers 函数的地址。它没有请求缓冲区并且未能获得它们,它请求了它应该请求缓冲区的函数的地址,甚至没有得到。

    然而,这只是一个fgWarning,即一个警告,而不是一个错误。我敢猜测,从程序启动的那一刻起,您就会在终端上看到该消息,无论它随后是否失败。这是 GLUT 想让你知道的事情,但它与你的失败无关。

    至于您的实际问题:几乎可以肯定,这与试图过度填充显示列表有关。在上下文中,您最好的解决方案是只将一个球体放入显示列表中,然后发出 2000 次调用来绘制它,修改每个球体之间的模型视图矩阵。

    顺便说一句:显示列表被证明是一个坏主意,它没有提供太多的优化空间,并且在 OpenGL 1.5 中大部分未使用。它们在 2008 年的 OpenGL 3.0 中被弃用,整个固定功能管道也被弃用——包括 glPushMatrixglTranslateglPopMatrix

    这并不是要夸夸其谈,但请注意,您的代码的形成方式依赖于挥之不去的已弃用功能。它可能包含没有人愿意更新的硬限制,或者以任何其他方式看到非常有限的维护。

    不过,这无疑是最简单的方法,而且您可能正在使用一千个 CAD 或其他科学程序,所以现在最好的建议就是不要尝试将所有领域都投入其中一个显示列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多