OpenGL:动态更改纹理坐标

8

我目前正在尝试使用位图(类似于入侵者游戏中的记分牌)来呈现整数值,但是我在游戏运行时更改纹理坐标时遇到了问题。

我将着色器和数据链接如下:

GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE,
4 * sizeof(float), (void*)(2 * sizeof(float)));

在我的着色器中,我执行以下操作:

顶点着色器:

#version 150

uniform mat4 mvp;

in vec2 position;
in vec2 texcoord;

out vec2 Texcoord;


void main() {
    Texcoord = texcoord;
    gl_Position =  mvp * vec4(position, 0.0, 1.0) ;
}

片元着色器:

#version 150 core
in vec2 Texcoord;
out vec4 outColor;

uniform sampler2D tex;
void main() {
    outColor = texture2D(tex, Texcoord);
}

我该如何更改这段代码/实现一个函数来改变texcoord变量?

制作一个函数来生成网格,使UV坐标设置在正确的位置。这并不是易如反掌,但也不是太难。 - BWG
@BWG 我不太确定你所说的“UV坐标设置在正确位置”的意思。为了澄清,我成功地渲染了一个字母/数字,但我使用纹理图集通过改变纹理坐标来改变字母/数字。 - user3162904
2
当你说“像这样链接着色器和数据”时,你不应该这样做。你只需要设置一次着色器,但每次绘制新角色时都需要更新网格的数据。或者你可以将整数传递给着色器,在那里生成纹理坐标。 - BWG
@BWG 对,我明白了,谢谢!这是否意味着我必须将变量类型更改为类似于布局限定符的东西?当我想要更改/访问它们时,我该怎么做? - user3162904
我个人使用布局限定符,但我正在使用OpenGL 3.3。我会给你写一个更详细的答案。 - BWG
@BWG 老兄,太感谢了!OpenGL版本仍然是可以更改的,所以不要感到受限制。 - user3162904
3个回答

10

如果您需要频繁修改纹理坐标,但其他顶点属性保持不变,则将纹理坐标保留在单独的VBO中可能会更有益。虽然通常最好使用交错属性,但这是一种情况,其中并不一定是最有效的解决方案。

因此,您将拥有两个VBO,一个用于位置,另一个用于纹理坐标。您的设置代码将类似于以下内容:

GLuint vboIds[2];
glGenBuffers(2, vboIds);

// Load positions.
glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

// Load texture coordinates.
glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_DYNAMIC_DRAW);

请注意glBufferData()的最后一个参数是不同的,它是一个使用提示。 GL_STATIC_DRAW表示数据不会经常被修改,而GL_DYNAMIC_DRAW表示数据将被频繁修改。

因此,每当您的纹理数据发生变化时,您可以使用glBufferSubData()进行修改:

glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(texCoords), texCoords);

当然,如果只有部分发生了变化,您只需要对发生变化的部分进行调用。

您没有指定纹理坐标的变化方式。如果只是一些简单的转换,那么在着色器代码中应用该转换会更加高效,而不是修改原始纹理坐标。

例如,假设您只想要移动纹理坐标。您可以在顶点着色器中使用一个统一变量来表示该移动,并将其添加到传入的纹理坐标属性中:

uniform vec2 TexCoordShift;
in vec2 TexCoord;
out vec2 FragTexCoord;
...
    FragTexCoord = TexCoord + TexCoordShift;

然后在您的 C++ 代码中:

// Once during setup, after linking program.
TexCoordShiftLoc = glGetUniformLocation(program, "TexCoordShift");

// To change transformation, after glUseProgram(), before glDraw*().
glUniform2f(TexCoordShiftLoc, xShift, yShift);

我的错,我没有提到,但确实是这样。你介意向我展示如何执行这样的翻译吗?我猜测需要另一个着色器变量来进行偏移,但我不知道如何在想要时改变它,除了上面提到的方法。 - user3162904
我添加到答案的末尾。 - Reto Koradi

3

我不能保证这种技术的效率,但这是我所做的,如果文本渲染会减慢我的程序,那我就完了。

我有一个专门用于存储网格的类,它由一些数据向量和一些GLuint指针组成,用于存储上传到OpenGL的数据。我像这样上传数据到OpenGL:

            glBindBuffer(GL_ARRAY_BUFFER, position);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * data.position.size(), &data.position[0], GL_DYNAMIC_DRAW);

            glBindBuffer(GL_ARRAY_BUFFER, normal);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * data.normal.size(), &data.normal[0], GL_DYNAMIC_DRAW);

            glBindBuffer(GL_ARRAY_BUFFER, uv);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vec2) * data.uv.size(), &data.uv[0], GL_DYNAMIC_DRAW);

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * data.index.size(), &data.index[0], GL_DYNAMIC_DRAW);

然后,我按照以下方式进行绘制:
        glEnableVertexAttribArray(positionBinding);
        glBindBuffer(GL_ARRAY_BUFFER, position);
        glVertexAttribPointer(positionBinding, 3, GL_FLOAT, GL_FALSE, 0, NULL);

        glEnableVertexAttribArray(normalBinding);
        glBindBuffer(GL_ARRAY_BUFFER, normal);
        glVertexAttribPointer(normalBinding, 3, GL_FLOAT, GL_TRUE, 0, NULL);

        glEnableVertexAttribArray(uvBinding);
        glBindBuffer(GL_ARRAY_BUFFER, uv);
        glVertexAttribPointer(uvBinding, 2, GL_FLOAT, GL_FALSE, 0, NULL);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index);

        glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, NULL);

        glDisableVertexAttribArray(positionBinding);
        glDisableVertexAttribArray(normalBinding);
        glDisableVertexAttribArray(uvBinding);

这个设置是为了一个完整的3D引擎设计的,所以你可以缩小一些。基本上,我有四个缓冲区,位置、纹理坐标、法向量和索引。你可能只需要前两个,所以忽略其他的就可以了。
无论如何,每次我想要绘制一些文本,我使用我展示的第一个代码块上传我的数据,然后使用第二个代码块绘制它。这工作得相当不错,而且非常优雅。这是我用它来绘制文本的代码:
vbo(genTextMesh("some string")).draw(); //vbo is my mesh containing class

希望这能帮到你,如果你有任何问题,请随时提出。


现在已经很晚了,我要去睡觉了。明天再试一下。感谢你的努力。 - user3162904

2

我使用一个统一的vec2将纹理偏移传递到顶点着色器中。

我不确定这是否高效,但如果您的纹理坐标具有相同的形状,只是移动了位置,那么这是一种选择。

#version 150

uniform mat4 mvp;
uniform vec2 texOffset;

in vec2 position;
in vec2 texcoord;

out vec2 Texcoord;


void main() {
    Texcoord = texcoord + texOffset;
    gl_Position =  mvp * vec4(position, 0.0, 1.0) ;
}

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