0

I'm trying to implement instancing into my 2d Game Engine so that it can support particle systems without losing any performance. My class, ISprite, is derived from a working Sprite class. I just went through and removed all the functionality affecting single sprites and replaced it with an instancing plan in mind. Unfortunately, nothing is drawing on the screen.

Here is the relevant information:

Vertex Shader

#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;
}

Fragment Shader

#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 - Prepares all sprites for drawing, called once.

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 - called by each sprite, each frame. Sends data to static vectors

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);
}

Draw Sprites - called once each frame, actually draws the sprites

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);
}
red_always_mafia
  • 179
  • 2
  • 10
  • Not directly addressing the problem with your code, but is there a reason you are drawing a quad for each sprite, instead of using point sprites? That would seem much easier and more efficient unless it does not meet your needs. – Reto Koradi Sep 21 '15 at 00:17
  • Well I want to put textures over the quad. I don't really have a good reason for doing things the exact way i am. All of those reasons are going to be "That's how they did it on learnopenGL.com" – red_always_mafia Sep 21 '15 at 00:39
  • You can apply textures to point sprites. My answer here outlines how to do that: http://stackoverflow.com/questions/27098315/render-large-circular-points-in-modern-opengl. – Reto Koradi Sep 21 '15 at 00:59

1 Answers1

0

There could be a lot of reasons why nothing is rendered. Problems with the transformations, coordinates out of range, etc. But one thing related to instancing definitely looks wrong in the posted code:

glBindVertexArray(0);

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

The first argument to glVertexAttribDivisor() is the location of a vertex attribute, not the name of a buffer. Also, the state set by this call is part of the VAO state, so you should make these calls while the VAO is still bound.

So the calls should look like this:

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

glBindVertexArray(0);

where the first arguments to glVertexAttribDivisor() match the location values you also use as the first argument to glVertexAttribPointer() and glEnableVertexAttribArray().

The divisor value for the texture coordinates (attribute 1) should most likely be 0, since you want the texture coordinates to be set per vertex, just like the positions. For the colors and other remaining attributes, 1 is the correct value so that they are applied per instance.

Also, as I mentioned in a comment, you may also want to look into using point sprites. While they do not offer the same flexibility you can get from drawing individual quads, they can often be used for sprites. With point sprites, you only need one vertex per sprite, and the texture coordinates are generated automatically. My answer here gives an outline on how point sprites are used, including how to apply textures to them: Render large circular points in modern OpenGL.

Community
  • 1
  • 1
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • I made the changes to attrib divisor call and fixed up the binding of the VAO. Unfortunately still no drawing. The very same calculateTransform() function works with my non-instanced sprite class, so I don't think it's that. To try and check if the stuff was maybe out of range, i zoomed the camera all the way out. I am eventually going to be using a texture atlas so the texture coordinates won't be the same for each instance. – red_always_mafia Sep 21 '15 at 01:51
  • If you set the divisor to 1 for the texture coordinates, all 4 vertices of each instance will have the same texture coordinates, which won't give you texturing. – Reto Koradi Sep 21 '15 at 02:10
  • Hm, I'm a bit confused on that. I push back all 12 of the sprite's texture coordinates in prepareDraw. Doesn't each instance get its own personal texture coordinates? My understanding is that it would walk down the array – red_always_mafia Sep 21 '15 at 02:17
  • With divisor 0, the vertices within one instance get different attribute values, and those values are then repeated for each instance. With divisor 1, the vertices within one instance all get the same attribute value, and that value changes per instance. – Reto Koradi Sep 21 '15 at 02:41
  • Hm, I do want the values to affect different vertices, but i don't want the values to be repeated for each instance. – red_always_mafia Sep 21 '15 at 02:47