Hello,
I decided to implement Batch Rendering using SSBO, where SSBO will contain an array of structs that contains all data for each object (lets say 100 objects). Along the way I also had to change the VBO in order to accommodate some changes from the previous pipeline.
The problem is, nothing renders. I doubled checked my math and everything checks out. glGetError() also showed no issues (perhaps I didn’t check for the proper enumerator), so I figured to ask for the forum’s opinion.
The setup is very simple:
- each struct has a bunch of flags and other data for each object.
- as the vertex data is pushed into VBO, the object’s index in the array is also pushed into the VBO.
- shader accesses the struct using the object index that was acquired from vertex data
- shader uses the struct’s data
This is the struct I am pushing into the SSBO:
struct objectVarsData {
float posVec[3]; // 12 12
float rotVec[3]; // 12 24
float scaleVec; // 4 28
int textureLayer; // 4 32
int drawEnable; // 4 36
int colorMapEnable; // 4 40
float colorVec[3]; // 12 52
float alphaScale; // 4 56
int normalMapEnable; // 4 60
int specularMapEnable; // 4 64
int lightMapEnable; // 4 68
float lightVec[3]; // 12 80
float lightScale; // 4 84
int controlEnable; // 4 88
float controlColorVec[3]; // 12 100
};
This is how initialize my SSBO and VBO:
// Object VBO
glGenBuffers(1, &(this->objectVBO));
glBindBuffer(GL_ARRAY_BUFFER, this->objectVBO);
glBufferData(GL_ARRAY_BUFFER, graphics2DMaximumVBOSize_Byte, NULL, GL_STATIC_DRAW);
glGenVertexArrays(1, &(this->objectVAO));
glBindVertexArray(this->objectVAO);
glBindBuffer(GL_ARRAY_BUFFER, this->objectVAO);
glVertexAttribPointer(0, graphicsVertexPosSize, GL_FLOAT, GL_FALSE, graphicsVertexDataSize_Byte, NULL);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, graphicsVertexTangentSize, GL_FLOAT, GL_FALSE, graphicsVertexDataSize_Byte, (GLvoid*)(sizeof(GLfloat) * graphicsVertexTangentOffset));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, graphicsVertexBitangentSize, GL_FLOAT, GL_FALSE, graphicsVertexDataSize_Byte, (GLvoid*)(sizeof(GLfloat) * graphicsVertexBitangentOffset));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, graphicsVertexNormalSize, GL_FLOAT, GL_FALSE, graphicsVertexDataSize_Byte, (GLvoid*)(sizeof(GLfloat) * graphicsVertexNormalOffset));
glEnableVertexAttribArray(3);
glVertexAttribPointer(4, graphicsVertexUVSize, GL_FLOAT, GL_FALSE, graphicsVertexDataSize_Byte, (GLvoid*)(sizeof(GLfloat) * graphicsVertexUVOffset));
glEnableVertexAttribArray(4);
glVertexAttribIPointer(5, graphicsVertexObjectIndexSize, GL_UNSIGNED_INT, graphicsVertexDataSize_Byte, (GLvoid*)(sizeof(GLfloat) * graphicsVertexObjectIndexOffset));
glEnableVertexAttribArray(5);
// Object SSBO
glGenBuffers(1, &(this->objectSSBO));
glBindBuffer(GL_SHADER_STORAGE_BUFFER, this->objectSSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, graphics2DMaximumSSBOSize_Byte, NULL, GL_STATIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this->objectSSBO);
Then I push everything into the substack (currently I don’t account for substack overflow for testing I just do 1 object):
unsigned int objectCount = 0;
unsigned int vertexIndex = 0;
graphicsObject * bufferObject;
std::cout << " - Pushing Objects:" << std::endl;
for (unsigned int i = 0; i < this->objectQueue->size; i++) {
bufferObject = (graphicsObject *) this->objectQueue->lowestEntry->attachedObject;
std::cout << " Object " << bufferObject->objectID << " with " << bufferObject->meshSize << " geometries at " << graphics2DStartDepth + graphics2DDepthChange*(objectCount) << std::endl;
this->push2DObject( bufferObject, &objectCount, &vertexIndex);
graphicsLinkedListRotateBackward(this->objectQueue);
if (objectCount == graphics2DMaximumObjects) { // Assumes 1 object per push
std::cout << " - Flushing Substack (Substack full)..." << std::endl;
std::cout << " Object Count: " << objectCount << std::endl;
std::cout << " Vertex Count: " << vertexIndex << std::endl;
this->drawSubStack(vertexIndex); // Stages 1,2 and 3
this->drawToBuffer(); // Stage 4
std::cout << " - Continuing Pushing..." << std::endl;
objectCount = 0;
vertexIndex = 0;
}
}
if (vertexIndex > 0) {
std::cout << " - Flushing Substack (Remains):" << std::endl;
std::cout << " Object Count: " << objectCount << std::endl;
std::cout << " Vertex Count: " << vertexIndex << std::endl;
this->drawSubStack(vertexIndex); // Stages 1,2 and 3
this->drawToBuffer(); // Stage 4
}
Where each object pushed using:
unsigned int objectIndex = *objectCount;
objectCount[0]++;
// Load the data into buffer struct
this->objectDataBuffer.posVec[0] = object->posVec[0];
this->objectDataBuffer.posVec[1] = object->posVec[1];
this->objectDataBuffer.posVec[2] = 1.0 - graphics2DDepthChange*(*objectCount); // change if the triangles are outside the camera limit
this->objectDataBuffer.rotVec[0] = object->rotVec[0];
this->objectDataBuffer.rotVec[1] = object->rotVec[1];
this->objectDataBuffer.rotVec[2] = object->rotVec[2];
this->objectDataBuffer.scaleVec = *(object->scaleVec);
this->objectDataBuffer.textureLayer = object->textureLayer;
this->objectDataBuffer.drawEnable = object->drawEnable;
this->objectDataBuffer.colorMapEnable = object->colorMapEnable;
this->objectDataBuffer.colorVec[0] = object->colorVec[0];
this->objectDataBuffer.colorVec[1] = object->colorVec[1];
this->objectDataBuffer.colorVec[2] = object->colorVec[2];
this->objectDataBuffer.alphaScale = *(object->alphaScale);
this->objectDataBuffer.normalMapEnable = object->normalMapEnable;
this->objectDataBuffer.specularMapEnable = object->specularMapEnable;
this->objectDataBuffer.lightMapEnable = object->lightMapEnable;
this->objectDataBuffer.lightVec[0] = object->lightVec[0];
this->objectDataBuffer.lightVec[1] = object->lightVec[1];
this->objectDataBuffer.lightVec[2] = object->lightVec[2];
this->objectDataBuffer.lightScale = *(object->lightScale);
this->objectDataBuffer.controlEnable = object->controlEnable;
this->objectDataBuffer.controlColorVec[0] = object->controlColorVec[0];
this->objectDataBuffer.controlColorVec[1] = object->controlColorVec[1];
this->objectDataBuffer.controlColorVec[2] = object->controlColorVec[2];
// Load the data into OpenGL buffers
// Shader Storage Data
glBindBuffer(GL_SHADER_STORAGE_BUFFER, this->objectSSBO);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(objectVarsData)*objectIndex, sizeof(objectVarsData), &(this->objectDataBuffer));
// Vertex Array Data
glBindBuffer(GL_ARRAY_BUFFER, this->objectVBO);
for (int i = 0; i < object->meshSize; i++) {
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex), graphicsVertexMeshDataSize_Byte, &object->meshPoints[(*vertexIndex) * graphicsVertexMeshDataSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte, graphicsVertexUVSize_Byte, &object->uvPoints[(*vertexIndex) * graphicsVertexUVSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte + graphicsVertexUVSize_Byte, graphicsVertexObjectIndexSize_Byte, &objectIndex);
vertexIndex[0]++;
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex), graphicsVertexMeshDataSize_Byte, &object->meshPoints[(*vertexIndex) * graphicsVertexMeshDataSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte, graphicsVertexUVSize_Byte, &object->uvPoints[(*vertexIndex) * graphicsVertexUVSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte + graphicsVertexUVSize_Byte, graphicsVertexObjectIndexSize_Byte, &objectIndex);
vertexIndex[0]++;
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex), graphicsVertexMeshDataSize_Byte, &object->meshPoints[(*vertexIndex) * graphicsVertexMeshDataSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte, graphicsVertexUVSize_Byte, &object->uvPoints[(*vertexIndex) * graphicsVertexUVSize]);
glBufferSubData(GL_ARRAY_BUFFER, graphicsVertexDataSize_Byte * (*vertexIndex) + graphicsVertexMeshDataSize_Byte + graphicsVertexUVSize_Byte, graphicsVertexObjectIndexSize_Byte, &objectIndex);
vertexIndex[0]++;
if ((*vertexIndex / 3) >= graphics2DMaximumTriangles) {
std::cout << " Object Count: " << objectCount << std::endl;
std::cout << " Vertex Count: " << vertexIndex << std::endl;
this->drawSubStack((*vertexIndex)); // Stages 1,2 and 3
this->drawToBuffer(); // Stage 4
(*objectCount) = 0;
(*vertexIndex) = 0;
}
}
Finally, I draw the objects:
glBindFramebuffer(GL_FRAMEBUFFER, this->subSceneFBO1);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(this->subSceneShaderArray[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->textureAsset->colorMapID);
glUniform1i(this->stage1ColorMapLocation, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, this->textureAsset->normalMapID);
glUniform1i(this->stage1NormalMapLocation, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, this->textureAsset->specularMapID);
glUniform1i(this->stage1SpecularMapLocation, 2);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, this->textureAsset->lightMapID);
glUniform1i(this->stage1LightMapLocation, 3);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
// Binding SSBO
glBindBuffer(GL_SHADER_STORAGE_BUFFER, this->objectSSBO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this->objectSSBO);
// Binding VBO
glBindVertexArray(this->objectVAO);
glBindBuffer(GL_ARRAY_BUFFER, this->objectVBO);
glDrawArrays(GL_TRIANGLES, 0, vertexIndex*graphicsVertexDataSize);
Vertex shader:
// shadertype=glsl
#version 450
layout (location = 0) in vec3 vertexPos;
layout (location = 1) in vec3 tangent;
layout (location = 2) in vec3 bitangent;
layout (location = 3) in vec3 normal;
layout (location = 4) in vec2 texCoord;
layout (location = 5) in uint objectIndex;
layout(std140, binding = 0) uniform cameraVars
{
mat4 projectionMat; // 64 bytes
mat4 cameraMat; // 128 bytes
};
struct objectVarsData {
vec3 posVec;
vec3 rotVec;
vec3 scaleVec;
int textureLayer;
int drawEnable;
int colorMapEnable;
vec3 colorVec;
float alphaScale;
int normalMapEnable;
int specularMapEnable;
int lightMapEnable;
vec3 lightVec;
float lightScale;
int controlEnable;
highp vec3 controlColorVec;
};
layout(std430, binding = 1) buffer objectVars
{
objectVarsData objectVarsArray[10000];
};
out vec2 tex_coord;
out mat3 normal_tran_mat;
out float object_depth;
flat out uint object_index;
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void main() {
object_index = objectIndex;
objectVarsData objectVarsDataBuffer = objectVarsArray[object_index];
vec3 objectPosVec = objectVarsDataBuffer.posVec;
vec3 objectRotVec = objectVarsDataBuffer.rotVec;
vec3 objectScaleVec = objectVarsDataBuffer.scaleVec;
vec3 vertexTangent = tangent;
vec3 vertexBitangent = bitangent;
vec3 vertexNormal = normal;
// WORLD SPACE MATRIX CREATION
// Position
mat4 objectPosMat = mat4(1.0);
objectPosMat[3].xyz = vec3(objectPosVec.x, -objectPosVec.y, objectPosVec.z);
// Rotation X
mat4 objectRotXMat = mat4(1.0);
objectRotXMat[1] = vec4(0.0, cos(objectRotVec[0]), sin(objectRotVec[0]), 0.0);
objectRotXMat[2] = vec4(0.0, -sin(objectRotVec[0]), cos(objectRotVec[0]), 0.0);
// Rotation Y
mat4 objectRotYMat = mat4(1.0);
objectRotYMat[0] = vec4(cos(objectRotVec[1]), 0.0, -sin(objectRotVec[1]), 0.0);
objectRotYMat[2] = vec4(sin(objectRotVec[1]), 0.0, cos(objectRotVec[1]), 0.0);
// Rotation Z
mat4 objectRotZMat = mat4(1.0);
objectRotZMat[0] = vec4( cos(objectRotVec[2]), sin(objectRotVec[2]), 0.0, 0.0);
objectRotZMat[1] = vec4(-sin(objectRotVec[2]), cos(objectRotVec[2]), 0.0, 0.0);
// TOTAL TRANSFORMATION MATRIX
mat4 transformMat = cameraMat*objectPosMat*objectRotZMat*objectRotYMat*objectRotXMat; //cameraMat
// TRANSFORMING OBJECT
tex_coord = texCoord;
object_depth = 1.0 - objectPosVec[2];
vec4 bufferVec1 = normalize(transformMat*vec4(normalize(vertexTangent), 0.0));
vec4 bufferVec2 = normalize(transformMat*vec4(normalize(vertexBitangent), 0.0));
vec4 bufferVec3 = normalize(transformMat*vec4(normalize(vertexNormal), 0.0));
normal_tran_mat = mat3(bufferVec1.xyz, bufferVec2.xyz, bufferVec3.xyz);
vec3 vertexPosBuffer = vertexPos;
vertexPosBuffer.x = vertexPos.x*objectScaleVec.x;
vertexPosBuffer.y = vertexPos.y*objectScaleVec.y;
vertexPosBuffer.z = vertexPos.z*objectScaleVec.z;
gl_Position = projectionMat*transformMat*vec4(vertexPosBuffer, 1.0); //projectionMat
}
Fragment shader:
// shadertype=glsl
#version 450
uniform sampler2D colorMap;
uniform sampler2D normalMap;
uniform sampler2D specularMap;
uniform sampler2D lightMap;
struct objectVarsData {
vec3 posVec;
vec3 rotVec;
vec3 scaleVec;
int textureLayer;
int drawEnable;
int colorMapEnable;
vec3 colorVec;
float alphaScale;
int normalMapEnable;
int specularMapEnable;
int lightMapEnable;
vec3 lightVec;
float lightScale;
int controlEnable;
highp vec3 controlColorVec;
};
layout(std430, binding = 1) buffer objectVars
{
objectVarsData objectVarsArray[10000];
};
in vec2 tex_coord;
in mat3 normal_tran_mat;
in float object_depth;
flat in uint object_index;
layout(location = 0) out vec4 stage1ColorMap;
layout(location = 1) out vec4 stage1NormalMap;
layout(location = 2) out vec4 stage1SpecularMap;
layout(location = 3) out vec4 stage1LightMap;
layout(location = 4) out vec4 stage1ControlMap;
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void main() {
objectVarsData objectVarsDataBuffer = objectVarsArray[object_index];
int textureLayer = objectVarsDataBuffer.textureLayer;
int drawEnable = objectVarsDataBuffer.drawEnable;
int colorMapEnable = objectVarsDataBuffer.colorMapEnable;
vec3 colorVec = objectVarsDataBuffer.colorVec;
float alphaScale = clamp(objectVarsDataBuffer.alphaScale, 0.0, 1.0);
int normalMapEnable = objectVarsDataBuffer.normalMapEnable;
int specularMapEnable = objectVarsDataBuffer.specularMapEnable;
int lightMapEnable = objectVarsDataBuffer.lightMapEnable;
vec3 lightColorVec = objectVarsDataBuffer.lightVec;
float lightScale = objectVarsDataBuffer.lightScale;
int controlEnable = objectVarsDataBuffer.controlEnable;
highp vec3 controlColorVec = objectVarsDataBuffer.controlColorVec;
vec4 textureBuffer = texture(colorMap, tex_coord);
vec4 normalBuffer = texture(normalMap, tex_coord);
vec4 specularBuffer = texture(specularMap, tex_coord);
vec4 lightBuffer = texture(lightMap, tex_coord);
float fragmentAlpha = alphaScale*textureBuffer.a;
fragmentAlpha = clamp(fragmentAlpha, 0.0, 1.0);
if (fragmentAlpha == 1.0) {
// CONTROL MAP controlColorVec
if (controlEnable == 1) {
stage1ControlMap = vec4(controlColorVec, 1.0);
} else {
stage1ControlMap = vec4(0.0, 0.0, 0.0, 0.0);
}
// IMAGE MAPS
if (drawEnable == 1) {
if (colorMapEnable == 0) {
stage1ColorMap = vec4(colorVec, 1.0);
} else {
stage1ColorMap = vec4(textureBuffer.xyz, 1.0);
}
if (normalMapEnable == 0) {
stage1NormalMap = vec4(0.0, 0.0, 0.0, 1.0);
} else {
vec3 normalFromMap = 2.0*normalBuffer.xyz - vec3(1.0, 1.0, 1.0);
stage1NormalMap = vec4((normalize(normal_tran_mat*normalFromMap) + 1.0) / 2.0, 1.0);
}
if (specularMapEnable == 0) {
stage1SpecularMap = vec4(0.0, 0.0, 0.0, 1.0);
} else {
stage1SpecularMap = vec4(vec3(specularBuffer.x),1.0);
}
if (lightMapEnable == 0) {
stage1LightMap = vec4(0.0, 0.0, 0.0, 1.0);
} else {
stage1LightMap = vec4(lightBuffer.xyz,1.0);
}
} else {
stage1ColorMap = vec4(0.0, 0.0, 0.0, 0.0);
stage1NormalMap = vec4(0.0, 0.0, 0.0, 0.0);
stage1SpecularMap = vec4(0.0, 0.0, 0.0, 0.0);
stage1LightMap = vec4(0.0, 0.0, 0.0, 0.0);
}
gl_FragDepth = object_depth;
} else {
discard;
}
}
These are the only parts of the code that were changed, the rest of my code was unchanged from previous render version and well tested.
Any suggestions on debugging techniques for SSBOs would be awesome.
Thanks