I am trying to get a hold of how memoryBarrier() works in OpenGL 4.4
I tried the following once with a texture image and once with Shader Storage Buffer Object (SSBO).
The basic idea is to create an array of flags for however many objects that need to be rendered in my scene and then perform a simple test in the geometry shader.
For each primitive in GS, if at least one vertex passes the test, it
sets the corresponding flag in the array at the location specified
by this primitive's object ID (Object IDs are passed to GS as vertex
attributes).
I then perform a memoryBarrier() to make sure all threads have written their values.
Next, I have all primitives read from the flags array and only emit a vertex if the flag is set.
Here is some code from my shaders to explain:
// Vertex Shader:
#version 440
uniform mat4 model_view;
uniform mat4 projection;
layout(location = 0) in vec3 in_pos;
layout(location = 1) in vec3 in_color;
layout(location = 2) in int lineID;
out VS_GS_INTERFACE
{
vec4 position;
vec4 color;
int lineID;
} vs_out;
void main(void) {
vec4 pos = vec4(in_pos, 1.0);
vs_out.position = pos;
vs_out.color = vec4(in_colo, 1.0);
vs_out.lineID = lineID;
gl_Position = projection * model_view * pos;
}
and here is a simple Geometry shader in which I use only a simple test based on lineID ( I realize this test doesn't need a shared data structure but this is just to test program behavior)
#version 440
layout (lines) in;
layout (line_strip, max_vertices = 2) out;
layout (std430, binding = 0) buffer BO {
int IDs[];
};
in VS_GS_INTERFACE
{
vec4 position;
vec4 color;
int lineID;
} gs_in[];
out vec4 f_color;
void main()
{
if(gs_in[0].lineID < 500)
{
IDs[gs_in[0].lineID] = 1;
}
else
{
IDs[gs_in[0].lineID] = -1;
}
memoryBarrier();
// read back the flag value
int flag = IDs[gs_in[0].lineID];
if ( flag > 0)
{
int n;
for( n = 0; n < gl_in.length(), n++)
{
f_color = gs_in[n].color;
gl_Position = gl_in[n].gl_Position;
emitVertex();
}
}
}
No matter what value I put instead of 500, this code always renders only 2 objects. If I change the condition for rendering in the GS to if( flag > = 0) it seems to me that all objects are rendered which means the -1 is never written by the time these IDs are read back by the shader.
Can someone please explain why the writes are not coherently visible to all shader invocations despite the memoryBarrier() and what would be the most efficient work around to get this to work?
Thanks.
Related
I have code trying to upload data into an OpenGL shader, but when I call glGetAttribLocation() going for the array of data I am looking for, it always returns -1 as location (thus not found). I have no idea how to debug this issue in the first place, since the variables are in the code (albeit the vertex shader only passes it on to the geometry shader).
Can someone help me figure out why the glGetAttribLocation returns not found? Other items, like worldMatrix for example, when using glGetUniformLocation(), work just fine.
C++ Code trying to get the attribute id:
for (unsigned int i = 0; i < _nNumCameras; ++i) {
const auto glName = "texcoords[" + std::to_string(i) + "]";
const auto location = glGetAttribLocation(id(), glName.c_str());
if (location == -1) {
continue;
}
Vertex Shader:
#version 430
#define NUM_CAMERAS 3
uniform mat4 worldMatrix;
uniform mat4 viewProjMatrix;
layout(location = 0)in vec3 position;
layout(location = 1)in vec3 normal;
layout(location = 2)in float radius;
in vec2 texcoords[NUM_CAMERAS];
in uint cameraIds[NUM_CAMERAS];
out vec4 gl_Position;
out VS_OUT {
vec2 v_texCoords[NUM_CAMERAS];
vec3 v_normal;
uint cameraIDs[NUM_CAMERAS];
} vs_out;
void main()
{
gl_Position.xyz = position.xyz;
gl_Position.w = 1;
gl_Position = worldMatrix * gl_Position;
gl_Position = viewProjMatrix * gl_Position;
vs_out.v_texCoords = texcoords;
vs_out.cameraIDs = cameraIds;
vs_out.v_normal = normal;
}
Geometry Shader:
#version 430
#define NUM_CAMERAS 3
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out VS_OUT {
vec2 v_texCoords[NUM_CAMERAS];
vec3 v_normal;
uint cameraIDs[NUM_CAMERAS];
} gs_in[];
out GS_OUT {
vec2 v_texcoord;
} gs_out;
flat out uint camera_id_unique;
void main() {
// Code selecting best camera texture
...
///
gl_Position = gl_in[0].gl_Position;
gs_out.v_texcoord = gs_in[0].v_texCoords[camera_id_unique];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
gs_out.v_texcoord = gs_in[1].v_texCoords[camera_id_unique];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
gs_out.v_texcoord = gs_in[2].v_texCoords[camera_id_unique];
EmitVertex();
EndPrimitive();
}
Fragment Shader:
#version 430
#define NUM_CAMERAS 3
uniform sampler2D colorTextures[NUM_CAMERAS];
in GS_OUT {
vec2 v_texcoord;
} fs_in;
flat in uint camera_id_unique;
out vec4 color;
void main(){
color = texture(colorTextures[camera_id_unique], fs_in.v_texcoord);
}
Arrayed program resources work in different ways depending on whether they are arrays of basic types or arrays of structs (or arrays). Resources that are arrays of basic types only expose the entire array as a single resource, whose name is "name[0]" and which has an explicit array size (if you query that property). Other arrayed resources expose separate names for each array element.
Since texcoords is an array of basic types, there is no "texcoords[2]" or "texcoords[1]"; there is only "texcoords[0]". Arrayed attributes are always assigned contiguous locations, the locations for indices 1 and 2 will simply be 1 or 2 plus the location for index 0.
I've been learning OpenGL (actually OpenTK) for a month or so, and I've started playing with geometry shaders. I originally wrote a much more complicated shader, which didn't work, so I've stripped everything down a simple passthrough shader, which still doesn't work.
Vertex Shader:
#version 420 core
layout (location = 0) in vec3 position;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(position, 1);
}
Geometry Shader:
#version 420 core
layout (triangles) in;
layout (triangle_strip) out;
void main()
{
int i;
for(i = 0; i < gl_in.length(); i++)
{
gl_Position = gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}
Fragment Shader:
#version 420 core
layout (binding = 0) uniform sampler2D diffuse;
layout(location = 0) out vec4 color;
void main()
{
color = vec4(1, 1, 1, 1);
}
It should draw a white square in the bottom right of the window, and without the Geometry shader, it does, which is what I would expect. With the Geometry Shader, it renders nothing. Info logs show everything compiles and links fine.
What am I missing?
There must be a max_vertices declaration for the output. The number must be a compile-time constant, and it defines the maximum number of vertices that will be written by a single invocation of the GS. It may be no larger than the implementation-defined limit of MAX_GEOMETRY_OUTPUT_VERTICES
I'm currently working with skeletal animation and I'm really close to getting it working. Currently, I have a struct that has a matrix with 100 spots ( this is so that I can max have 100 joints ) like so :
struct skelShader {
glm::mat4 currentJointTrans[100];
};
The struct should be binded in the shader, I've done it like this:
glGenBuffers(1, &sksBuff);
glBindBuffer(GL_UNIFORM_BUFFER, sksBuff);
// bind buffer to work further with it...
// allocate memory for the buffer in the GPU
glBufferData(GL_UNIFORM_BUFFER, sizeof(skelShader), NULL, GL_STATIC_DRAW);
// because we hard-coded "binding=3" in the shader code we can do this:
// bind Uniform Buffer to binding point 3 (without caring about index of UBO)
glBindBufferBase(GL_UNIFORM_BUFFER, 4, sksBuff);
// good practice, unbind buffer
glBindBuffer(GL_UNIFORM_BUFFER, 0);
sksBuff is just an GLuint.
I fill this array with new values every render/frame that goes by, these values are the new transformations for the joints. I do it like this:
for (int i = 0; i < skeleton.size(); i++) {
globalSkelInfo.currentJointTrans[i] = skeleton[i]->transformMat[currentFrame - 1] * skeleton[i]->globalBindPosMat;
}
This is working correctly for the root joint, but the rest of the joints/mesh remains in bind pose. The problem should be located in where I update the array. Currently I do it like this in the render function after I've done the multiplication for each joint:
for (int i = 0; i < skeleton.size(); i++) {
glUniformMatrix4fv(glGetUniformLocation(aShaderProgram, ("currentJointTrans[" + std::to_string(i) + "]").c_str()),
1, GL_FALSE, glm::value_ptr(globalSkelInfo.currentJointTrans[i]));
}
After this I draw. The root joints values seem to be moving correctly, but the rest of the mesh is in bindpose and doesn't move. In the Vertex Shader I try to update the matrix like this:
#version 440
const int maxJoints = 100;
const int maxWeights = 4;
layout(location = 0) in vec3 vertex_position;
layout(location = 1) in vec2 vertex_UV;
layout(location = 2) in vec3 vertex_normal;
layout(location = 3) in vec4 vertex_weight;
layout(location = 4) in ivec4 vertex_controllers;
out vec2 outUVs;
out vec3 outNorm;
layout(binding = 3 , std140) uniform uniformBlock
{
vec3 camPos;
mat4 world;
mat4 LookAt;
mat4 projection;
mat4 MVP;
};
layout(binding = 4 , std140) uniform animationStruct
{
mat4 currentJointTrans[maxJoints];
};
void main() {
vec4 finalModelPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
for (int i = 0; i < 4; i++) {
mat4 jointTrans = currentJointTrans[vertex_controllers[i]];
vec4 posePos = jointTrans * vec4(vertex_position, 1.0);
finalModelPos += posePos * vertex_weight[i];
vec4 worldNormal = jointTrans * vec4(vertex_normal, 0.0);
finalNormal += worldNormal * vertex_weight[i];
}
gl_Position = MVP * finalModelPos;
outNorm = finalNormal.xyz;
outUVs = vertex_UV;
}
My theory is that the updating of the struct skelShader with my currentJointTrans array is incorrect. Any tips on how I should do this instead?
glUniform* calls cannot set data in uniform buffers. Indeed, the whole point of uniform buffers is that the uniform data comes from a buffer object. That's why you had to create one.
So if you want to set the uniform data for a uniform block, you set that data into the buffer object.
I created two shaders for my program to render simple objects.
Vertex shader source:
#version 400 core
layout (location = 1) in vec4 i_vertexCoords;
layout (location = 2) in vec3 i_textureCoords;
layout (location = 3) in vec3 i_normalCoords;
layout (location = 4) in int i_material;
uniform mat4 u_transform;
out VertexData {
vec3 textureCoords;
vec3 normalCoords;
int material;
} vs_out;
void main() {
vs_out.textureCoords = i_textureCoords;
vs_out.material = i_material;
gl_Position = u_transform * i_vertexCoords;
vs_out.normalCoords = gl_Position.xyz;
}
Fragment shader source:
#version 400 core
struct MaterialStruct {
int ambientTexutre;
int diffuseTexture;
int specularTexture;
int bumpTexture;
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
float specularComponent;
float alpha;
int illuminationModel;
};
in VertexData {
vec3 textureCoords;
vec3 normalCoords;
int material;
} vs_out;
layout (std140) uniform MaterialsBlock {
MaterialStruct materials[8];
} u_materials;
uniform sampler2D u_samplers[16];
out vec4 fs_color;
void main() {
MaterialStruct m = u_materials.materials[vs_out.material];
fs_color = vec4(m.diffuseColor.rgb, m.diffuseColor.a * m.alpha);
}
Program created with this two shaders renders picture 2
When I change main() function contents to next:
void main() {
MaterialStruct m = u_materials.materials[vs_out.material];
fs_color = vec4(m.diffuseColor.rgb * (vs_out.normalCoords.z + 0.5), m.diffuseColor.a * m.alpha);
}
It renders picture 1, but materials still exists (if I'm trying to select material from u_materials.materials manually it works). Shader thinks what vs_out.material is constant and equals 0, but it isn't. Data is not changed (excluding transformation matrix)
Could someone explain the solution of this problem?
The GLSL 4.5 spec states in section 4.3.4 "Input Variables":
Fragment shader inputs that are signed or unsigned integers, integer vectors, or any double-precision
floating-point type must be qualified with the interpolation qualifier flat.
You can't use interpolation with those types, and actually, your code shouldn't compile on a strict implementation.
I'm working on an ASSIMP skeletal animation loader and renderer and right now all the data is correctly loaded and interpolated at its current timeframe. However, there is still one part that isn't working as it should and that's the vertex shader stage.
Via a VBO I pass in two vec4s that contain the bone IDs and the weights for each vertex (up to a maximum of 4 bones/weights per vertex) and the vertex shader has a matrix array of 100 bone transformations (pre-calculated per frame) that are indexed via the bone IDs.
However, it seems that the bones uniform doesn't contain the proper transformations. For debugging purposes I colored the model with the weight values and the bone IDs value and they contain a color (and thus valid values). However, when I transform my vertex via the bone transformation and color the model with the result, the entire model is colored black, meaning the transformation matrices are all 0.0. So they're not initialized properly.
I think the problem is with passing the matrices to the uniform array, or perhaps the maximum size of uniforms allowed (I also tried setting the number of uniform matrices to 32 (number of bones on current model) but without effect)?
Before passing the information to the shader, the transformation matrices are indeed valid matrices (not identity/empty matrices) so the fault should probably be in the GLSL shader or the passing of the uniforms.
The following code is from the vertex shader:
#version 330
layout (location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 tangent;
layout(location = 3) in vec3 color;
layout(location = 4) in vec2 texCoord;
layout(location = 5) in ivec4 boneIDs;
layout(location = 6) in vec4 weights;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 bones[100];
out vec2 TexCoord;
out vec4 colorz;
void main()
{
vec4 newPos = vec4(position, 1.0);
colorz = vec4(0.0, 1.0, 0.0, 1.0);
if (weights != vec4(0.0, 0.0, 0.0, 0.0))
{
mat4 boneTransform = bones[boneIDs[0]] * weights[0];
boneTransform += bones[boneIDs[1]] * weights[1];
boneTransform += bones[boneIDs[2]] * weights[2];
boneTransform += bones[boneIDs[3]] * weights[3];
// newPos = boneTransform * vec4(position, 1.0);
vec4 test = vec4(1.0);
colorz = boneTransform * test;
// newPos = boneTransform * newPos;
}
TexCoord = texCoord;
gl_Position = projection * view * model * newPos;
}
The following snippet of code pass the matrix data to the GLSL shader:
// Sets bone transformation matrices
void Shader::SetBoneMatrix(GLint index, aiMatrix4x4 matrix)
{
glm::mat4 mat = glm::transpose(glm::make_mat4(&matrix.a1));
glUniformMatrix3fv(boneLocations[index], 1, GL_FALSE, glm::value_ptr(mat));
}
Also the code that gets all the uniform locations of the bones array:
for(unsigned int i = 0; i < 100; i++)
{
string name = "bones[";
string number;
stringstream ss;
ss << i;
ss >> number;
name += number;
name += ']';
boneLocations[i] = glGetUniformLocation(this->program, name.c_str());
}
Oké, via glslDevil I came across a continous GL_INVALID_OPERATION error when setting the bone matrix to the shader via glUniformMatrix. The origin of the problem was indeed at the stage where the program passes the information along to the shader.
It is quite a stupid mistake actually since I'm using glUniformMatrix3f instead of glUniformMatrix4f. Changing this did indeed solve the problem and the animations are working perfectly right now.