Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Misbehaving transformations

This thread is locked; no one can reply to it. rss feed Print
Misbehaving transformations
Hepolite
Member #13,716
November 2011

I've been working on a small project of my own for some time now, and have started the process of drawing things to the screen. I've set up an OpenGL 3 environment with Allegro 5.2.1.1. All that is working about as expected.

The problem lies with my transformations. For some unknown reason, I seem to be unable to translate my objects individually. They all render with the exact same transformation, and I just can't figure out why this is happening.

For my test scene, I've made three simple structures and attempted to render them using three different transformations. This was the result:
http://imgur.com/a/ebLnw
The result I want, is this:
http://imgur.com/a/SvbEf

The code I used to render this scene is the following:

#SelectExpand
1 auto helper = hen::Core::getRenderHelper(); 2 3 if (m_program != nullptr) 4 m_program->bind(); 5 6 helper->setProjection(45.0f, 0.1f, 1000.0f); 7 helper->setView(m_pos, glm::vec3{}, glm::vec3{ 0.0f, 0.0f, 1.0f }); 8 9 for (const auto& chunk : m_chunks) 10 { 11 helper->pushMatrix(glm::translate(glm::mat4{ 1.0f }, glm::vec3{ Chunk::SIZE * chunk.second->getPosition() })); 12 chunk.second->render(); 13 helper->popMatrix(); 14 }

The chunk render() method only renders the VBO associated with said chunk, which is the following:

#SelectExpand
5void hen::render::VBOBase::render() const 6{ 7 glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); 8 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indiceBuffer); 9 10 for (const auto& attribute : m_attributes) 11 { 12 glEnableVertexAttribArray(attribute.m_index); 13 glVertexAttribPointer(attribute.m_index, attribute.m_count, attribute.m_format, GL_FALSE, m_vertexSize, (const GLvoid*)attribute.m_offset); 14 } 15 16 glDrawElements(m_renderMode, m_indiceCount, GL_UNSIGNED_INT, (void*)0); 17 18 for (const auto& attribute : m_attributes) 19 glDisableVertexAttribArray(attribute.m_index); 20 21 glBindBuffer(GL_ARRAY_BUFFER, 0); 22 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 23}

Oddly enough, the desired result was obtained, albeit flickering, if I added an al_flip_display() inside the loop. It seems that only the first series of transformations pushed in with pushMatrix counts when flipping the display. Even weirder, any transformations applied, apply to all objects as long as they aren't popped before rendering the first object. Once one object has been drawn, any subsequent transformations are ignored. This really doesn't make any sense...

The push/popMatrix methods are as following:

#SelectExpand
2void hen::render::RenderHelper::pushMatrix(const glm::mat4& matrix) 3{ 4 if (m_matrices.size() == 0) 5 m_matrices.push(matrix); 6 else 7 m_matrices.push(m_matrices.top() * matrix); 8 9 auto block = Core::getUniformBlockManager()->get("Matrix"); 10 if (block != nullptr) 11 block->set<glm::mat4>("model", m_matrices.top()); 12} 13void hen::render::RenderHelper::popMatrix() 14{ 15 if (m_matrices.size() != 0) 16 m_matrices.pop(); 17 18 auto block = Core::getUniformBlockManager()->get("Matrix"); 19 if (block != nullptr) 20 block->set<glm::mat4>("model", m_matrices.size() == 0 ? glm::mat4{ 1.0f } : m_matrices.top()); 21}

The RenderHelper::setView and setProjection methods sends the data to the GPU in the same manner as push/popMatrix, using uniform blocks.

Creating and updating the uniform block:

#SelectExpand
4 glGenBuffers(1, &m_handle); 5 ... 6 glBindBuffer(GL_UNIFORM_BUFFER, m_handle); 7 glBufferData(GL_UNIFORM_BUFFER, m_size, nullptr, GL_STATIC_DRAW); 8 glBindBuffer(GL_UNIFORM_BUFFER, 0); 9 10 11template<typename T> 12bool hen::render::UniformBlock::set(const std::string& name, const T& value) const 13{ 14 ... 15 glBindBuffer(GL_UNIFORM_BUFFER, m_handle); 16 glBufferSubData(GL_UNIFORM_BUFFER, getOffset(name), sizeof(T), (GLvoid*)&value[0]); 17 glBindBuffer(GL_UNIFORM_BUFFER, 0); 18 return true; 19}

My vertex and fragment shader are as following:

#SelectExpand
3Vertex shader: 4#version 330 core 5 6layout (std140) uniform Matrix 7{ 8 mat4 projection; 9 mat4 view; 10 mat4 model; 11} matrices; 12 13layout(location = 0) in vec3 inPosition; 14layout(location = 1) in vec3 inNormal; 15layout(location = 2) in vec2 inUV; 16layout(location = 3) in vec4 inColor; 17 18out VertexData 19{ 20 vec3 normal; 21 vec2 uv; 22 vec4 color; 23} vertex; 24 25void main() 26{ 27 gl_Position = matrices.projection * matrices.view * matrices.model * vec4(inPosition, 1); 28 29 vertex.normal = (matrices.model * vec4(inNormal, 0)).xyz; 30 vertex.uv = inUV; 31 vertex.color = inColor; 32} 33 34Fragment shader: 35#version 330 core 36 37in VertexData 38{ 39 vec3 normal; 40 vec2 uv; 41 vec4 color; 42} vertex; 43 44out vec4 fragment; 45 46void main() 47{ 48 fragment = vec4(vertex.color.xyz, 1.0); 49}

I know that my shaders are working, and the transformations are working to some degree. I'm able to rotate/scale/translate all the objects in bulk with constant transformations, even apply multiple transformations in a row.

I've stared at this issue for quite some time now, and I just can't figure out how to fix this, can't even figure out what is going on here... :/
Any help would be highly appreciated!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I have an idea or two. Allegro keeps transformations per-bitmap. If you change the target bitmap, you change the transformations.

What does this set of code do?

Hepolite said:

template<typename T> 
bool hen::render::UniformBlock::set(const std::string& name, const T& value) const {
   ... 
   glBindBuffer(GL_UNIFORM_BUFFER, m_handle); 
   glBufferSubData(GL_UNIFORM_BUFFER, getOffset(name), sizeof(T), (GLvoid*)&value[0]); 
   glBindBuffer(GL_UNIFORM_BUFFER, 0); return true; 
}

I don't see where you are actually setting the transformations anywhere, either with OpenGL, or with Allegro.

Erin Maus
Member #7,537
July 2006
avatar

Run your program through apitrace. Then diagnose uniform block binding values at the time of the draw call via qapitrace. Look at the draw calls prior to try and isolate the problem. For example:

{"name":"610758","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/c\/3cb2683895729b1ea60991bcddd5021b.png","w":977,"h":785,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/c\/3cb2683895729b1ea60991bcddd5021b"}610758

I don't see where you are actually setting the transformations anywhere, either with OpenGL, or with Allegro.

He is using uniform block buffers. It's an OpenGL 3+ feature. See https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Hepolite
Member #13,716
November 2011

What does this set of code do?
<snip>
I don't see where you are actually setting the transformations anywhere, either with OpenGL, or with Allegro.

The code you highlighted here is the code that passes the transformations to the shaders. pushMatrix invoke the "set" method, which uploads the matrix data.

Edgar Reynaldo said:

I have an idea or two. Allegro keeps transformations per-bitmap. If you change the target bitmap, you change the transformations.

The only time this would be relevant would be during al_flip_display, if my understanding is correct. I don't modify the target bitmap in any location elsewhere. That said, though, using al_flip_display more than once per frame does yield some weird results. If used once per object, the transformation actually does work exactly as expected. Of course, this solution is utterly unacceptable for obvious reasons.

Run your program through apitrace. Then diagnose uniform block binding values at the time of the draw call via qapitrace. Look at the draw calls prior to try and isolate the problem. For example:

I've been unable to get apitrace and apply that to my program. I've used glGetBufferSubData, however, to verify that the shader has the correct model matrix before rendering the objects. If the matrix was corrupted in some form, then I likely wouldn't be able to apply any transformations at all (including the view and projection matrix), which is not the case. From visual inspection, it is clear that the view and projection matrix (which use the very same method to upload data) are working.

My rendering code is very simple at the moment. It consists of only this code here:

#SelectExpand
1 Initializing: 2 glGenVertexArrays(1, &m_vao); 3 glBindVertexArray(m_vao); 4 5 Render loop: 6 glClearColor(0.0f, 0.5f, 1.0f, 1.0f); 7 glClearDepth(1.0f); 8 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 9 10 [render code given in the first post, first block of code] 11 12 al_flip_display();

There aren't many places the problem can hide, it would seem.

From everything I have tried, only al_flip_display seem to yield some interesting behavior. Is it possible that glDrawElements does nothing before al_flip_display is invoked? I cannot think of anything else that would explain the behavior my program is currently displaying.

EDIT:
In testing, I have discovered something even stranger. When I leave my rendering to only render the objects, the transformations does not apply on a per-object basis. If I introduce some more code, however...

#SelectExpand
2for (const auto& chunk : m_chunks) 3 { 4 helper->pushMatrix(glm::translate(glm::mat4{ 1.0f }, glm::vec3{ Chunk::SIZE * chunk.second->getPosition() })); 5 6 glm::mat4 data; 7 glBindBuffer(GL_UNIFORM_BUFFER, block->getHandle()); 8 glGetBufferSubData(GL_UNIFORM_BUFFER, 128, sizeof(data), &data[0]); // Nasty hardcoded offset to grab model matrix 9 glBindBuffer(GL_UNIFORM_BUFFER, 0); 10 11 chunk.second->render(); 12 helper->popMatrix(); 13 }

This actually works. I have no idea why, but if I leave these four additional lines in, the transformations actually works just as expected. If I try to remove either of two first gl calls, the transformations stop working again. If I remove glBindBuffer(..., 0), it still works. But if I remove glGetBufferSubData, it doesn't work. Just... what?

Chris Katko
Member #1,881
January 2002
avatar

Which for lines are you specifically talking about? You've got for in a row in the middle, but it seems like you're refering to the last four.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Hepolite
Member #13,716
November 2011

I was referring to these lines:

#SelectExpand
1glm::mat4 data; 2glBindBuffer(GL_UNIFORM_BUFFER, block->getHandle()); 3glGetBufferSubData(GL_UNIFORM_BUFFER, 128, sizeof(data), &data[0]); 4glBindBuffer(GL_UNIFORM_BUFFER, 0);

It doesn't make much sense that this somehow prevents the problem from occuring. These lines shouldn't alter the render state in any meaningful way.

Elias
Member #358
May 2000

Check for OpenGL errors with glGetError. In this case, start with the glBufferSubData call I'd say then expand to others.

Hepolite said:

These lines shouldn't alter the render state in any meaningful way.

Quote:

If any rendering in the pipeline makes reference to data in the buffer object being updated by glBufferSubData, especially from the specific region being updated, that rendering must drain from the pipeline before the data store can be updated.

Since you are referencing the same buffer you update earlier, you are indeed forcing a pipeline drain. Which is the same thing that otherwise only happens when you call al_flip_display. It looks a lot like somewhere some buffer related operation fails and you are therefore overwriting the transformation. That's why I think you need to check for errors on all the buffer related operations.

--
"Either help out or stop whining" - Evert

Erin Maus
Member #7,537
July 2006
avatar

Hepolite said:

I've been unable to get apitrace and apply that to my program.

What operating system are you using? Latest apitrace (from git) works fine on FreeBSD (and by extension Linux I suppose) with my Allegro program (screenshot I posted has trace from there). I only use Allegro for input, display management, and OpenGL extension grabbing with an OpenGL 3+ forward-compatible context. apitrace is an invaluable tool when debugging OpenGL rendering.

Per Elias's suggestion, apitrace will also help catching OpenGL usage errors.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Chris Katko
Member #1,881
January 2002
avatar

Elias said:

It looks a lot like somewhere some buffer related operation fails and you are therefore overwriting the transformation. That's why I think you need to check for errors on all the buffer related operations.

THIS.^^

Anytime you have code that magically fixes due to an unrelated change?

Try making a different change, and check all pointers, buffers, arrays, etc. What is often the case when "unrelated change causes fix" is an memory bound overrun. Something is clobbering the stack, or an array, or something else, but because it's still in YOUR PROGRAM'S memory--it doesn't crash. So the "fix" is moving your code and data in memory slightly and merely pushing the error somewhere else that you don't see it.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Go to: