在工作精灵系统中添加实例的问题

Issues adding instancing to a working sprite system

本文关键字:实例 问题 添加 工作 精灵 系统      更新时间:2023-10-16

我试图在我的2d游戏引擎中执行实例化,这样它就可以在不损失任何性能的情况下支持粒子系统。我的类ISprite派生自一个工作的Sprite类。我只是删除了所有影响单个精灵的功能,并将其替换为脑海中的实例化计划。遗憾的是,屏幕上什么也没画。

以下是相关信息:

顶点着色器

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoords;
layout (location = 2) in vec4 colorSource;
layout (location = 3) in mat4 transform;
out vec2 TexCoords;
out vec4 Color;
uniform mat4 uniformView;
uniform mat4 uniformProjection;
void main()
{
  gl_Position = uniformProjection * uniformView * transform * vec4(position, 1.0f);
  TexCoords = texCoords;
  Color = colorSource;
}

片段着色器

#version 330 core
in vec2 TexCoords;
in vec4 Color;
out vec4 color;
uniform sampler2D Texture;
uniform vec4 uniformColor;
void main()
{
    vec4 texColor = texture(Texture, TexCoords) * Color;
    if(texColor.a < 0.1)
        discard;
    color = texColor;
 }

Load -准备绘制的所有精灵,调用一次。

void ISprite::Load(Shader spriteShader)
{
    spriteShader.Use();
  GLfloat vertices[] = {
    //X    Y     Z     
    0.5f, -0.5f, 0.0f,
    -0.5f, 0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.5f, 0.5f, 0.0f,
    -0.5f, 0.5f, 0.0f
  };
  glGenVertexArrays(1, &vertexArray);
  glGenBuffers(1, &positionBuffer);
  glGenBuffers(1, &texCoordsBuffer);
  glGenBuffers(1, &colorBuffer);
  glGenBuffers(1, &matrixBuffer);
  glBindVertexArray(vertexArray); 
  //The vertex data will never change, so send that data now. 
  glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  //For vertex Position
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), (GLvoid*)0);
  //For texture coordinates
  glBindBuffer(GL_ARRAY_BUFFER, texCoordsBuffer);
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
  //For Color
  glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
  glEnableVertexAttribArray(2);
  glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
  //For Transformation Matrix
  glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
  for (int i = 0; i < 4; ++i)
  {
      glEnableVertexAttribArray(3 + i);
      glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, 
          4 * 4 * sizeof(GLfloat), (GLvoid*)(4 * i * sizeof(GLfloat)));
  }
  glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, texCoordsBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
  glBindVertexArray(0);
  glVertexAttribDivisor(positionBuffer, 0);
  glVertexAttribDivisor(texCoordsBuffer, 1);
  glVertexAttribDivisor(colorBuffer, 1);
  glVertexAttribDivisor(matrixBuffer, 1);
  glVertexAttribDivisor(matrixBuffer + 1, 1);
  glVertexAttribDivisor(matrixBuffer + 2, 1);
  glVertexAttribDivisor(matrixBuffer + 3, 1);
  ISprite::shader = &spriteShader;
}

Prepare Draw -由每个精灵,每帧调用。将数据发送到静态向量

void ISprite::prepareDraw(void)
{
  //Adds their personal data to vectors shared by class 
  glm::mat4 transform = calculateTransorm();
  for (int i = 0; i < 4; ++i)
  {
      for (int j = 0; j < 4; ++j)
        ISprite::transformMatrices.push_back(transform[i][j]);
  }
  texture.updateAnimation();
  for (int i = 0; i < 12; ++i)
    ISprite::textureCoordinatesAll.push_back(texture.textureCoordinates[i]);
  ISprite::colorValues.push_back(color.x);
  ISprite::colorValues.push_back(color.y);
  ISprite::colorValues.push_back(color.z);
  ISprite::colorValues.push_back(color.w);
}

绘制精灵 -每帧调用一次,实际绘制精灵

void ISprite::drawSprites(Texture testTexture)
{
  shader->Use();
  for (std::vector<ISprite*>::iterator it = Isprites.begin(); it != Isprites.end(); ++it)
    (*it)->prepareDraw();
  glBindVertexArray(vertexArray);
  glBindTexture(GL_TEXTURE_2D, testTexture.ID);
  //Bind texture here if you want textures to work. if not, a single texture atlas will be bound
  glBindBuffer(GL_ARRAY_BUFFER, texCoordsBuffer);
  glBufferData(GL_ARRAY_BUFFER, textureCoordinatesAll.size() * sizeof(GLfloat),
  textureCoordinatesAll.data(), GL_STREAM_DRAW);
  glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
  glBufferData(GL_ARRAY_BUFFER, colorValues.size() * sizeof(GLfloat),
  colorValues.data(), GL_STREAM_DRAW);
  glBindBuffer(GL_ARRAY_BUFFER, matrixBuffer);
  glBufferData(GL_ARRAY_BUFFER, transformMatrices.size() * sizeof(GLfloat),
  transformMatrices.data(), GL_STREAM_DRAW);
  glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 6, Isprites.size());
  textureCoordinatesAll.clear();
  colorValues.clear();
  transformMatrices.clear();
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindTexture(GL_TEXTURE_2D, 0);
  glBindVertexArray(0);
}

不呈现任何内容的原因可能有很多。变换的问题,坐标超出范围等。但是与实例化相关的一件事在发布的代码中肯定看起来是错误的:

glBindVertexArray(0);
glVertexAttribDivisor(positionBuffer, 0);
glVertexAttribDivisor(texCoordsBuffer, 1);
glVertexAttribDivisor(colorBuffer, 1);
...

glVertexAttribDivisor()的第一个参数是顶点属性的位置,而不是缓冲区的名称。此外,这个调用设置的状态是VAO状态的一部分,因此您应该在VAO仍然绑定时进行这些调用。

所以调用应该是这样的:

glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 0);
glVertexAttribDivisor(2, 1);
...
glBindVertexArray(0);

,其中glVertexAttribDivisor()的第一个参数与您也用作glVertexAttribPointer()glEnableVertexAttribArray()的第一个参数的位置值相匹配。

纹理坐标的除数值(属性1)应该最有可能是0,因为你希望纹理坐标被设置为每个顶点,就像位置一样。对于颜色和其他其他属性,1是正确的值,以便它们在每个实例中应用。

另外,正如我在评论中提到的,你可能还想使用点精灵。虽然它们不能提供与绘制单个四边形相同的灵活性,但它们通常可以用于精灵。对于点精灵,每个精灵只需要一个顶点,并且纹理坐标会自动生成。我在这里给出了一个关于如何使用点精灵的大纲,包括如何应用纹理:在现代OpenGL中渲染大的圆形点。