【问题标题】:How does one add vertices to a mesh object in OpenGL?如何在 OpenGL 中向网格对象添加顶点?
【发布时间】:2014-08-13 04:37:31
【问题描述】:

我是 OpenGL 的新手,我一直在使用红皮书和超级圣经。在 SB 中,我已经了解了有关使用从文件加载的对象的部分。到目前为止,我认为我理解正在发生的事情以及如何去做没有问题,但这让我开始考虑在我自己的应用程序中制作自己的网格 - 本质上是一个建模应用程序。我已经通过我的参考资料和互联网进行了大量搜索,但我还没有找到一个很好的教程来将这些功能实现到自己的应用程序中。我找到了一个API,它只是提供了这个功能,但我试图理解它的实现;不仅仅是界面。

到目前为止,我已经创建了一个“应用程序”(我轻描淡写地使用了这个术语),它为您提供了一个可以单击并添加顶点的视图。顶点不连接,只是显示在您单击的位置。我担心的是,我在试验时偶然发现的这种方法不是我应该实施此过程的方式。

我正在使用 Mac 并在 Xcode 中使用 Objective-C 和 C。

我的OpenGLView.m #import "MyOpenGLView.h"

@interface MyOpenGLView () {

NSTimer *_renderTimer
Gluint VAO, VBO;

GLuint totalVertices;
GLsizei bufferSize;

}

@end

@implementation MyOpenGLView

/* Set up OpenGL view with a context and pixelFormat with doubleBuffering */

/* NSTimer implementation */

- (void)drawS3DView {

    currentTime = CACurrentMediaTime();

    NSOpenGLContext *currentContext = self.openGLContext;
    [currentContext makeCurrentContext];
    CGLLockContext([currentContext CGLContextObj]);

    const GLfloat color[] = {
        sinf(currentTime * 0.2),
        sinf(currentTime * 0.3),
        cosf(currentTime * 0.4),
        1.0
    };

    glClearBufferfv(GL_COLOR, 0, color);

    glUseProgram(shaderProgram);

    glBindVertexArray(VAO);

    glPointSize(10);
    glDrawArrays(GL_POINTS, 0, totalVertices);

    CGLFlushDrawable([currentContext CGLContextObj]);
    CGLUnlockContext([currentContext CGLContextObj]);

}

#pragma mark - User Interaction

- (void)mouseUp:(NSEvent *)theEvent {

    NSPoint mouseLocation = [theEvent locationInWindow];
    NSPoint mouseLocationInView = [self convertPoint:mouseLocation fromView:self];

    GLfloat x = -1 + mouseLocationInView.x * 2/(GLfloat)self.bounds.size.width;
    GLfloat y = -1 + mouseLocationInView.y * 2/(GLfloat)self.bounds.size.height;

    NSOpenGLContext *currentContext = self.openGLContext;
    [currentContext makeCurrentContext];
    CGLLockContext([currentContext CGLContextObj]);

    [_renderer addVertexWithLocationX:x locationY:y];

    CGLUnlockContext([currentContext CGLContextObj]);

}

- (void)addVertexWithLocationX:(GLfloat)x locationY:(GLfloat)y {

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    GLfloat vertices[(totalVertices * 2) + 2];

    glGetBufferSubData(GL_ARRAY_BUFFER, 0, (totalVertices * 2), vertices);

    for (int i = 0; i < ((totalVertices * 2) + 2); i++) {
        if (i == (totalVertices * 2)) {
            vertices[i] = x;
        } else if (i == (totalVertices * 2) + 1) {
            vertices[i] = y;
        }
    }

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    totalVertices ++;

}

@end

应用程序应该获取鼠标点击的位置并提供它是一个顶点位置。对于每个添加的顶点,我首先绑定 VBO 以确保它处于活动状态。接下来,我创建一个新数组来保存我当前的顶点位置(totalVertices)加上一个顶点的空间(x 和 y 为 + 2)。然后我使用 glGetBufferSubData 从 VBO 取回数据并将其放入这个数组中。使用 for 循环,我将 X 和 Y 数字添加到数组的末尾。最后,我将这些数据发送回 GPU 到 VBO 并调用 totalVertices++,这样我就知道下次我想添加一个顶点时数组中有多少个顶点。

这让我想到了我的问题:我这样做对吗?换句话说,我是否应该在 CPU 端保留 BufferData 的副本,这样我就不必调用 GPU 并将数据发送回进行编辑?这样,我就不会调用 glGetBufferSubData,我只会创建一个更大的数组,将新顶点添加到末尾,然后调用 glBufferData 用更新的顶点数据重新分配 VBO。

** 我试图将我的思考过程包括在内,以便像我这样缺乏编程经验的人能够理解我想要做什么。我不希望任何人因为我对我所做的解释而感到生气。 **

【问题讨论】:

    标签: objective-c c macos opengl vertexdata


    【解决方案1】:

    我当然会避免读回数据。不仅是因为额外的数据拷贝,也是为了避免 CPU 和 GPU 之间的同步。

    当您进行 OpenGL 调用时,您可以想象驱动程序构建一个 GPU 命令,将其排队等待稍后提交给 GPU,然后返回。这些命令将在稍后提交给 GPU。这个想法是 GPU 可以尽可能独立于 CPU 上运行的任何东西(包括您的应用程序)运行。 CPU 和 GPU 以最小的依赖性并行运行是非常理想的性能。

    对于大多数glGet*() 调用,这种异步执行模型会失效。他们通常必须等到 GPU 完成所有(或至少一些)待处理命令才能返回数据。所以 CPU 可能会阻塞,而只有 GPU 正在运行,这是不可取的。

    因此,您绝对应该保留数据的 CPU 副本,这样您就不必再读回它了。

    除此之外,还有一些选择。这完全取决于您的使用模式、特定平台的性能特征等。要真正充分利用它,没有办法实现多种变体并对其进行基准测试。

    对于您所描述的内容,我可能会从与 C++ 中的 std::vector 类似的东西开始。您分配的内存量(通常命名为容量)大于您目前所需的内存量。然后您可以在不重新分配的情况下添加数据,直到您填满分配的容量。到那时,您可以将容量翻倍。

    将此应用于OpenGL,您可以通过调用glBufferData() 并以NULL 作为数据指针来保留一定数量的内存。跟踪您分配的容量,并通过调用glBufferSubData() 填充缓冲区。在示例代码中添加单个点时,您将调用 glBufferSubData() 并仅使用新点。只有当你的容量用完时,你才用一个新的容量调用glBufferData(),然后用你已经拥有的所有数据填充它。

    在伪代码中,初始化看起来像这样:

    int capacity = 10;
    glBufferData(GL_ARRAY_BUFFER,
        capacity * sizeof(Point), NULL, GL_DYNAMIC_DRAW);
    std::vector<Point> data;
    

    那么每次加一个点:

    data.push_back(newPoint);
    if (data.size() <= capacity) {
        glBufferSubData(GL_ARRAY_BUFFER,
            (data.size() - 1) * sizeof(Point), sizeof(Point), &newPoint);
    } else {
        capacity *= 2;
        glBufferData(GL_ARRAY_BUFFER,
            capacity * sizeof(Point), NULL, GL_DYNAMIC_DRAW);
        glBufferSubData(GL_ARRAY_BUFFER,
            0, data.size() * sizeof(Point), &data[0]);
    }
    

    作为glBufferSubData() 的替代方案,glMapBufferRange() 是更新缓冲区数据时要考虑的另一个选项。更进一步,您可以考虑使用多个缓冲区,并在它们之间循环,而不是只更新单个缓冲区。这就是基准测试发挥作用的地方,因为没有一种方法可以最适合所有可能的平台和用例。

    【讨论】:

    • 谢谢!你的回答非常非常有帮助。深思熟虑;我非常感谢您花时间解释所有内容并提供一些示例。我迫不及待地想把它应用到我自己的代码中。再次感谢您。
    猜你喜欢
    • 2015-04-14
    • 1970-01-01
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多