在 OpenGL、着色器或缓冲区中操作对象位置数据的正确方法?

问题描述 投票:0回答:0

我一直在尝试使用较新的着色器管道功能来学习 OpenGL,而不是已弃用的即时模式固定管道 gl。无论是在性能方面还是在设计方面,我都对一些事情感到困惑,无论我正在做的是“正确”的方式,还是普遍接受的做事方式。

在旧版本的 GL 中,我可以使用 glTranslate 在屏幕上操纵我的对象,并使用矩阵堆栈来推送我的对象的副本+单独翻译每个对象。使用较新的 GL 这是不可能的,所以我一直在尝试实现类似功能的方法。

我的环境的最小例子:

glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);

// centered square
float verts[] = {
    -0.5,    0.5
     0.5,    0.5,
    -0.5,   -0.5,
     0.5,   -0.5,
};

glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);

int idx[] = {
    0, 1, 2,
    1, 2, 3
};

glGenBuffers(1, &_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);
glEnableVertexAttribArray(0);

while (renderLoop) {
    glUseProgram(_program);
    glBindVertexArray(_vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    // swap buffers
}

我想在屏幕上平移我的正方形。我知道它的位置取决于我传递到我的数组缓冲区的顶点,并且这些顶点由顶点着色器操纵。据此,我推断出有两个逻辑入口点供我操作我的数据,直接缓冲区或通过着色器。我唯一的困境是我不确定哪种方法是正确的,无论是性能还是可维护性。我想从一开始就养成正确的习惯,这样我就不会在整个发展过程中养成一个坏习惯,但我不确定我打算做什么。

如果我要直接重写缓冲区,我必须在每次渲染时都更改它,这可能代价高昂。我还必须将它切换为 GL_DYNAMIC_DRAW,因为我经常更改它。然而,主要的好处是我可以分别操纵每个点,这可能是有意为之的。比方说,在一个例子中,我想在我的鼠标指针处创建我的对象。我需要知道鼠标指针的 x 和 y 坐标,然后用标准化的宽度/高度坐标缩放它们,所有这些都需要我重写缓冲区。

while (renderLoop) {
    glUseProgram(_program);
    glBindVertexArray(_vao);

    manipulateVerts(verts);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_DYNAMIC_DRAW); // also changed above, before render loop

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    // swap buffers
}

我考虑的另一种可能的方法是通过着色器,我拥有一个统一的位置变量,并将坐标传递给我的着色器。假设我的

manipulateVerts
函数将 x 移动了 -0.6,将 y 移动了 0.4。我可以使用这些值通过 uniform vec2 传递运动偏移。鉴于顶点着色器旨在操纵顶点数据,这似乎是更合乎逻辑的事情。但是,我只能独立地操作每个像素,如果它们依赖于另一个来了解它们的新位置,我就不能那样做。这给着色器方法带来了问题。

#version 330 core
layout (location = 0) in vec2 pos;
uniform vec2 offset;

void main() {
    gl_Position = vec4(pos.x + offset.x, pos.y + offset.y, 0.0, 1.0);
}

然后在我的渲染循环中,我可以查找统一 ID 并更改它。

while (renderLoop) {
    glUseProgram(_program);
    glBindVertexArray(_vao);

    float offset[] = { -0.6, 0.4 };
    unsigned int _offset = glGetUniformLocation(_program, "offset");
    glUniform2fv(_offset, 1, offset);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    // swap buffers
}

这是一个很好的方法,直到我认为着色器通常要运行多个,数百个甚至数千个 VBO。虽然在我的情况下,一个 VBO 可以使用这种方法,但多个不同的 VBO 又如何呢?我只需要将偏移量设置为 0 吗?运行多个 VBO 并按预期绑定它们,然后操纵我的偏移量,或者操纵顶点更有意义。如果我有一个 VBO 的多个副本怎么办,我不能一直平移它的顶点,因为它会影响其他顶点,所以我要么不得不在内存中制作多个副本,由于完全不必要的原因占用大量 RAM .

我想我得出的结论是这完全取决于,但我想要一个外部意见。我对 OpenGL 和 GLSL 着色器还很陌生,所以我的经验不足可能会影响我做出理性选择的能力。

c++ optimization opengl glsl vertex-shader
© www.soinside.com 2019 - 2024. All rights reserved.