4
votes

Hi I am trying to render lots of axis aligned cubes, with glDrawArraysInstanced(). Each cube of fixed size can only vary on its center position and color. Also each cube only takes few different colors. So I want to potentially render millions of cubes with following per instance data:

struct CubeInfo {
  Eigen::Vector3f center; // center of the cube (x,y,z)
  int labelId;  // label of the cube which affects its color
};

So I am using the following vertex shader:

#version 330

uniform mat4 mvp_matrix;

//regular vertex attributes
layout(location = 0) in vec3 vertex_position;

// Per Instance variables
layout(location = 1) in vec3 cube_center;
layout(location = 2) in int cube_label;

// color out to frag shader
out vec4 color_out;

void main(void) {
  // Add offset cube_center
  vec4 new_pos = vec4(vertex_position + cube_center, 1);
  // Calculate vertex position in screen space
  gl_Position = mvp_matrix * new_pos;

  // Set color_out based on label
  switch (cube_label) {
  case 1:
    color_out = vec4(0.5, 0.25, 0.5, 1);
    break;
  case 2:
    color_out = vec4(0.75, 0.0, 0.0, 1);
    break;
  case 3:
    color_out = vec4(0.0, 0.0, 0.5, 1);
    break;
  case 4:
    color_out = vec4(0.75, 1.0, 0.0, 1);
    break;
  default:
    color_out = vec4(0.5, 0.5, 0.5, 1); // Grey
    break;
  }
}

and the corresponding fragment shader:

#version 330

in vec4 color_out;
out vec4 fragColor;

void main()
{
    // Set fragment color from texture
    fragColor = color_out;
}

However color_out always takes the default gray value, even though, cube_label values are between 1 to 4. This is my problem. Am I doing something wrong in the shader above**?**

I initialized the cubeInfo vbo with random labelIds between 1-4. So I am expecting to see a colorful output than following: enter image description here

This is my render code, which makes use of Qt's QGLShaderProgram and QGLBuffer wrapper:

  // Enable back face culling
  glEnable(GL_CULL_FACE);
  cubeShaderProgram_.bind();

  // Set the vertexbuffer stuff (Simply 36 vertices for cube)
  cubeVertexBuffer_.bind();
  cubeShaderProgram_.setAttributeBuffer("vertex_position", GL_FLOAT, 0, 3, 0);
  cubeShaderProgram_.enableAttributeArray("vertex_position");
  cubeVertexBuffer_.release();

  // Set the per instance buffer stuff
  cubeInstanceBuffer_.bind();

  cubeShaderProgram_.setAttributeBuffer("cube_center", GL_FLOAT, offsetof(CubeInfo,center), 3, sizeof(CubeInfo));
  cubeShaderProgram_.enableAttributeArray("cube_center");
  int center_location = cubeShaderProgram_.attributeLocation("cube_center");
  glVertexAttribDivisor(center_location, 1);

  cubeShaderProgram_.setAttributeBuffer("cube_label", GL_INT, offsetof(CubeInfo,labelId), 1, sizeof(CubeInfo));
  cubeShaderProgram_.enableAttributeArray("cube_label");
  int label_location = cubeShaderProgram_.attributeLocation("cube_label");
  glVertexAttribDivisor(label_location, 1);

  cubeInstanceBuffer_.release();

  // Do Instanced Renering
  glDrawArraysInstanced(GL_TRIANGLES, 0, 36, displayed_num_cubes_ );

  cubeShaderProgram_.disableAttributeArray("vertex_position");
  cubeShaderProgram_.disableAttributeArray("cube_center");
  cubeShaderProgram_.disableAttributeArray("cube_label");

  cubeShaderProgram_.release();

Apart from my primary question above (color problem), is this a good way to do Minecraft?

Update If I change my CubeInfo.labelId attribute from int to float, and the corresponding vertex shader variable cube_label to also float, it Works!!. Why is it so? This page says GLSL suppoers int type. For me, I would prefer labelId/cube_label to be some int/short.

Update2:

Even if i just change to GL_FLOAT instead of GL_INT in the following line of my render code, I get proper colors.

cubeShaderProgram_.setAttributeBuffer("cube_label", GL_INT, offsetof(CubeInfo,labelId), 1, sizeof(CubeInfo));
2
Does it work without Qt? I would nail the problem down... - lpapp
I nailed the problem to be the int type for cube_label and CubeInfo.labelId. See my update. Why does int type does not work? - iNFINITEi

2 Answers

2
votes

The problem with your label attribute is, that it is an integer attribute, but your don't set it as integer attribute. Qt's setAttributeBuffer functions don't know anything about integer attributes, they all use glVertexAttribPointer under the hood, which takes the vertex data in any arbitrary format and converts it into float to pass it into an in float attribute, which doesn't match the in int from your shader (so the attribute will probably just remain at some random default value, or get some undefined values).

To actually pass data into a real integer vertex attribute (which is something entirely different from a float attribute and wasn't introduced until GL 3+), you need the function glVertexAttribIPointer (note the I in there, and similar D for in double attributes, just using GL_DOUBLE won't work in this case either). But sadly enough Qt, not being really fit for GL 3+ yet, doesn't seem to have a wrapper for that. So you will either have to do it manually using:

glVertexAttribIPointer(cubeShaderProgram_.attributeLocation("cube_label"), 
                       1, GL_INT, sizeof(CubeInfo), 
                       static_cast<const char*>(0)+offsetof(CubeInfo,labelId));

instead of the cubeShaderProgram_.setAttributeBuffer call, or use an in float attribute instead.

0
votes

If you want to use the color assigned in vertex shader, you should at least write a trivial fragment shader like:

void main()
{
   gl_FragColor = color_out;
}

Update

I think that either you can't pass cube_label to your vertex shader or you didn't set them in the first place (in the structure). Latter one is more likely but you can replace your switch-case with the following line to see the real value passed.

color_out = vec4(float(cube_label) / 4.0, 0, 0, 1.0); 

Update2

Once I had a similar problem with Intel GPUs (drivers). No matter what I tried couldn't pass integer values. However, same shader was working flawlessly on NVIDIA so as in your case I converted int into float.