|
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: The code I used to render this scene is the following: 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: 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: 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: 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: 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... :/ |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Erin Maus
Member #7,537
July 2006
|
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"} Edgar Reynaldo said: 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 --- |
Hepolite
Member #13,716
November 2011
|
Edgar Reynaldo said:
What does this set of code do? 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. Aaron Bolyard said: 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: 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: 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
|
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: |
Hepolite
Member #13,716
November 2011
|
I was referring to these lines: 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. -- |
Erin Maus
Member #7,537
July 2006
|
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. --- |
Chris Katko
Member #1,881
January 2002
|
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: |
|