Go to bottom

OpenGL: Stupid FBO flipping shit upside down

category: code [glöplog]

Normally I don't post on Pouët, but I'm sort of on a deadline here (sounds usual?). Anyway, as usual, I have problems with OpenGL.

I am working on an AI using Collaborative diffusion, and thought that it would be a good idea to speed it up on the GPU (since I am developing on an IBM t60 because I am studying abroad in Rio de Janeiro). I can do like 1500 iterations/sec on my crappy Mobility Radeon x1400, and 50-100 times less on my CPU.

The problem is as following: When I render something to the screen directly, it works fine. When I render it to an FBO, and then render the FBO to the screen, things get flipped horizontally. I don't know why, and it makes me feel angry and sad.

I try to lay out my coordinates so that (0, 0) corresponds to the top left corner. I am using glOrtho for this. It should just work.

Here is my code - I wrote the shortest possible program that has the problem. It is written in Python and pygame, because it's a much more awesome developing experience than C++, especially on this shitty hardware. Performance is actually OK now that I am offloading most of the work to the GPU anyway. OpenGL with exceptions is cool.

There are not an awful lot of comments, but here is a quick rundown:

  • In the main function run, I generate a set of random points called goals_obstacles. I give them an intensity corresponding to their x, y position. What I want is for the points with (x,y) ~= (1, 1) to be bright (and render to the lower right corner), and (x, y) ~= (0, 0) to be dark (and render to the upper left corner).
  • render_obstacles_directly renders the random points directly to the framebuffer. I set my projection matrix with glOrtho(0, 1, 1, 0, ...), which gives me what I want.
  • render_obstacles_directly renders the random points directly to a texture through a FBO. I set my projection matrix with glOrtho(0, 1, 1, 0, ...).
  • render_fbo_content renders the content of the FBO to the framebuffer. I just the same projection matrix, and I render everything on a quad. UV's and vertex coordinates are suposed to match, and I render them in the order (0, 0), (1, 0), (1, 1), (0, 1), which should be clockwise.

Maybe the problem is just that I don't understand how the UV coordinates of textures are laid out. (0, 0) is top left corner, yes?

I can easily fix the problem and move on, but I want to understand!

Code:import pygame import pygame.event import OpenGL from OpenGL.GL import * from framebufferobject import * from shader import Shader from heatmap import Heatmap from random import random, randrange screen_size = (256, 256) def render_obstacles_directly(goals_obstacles): glClearColor(0, 0, 0, 1) glClear(GL_COLOR_BUFFER_BIT) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, 1, 1, 0, -1, 1) glDisable(GL_TEXTURE_2D) glPointSize(5.0) glBegin(GL_POINTS) for (x, y), v in goals_obstacles.items(): glColor3f(v, v, v) glVertex2f(x, y) glEnd() def render_fbo_content(fbo): glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, 1, 1, 0, -1, 1) glColor3f(1, 1, 1) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, fbo.gl_tex_id) glBegin(GL_QUADS) glTexCoord2f(0, 0); glVertex2f(0, 0) glTexCoord2f(1, 0); glVertex2f(1, 0) glTexCoord2f(1, 1); glVertex2f(1, 1) glTexCoord2f(0, 1); glVertex2f(0, 1) glEnd() glDisable(GL_TEXTURE_2D) def render_to_fbo(fbo, goals_obstacles): fbo.bind() glClearColor(0, 0, 0, 1) glClear(GL_COLOR_BUFFER_BIT) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, 1, 1, 0, -1, 1) glDisable(GL_TEXTURE_2D) glPointSize(5.0) glBegin(GL_POINTS) for (x, y), v in goals_obstacles.items(): glColor3f(v, v, v) glVertex2f(x, y) glEnd() fbo.unbind() def run(): from pygame.constants import HWSURFACE, OPENGL, DOUBLEBUF pygame.init() pygame.display.set_mode(screen_size, HWSURFACE|OPENGL|DOUBLEBUF) goals_obstacles = {} for _ in range(100): x, y = (random(), random()) goals_obstacles[(x, y)] = x * y fbo = FrameBufferObject(256, 256) render_to_fbo(fbo, goals_obstacles) display_fbo_content = False done = False # Main loop! while not done: while pygame.event.peek(): event = pygame.event.poll() done |= event.type == pygame.QUIT done |= getattr(event, 'key', None) == pygame.K_ESCAPE if event.type == pygame.KEYUP: if event.key == pygame.K_SPACE: display_fbo_content = not display_fbo_content if display_fbo_content: print "Rendering content of FBO." else: print "Rendering points directly to screen." if display_fbo_content: render_fbo_content(fbo) else: render_obstacles_directly(goals_obstacles) pygame.display.flip() pygame.quit() if __name__ == '__main__': run()

framebufferobject.py :
Code:from OpenGL.GL import * from OpenGL.GL.framebufferobjects import * class FrameBufferObject: def __init__(self, width = 256, height = 256): # Save dimensions self.width, self.height = width, height # Generate a framebuffer ID self.id = glGenFramebuffers(1) # The texture we're going to render to self.gl_tex_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.gl_tex_id) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, None) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glBindTexture(GL_TEXTURE_2D, 0) glBindFramebuffer(GL_FRAMEBUFFER, self.id) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.gl_tex_id, 0) glDrawBuffers([GL_COLOR_ATTACHMENT0]) if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE: print "Framebuffer incomplete: %s" % glCheckFramebufferStatus(GL_FRAMEBUFFER) glBindFramebuffer(GL_FRAMEBUFFER, 0) self.clear() def bind(self): glBindFramebuffer(GL_FRAMEBUFFER, self.id) glPushAttrib(GL_VIEWPORT_BIT) # save viewport glViewport(0, 0, self.width, self.height) def unbind(self): glBindFramebuffer(GL_FRAMEBUFFER, 0) glPopAttrib() def clear(self): self.bind() glClearColor(0.5, 0.5, 0, 0) glClear(GL_COLOR_BUFFER_BIT) self.unbind()
Correction, the problem is that things get flipped vertically.

This is what the output should look like, and also the result I get when I use render_obstacles_directly. Pixels with x, y ~= 1 are in the lower right corner like they should be, and x, y ~= 0 in the upper left:
BB Image

This is what it looks like when rendered to the FBO first with render_to_fbo, and then rendered to the framebuffer with render_fbo_content:
BB Image
Your texture coordinates are flipped. OpenGL has its origin at bottom-left and not top-left; while you compensate for this by changing top and bottom in glOrtho, you do not do so for the texture coordinates.

Add a texture coordinate matrix, or stop trying to fight the API's conventions :-)
added on the 2012-08-20 00:44:19 by Sesse Sesse
sorry for offing but i first thought the thread title was some gangsta rap song.
added on the 2012-08-20 00:44:28 by Gargaj Gargaj
Thanks Sesse!

@gargaj: Not yet...
In opengl the image origin is in lower left so the fbo textures work correctly.
The issue is actually with the regular textures. In photoshop/whatever your origin is on top left.. So you have to flip your disc read texture's data before sending it into glteximage2d. After the flip your fbo- and glteximage textures will both be in same 'space'.
added on the 2012-08-20 00:49:48 by rale rale
I cannot believe that I have been "programming" OpenGL for so many years, and not ever figured it out. Then again, I never used that many textures, and just sort of changed stuff till it "worked" (which is terrible practice).
You know, I've been doing this shit for years now too and have been just flipping texture coords as a quick + easy fix. Now I'm going to consider flipping all the images before saving them as textures :) Although that won't be possible for some, maybe I should stick with flipping.
added on the 2012-08-20 10:44:47 by psonice psonice
Yah, I had the same issue when using FBOs. :P Constantly flipping texture coords to compensate when a FBO is in effect.
added on the 2012-08-20 11:01:57 by mudlord mudlord
Actually, the wealth of coordinate systems is just a general headfuck. You've got 0,0 at either the top left or bottom left, or maybe the middle. You've got to flip stuff, translate stuff, and a whole lot more just to get back to some kind of sanity.

Then you've got the fact that your screen is 4:3 or 16:9 or whatever, and your screen is 1:1 in openGL. I'm working on mobile too, where the screen rotates, so it's likely that the screen either rotates or changes aspect ratio during rendering, just for extra layers of confusion :( And then you've got the screen size - which is one size in points and another in pixels. OpenGL works in pixels, almost everything else is in points
added on the 2012-08-20 13:45:22 by psonice psonice
erm... go and make a demo 'bout it...
added on the 2012-08-20 14:17:02 by Danzig Danzig
Actually, in case of ordinary textures (non-FBO) it doesn't count at all where (0,0) is physically. (0,0) is the first byte that goes to TexImage2D and that's all. In the same way in which you can print OpenGL Spec page about textures, read it upside down and it's still correct.

When you have FBO, that's a whole different story, because unlike textures in memory, computer screens usually have clearly defined "up" and "down".
added on the 2012-08-22 13:41:25 by KK KK
KK: Haha that's awesome! You're right!
glScalef(1.0f, -1.0f, 1.0f);

added on the 2012-09-23 01:16:00 by mudlord mudlord
Not if you use core profile.
Didn't we have the exact same topic some month ago?! I have some kind of deja vu.
added on the 2012-09-23 01:23:30 by las las
OGL3 > is for posers :P

OGL 2.1 + extensions for lyfe :D
added on the 2012-09-23 01:33:31 by mudlord mudlord
Collaborative diffusion - cool!
added on the 2012-09-23 07:21:57 by TLM TLM
@TLM: yup!


Go to top