glDrawElementsInstanced:未对每个实例应用VertexAttribute mat4



我在使用一个VertexArrayObject和四个VertexBufferObject渲染对象的多个实例时遇到问题。

我无法理解我的方法出了什么问题。以下是基本信息:

我相对简单的顶点着色器代码:

#version 330 core
precision highp float;
layout (location=0) in vec3 position;
layout (location=1) in vec2 texcoord;
layout (location=3) in mat4 modelViewMatrix;
out vec2 textureCoord;
uniform mat4 pr_matrix;
void main() {
textureCoord = vec2(texcoord.x, texcoord.y);

vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
gl_Position = pr_matrix * mvPos;
}

正如您所看到的,我尝试将模型视图矩阵(模型和camera_view组合(作为VertexAttribute传递。

据我所知,一个VertexAttribute被限制为vec4的最大值,这意味着我的mat4*vec4的位置。

每个VAO和VBO只存在一次。我不为每个"em"使用单独的一个;游戏对象",就像一些在线教程一样。因此,我在特定位置更新每个缓冲区。首先,让我向您展示以下代码,它初始化了它们:

// VAO
this.vao = glGenVertexArrays();
glBindVertexArray(this.vao);

// buffer for vertex positions
this.positionVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.positionVBO);
//  upload null data to allocate vbo storage in memory
glBufferData(GL_ARRAY_BUFFER, vertexpoints * Float.BYTES, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

// buffer for texture coordinates
this.textureVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.textureVBO);
glBufferData(GL_ARRAY_BUFFER, texturepoints * Float.BYTES, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);

// buffer for transform matrices
this.matricesVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.matricesVBO);
glBufferData(GL_ARRAY_BUFFER, mtrxsize * Float.BYTES, GL_DYNAMIC_DRAW);
// Byte size of one vec4
int vec4Size = 4 * Float.BYTES;
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, false, 4 * vec4Size, 0);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 4, GL_FLOAT, false, 4 * vec4Size, 1 * vec4Size);
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, false, 4 * vec4Size, 2 * vec4Size);
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, false, 4 * vec4Size, 3 * vec4Size);
glVertexAttribDivisor(6, 1);

//buffer for indices
this.indicesVBO = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indicesVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indsize * Integer.BYTES, GL_DYNAMIC_DRAW);

//unbind buffers and array
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

据我所知,此初始化应该对应于"顶点着色器"中定义的4个顶点属性。

出于测试目的,我初始化了4个游戏对象(A1、A2、B1、B2(,每个对象都有:

  • position属性的4个vec3点,导致4*3=12个浮点推送到positionVBO
  • 纹理属性的4 x vec2点,导致4*2=8个浮点推送到纹理VBO
  • 要绘制的索引的6个int点(0,1,2,2,3,0(,推到标记VBO
  • modelViewMatrix属性的1 x mat4,导致4*vec4=4*4=16个浮点推送到矩阵VBO

对于每个游戏对象,我使用以下逻辑将其数据推送到VBO:

long vertoff = gameObjectVertOffset;   // offset of the current gameobject's vertex points in position data
long texoff = gameObjectTexOffset;     // offset of the current gameobject's texture points in texture data
long indoff = gameObjectIndOffset;     // offset of the current gameobject's indices in index data
long instoff = gameObjectMatrixOffset; // offset of the current gameobject's matrix (vec4) in matrices data
// upload new position data
if(gameObjectVertBuf.capacity() > 0) {
gameObjectVertBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.positionVBO);
glBufferSubData(GL_ARRAY_BUFFER, vertoff * Float.BYTES, gameObjectVertBuf);
}
// upload new texture data
if(gameObjectTexBuf.capacity() > 0) {
gameObjectTexBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.textureVBO);
glBufferSubData(GL_ARRAY_BUFFER, texoff * Float.BYTES, gameObjectTexBuf);
}
// upload new indices data
if(gameObjectIndBuf.capacity() > 0) {
gameObjectIndBuf.flip();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indicesVBO);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indoff * Integer.BYTES, gameObjectIndBuf);
}
// upload new model matrix data
if(gameObjectMatrixBuf.capacity() > 0) {
gameObjectMatrixBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.matricesVBO);
glBufferSubData(GL_ARRAY_BUFFER, instoff * Float.BYTES, gameObjectMatrixBuf);
}

现在到实际渲染:

我想画元素A2次,然后画元素B两次。对于实例化渲染,我将游戏对象分组在一起,我知道我可以在列表中的一个调用中进行渲染。

我现在有两个列表,每个列表中都有两个元素:

  • 列表1[A1,A2]
  • 清单2[B1,B2]

每个列表我现在做一次

numInstances = 2;
this.vao.bind();
shaderprogram.useProgram();
glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, (int) (offset * Integer.BYTES), numInstances);
offset += 6 * numInstances; // 6 indices * 2 instances

问题:

这会导致前两个元素被正确渲染,但后两个元素(来自第二个列表/glDrawElementsInstanced((调用(是用前两个元件的变换矩阵渲染的。

先渲染哪个对象列表并不重要。第二次迭代似乎总是使用第一次迭代中的modelViewMatrix属性。

据我所知,glVertexAttribDivisor((调用应该限制每个实例的矩阵迭代,而不是每个vertex

我在这里错过了什么?

后两个(来自第二个list/glDrawElementsInstanced((调用(使用前两个元素的转换矩阵进行渲染。

这就是你要求做的。系统如何知道它需要使用数组中的第二个元素,而不是前两个?它所看到的只是另一个平局调用,它们之间的VAO状态没有变化。

该系统无法跟上先前绘制调用中使用的实例数量。这是你的工作。

现在,您可以更改有问题的属性的缓冲区绑定,但使用基本实例渲染更容易。在这些绘图函数中,可以指定应用于实例化属性的实例索引的偏移。因此,如果您想渲染从实例索引2开始的两个实例,可以执行以下操作:

glDrawElementsInstancedBaseInstance(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, (int) (offset * Integer.BYTES), 2, 2);

基本实例化渲染是GL 4.2的一项功能。

最新更新