使用glDrawElementsInstanced进行OpenGL实例化渲染时顶点位置与纹理坐标重复的问题排查
实例化渲染中所有顶点数据重复的问题解决
嘿,我一眼就瞅出问题所在了——你错误地给所有顶点属性都设置了glVertexAttribDivisor(..., 1),这直接导致OpenGL在每个实例里只取一次属性数据,所有顶点都复用同一个值,自然就出现了你看到的“所有顶点位置、纹理坐标都一样”的情况。
为啥会这样?
先给你捋清楚glVertexAttribDivisor的作用:
- 设为
0(默认值):每绘制一个顶点,就从VBO里取下一组数据,这是普通顶点属性的正确打开方式——毕竟每个顶点的位置、纹理坐标本来就该不一样对吧? - 设为
1:每绘制一个实例,才取一组数据,这只用来放每个实例独有的数据,比如不同实例的位置偏移、缩放参数这类。
你现在把顶点坐标、颜色、纹理坐标全设成divisor=1,等于告诉OpenGL:“不管这个实例里有多少个顶点,都用第一组数据就行”,那所有顶点当然都长得一模一样了。
怎么改?
首先得把普通顶点属性和实例化属性分开:
你的vertices里存的是单个四边形的顶点数据(每个顶点的x/y、rgb、uv),这部分属于普通顶点属性,divisor必须设为0。
修改你的VAO初始化代码:
glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(float) * vertices.size()), vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(unsigned int) * indices.size()), indices.data(), GL_STATIC_DRAW); GLsizei vertexStride = static_cast<GLsizei>(7 * sizeof(float)); // 顶点坐标:普通属性,divisor设为0 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, vertexStride, 0); glVertexAttribDivisor(0, 0); // 关键修改! glEnableVertexAttribArray(0); // 颜色:同样是普通属性,注意偏移量要转成void*,你之前的写法有问题 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertexStride, (void*)(2 * sizeof(float))); glVertexAttribDivisor(1, 0); // 关键修改! glEnableVertexAttribArray(1); // 纹理坐标:普通属性,偏移量转void* glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertexStride, (void*)(5 * sizeof(float))); glVertexAttribDivisor(2, 0); // 关键修改! glEnableVertexAttribArray(2); glBindVertexArray(0);
另外提个小细节:glVertexAttribPointer的最后一个参数是const void*类型,你之前直接写2、5会被当成内存地址,这也是潜在bug,必须转成(void*)(偏移量字节数)的形式。
如果需要每个实例有不同的参数(比如位置)
要是你想让每个四边形实例在不同位置,得单独搞个VBO存实例化数据(比如每个实例的x/y偏移),然后给这个新属性设divisor=1:
// 假设offsets是存储每个实例偏移的向量,每个元素是glm::vec2 unsigned int instanceVBO; glGenBuffers(1, &instanceVBO); glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); glBufferData(GL_ARRAY_BUFFER, offsets.size() * sizeof(glm::vec2), offsets.data(), GL_STATIC_DRAW); // 添加实例化属性(location=3) glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), 0); glVertexAttribDivisor(3, 1); // 这个才是实例化属性,divisor设1 glEnableVertexAttribArray(3);
然后在顶点着色器里用上这个偏移:
#version 330 core layout (location = 0) in vec2 vertex_pos; layout (location = 1) in vec3 vertex_color; layout (location = 2) in vec2 vertex_tex_coord; layout (location = 3) in vec2 instance_offset; // 实例化偏移量 out vec2 tex_coord; out vec3 color; uniform mat4 viewproj; void main() { vec2 final_pos = vertex_pos + instance_offset; gl_Position = viewproj * vec4(final_pos, 0.0, 1.0); tex_coord = vertex_tex_coord; color = vertex_color; }
额外排查小技巧
- 再确认下
vertices的结构:单个四边形4个顶点,每个顶点是x,y,r,g,b,u,v7个float,顺序别错。 - 用RenderDoc再检查VAO的属性配置,看看每个属性的
stride、offset、divisor是不是和你设置的一致。 - 顶点着色器里的变量名别写错,比如别把
vertex_pos写成别的。
内容的提问来源于stack exchange,提问作者aTom
相关产品推荐
相关产品推荐

