Simple FBO example

BlitzMax Forums/OpenGL Module/Simple FBO example

plash(Posted 2009) [#1]
The code was converted from this article using tidbits from this thread.


I'm planning on implementing GLSL in my modified version of GLMax2D and I decided to use an FBO as the main rendering target (so that shaders can change the whole screen if they want to).


ImaginaryHuman(Posted 2009) [#2]
Cool. Haven't coded this yet myself but was planning to very soon so I'll use this as reference :D


ImaginaryHuman(Posted 2009) [#3]
Played with this just now, it's pretty cool. Thanks for sharing the code.

I get a little puzzled with the differences between render buffer and framebuffer, etc... I'll have to read more in my OpenGL books.

I presume your color buffer attachment (texture) can't be any bigger than the maximum supported texture size? like 4096x4096 for example?

I know you can attach multiple buffers at once, tho and use then in shaders.

Also, I presume you cannot Flip 1 to cause the framebuffer object to be shown at all? You have to at some point copy from the framebuffer texture to the regular backbuffer before you can see anything? ie there's no way to use the fbo to flip without copying it/drawing with it?


plash(Posted 2009) [#4]
I presume your color buffer attachment (texture) can't be any bigger than the maximum supported texture size? like 4096x4096 for example?
Yup, same with any other texture.

ie there's no way to use the fbo to flip without copying it/drawing with it?
As far as I know, that is correct. You have to render it at some point, which is rather disappointing (simply rendering to the backbuffer is much faster than drawing a texture that is screen_width*screen_height).

I'm going to be using glDrawBuffer[s] to tell the shaders which FBO(s) are active/which to use (glDrawBuffer[s] sets the indexes for gl_FragColor in a fragment shader).
I hardly understand how shaders work, so the implementation might be changed when I get more into it.


Arowx(Posted 2009) [#5]
Hi Plash got an error on GL_VERSION_2_1 not found with BlitzMax version 1.33?


plash(Posted 2009) [#6]
Hi Plash got an error on GL_VERSION_2_1 not found with BlitzMax version 1.33?
Err, forgot to mention, you need this module modification:
http://www.blitzbasic.com/Community/posts.php?topic=85518

(Don't forget to add 'Const GL_TRUE:Int = True' to 'glew.bmx' if it isn't already there - I don't think Nilium updated the package yet.)


Arowx(Posted 2009) [#7]
Hmm What 2000+ fps is that good, but it's only a spinnging cube?

Cheers Plash!


plash(Posted 2009) [#8]
it's only a spinnging cube?
Does the spinning cube also have a texture of another spinning cube? If so then it is working properly.

This example renders a spinning cube to the FBO and then uses the texture for the FBO and slaps it on another spinning cube (and then finally rendering to the backbuffer - my current code uses a fullscreen FBO, essentially acts as a 'backbuffer', but stores the drawn stuff in a texture which can be used in shaders).


ImaginaryHuman(Posted 2009) [#9]
You can use textures in shaders without fbo's, right?


plash(Posted 2009) [#10]
You can use textures in shaders without fbo's, right?
Yes, you can. If you mean through shader uniforms, you have to do something like this (taken from here -- scroll to the bottom):
glUseProgramObjectARB(my_program)
Local my_sampler_uniform_location:Int = glGetUniformLocationARB(my_program, "my_texture")

glActiveTexture(GL_TEXTURE0 + i)
glBindTexture(GL_TEXTURE_2D, my_texture_object)

glUniform1iARB(my_sampler_uniform_location, i)


Fragment shader:
uniform sampler2D my_texture;

void main(){
.....
}


Also, this is the glDrawBuffer[s] thing I was talking about.
I think you need to use FBOs/multiple textures to use more than one post-process shader at a time (but I'm still not sure on that).


Jim Teeuwen(Posted 2009) [#11]
if you want your FBO to render to multiple buffers simultaneously, you have to specify the attachment points and pass them to glDrawBuffers, This also goes for any tricksies you want to perform on the textures attached to all these points (shaders etc).

I wrapped your FBO code in a little TFbo helper class.



It's 'Bind(indices:int[] = null)' method automatically takes care of this.

Creating an FBo is done like this:

local myfbo:TFbo = new TFbo;
local texture:int = CreateSomeTexture();

'// This binds our texture to attachment point 0.
'// Use any other index to attach it elsewhere.
'// This value can range from 0 to maxBuffers.
'// maxBuffers can be polled using this:
'//   local maxBuffers:int = 0;
'//   glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, varptr(maxBuffers));
'//
myfbo.Bind();
myfbo.Attach(0, texture);
myfbo.unbind();


To render to our FBO, we do this:
...
myfbo.Bind();
... rendering stuff here
myfbo.unbind();
...


When calling Bind() without parameters, the code assumes we want to use the default single attachment point 0 (GL_COLOR_ATTACHMENT0_EXT).

if you want to render to a different buffer, or multiple buffers, simply pass an array with integer indices. of course these indices should have valid textures/etc attached to them.

...
myfbo.Bind( [0, 2, 3, 6] );
... rendering stuff here
myfbo.unbind();
...



Armitage 1982(Posted 2009) [#12]
Hi Plash

I hope you don't mind if I ask you a tips on FBO since you know much more than me about openGL and Glew implementation.

I was wondering if FBO is the way to go in my problem exposed in this thread : http://blitzmax.com/Community/posts.php?topic=85762

I don't know much how to perform bliting operations in texture buffer but I suppose this would increase performance and allow many more graphic effect ?

If you could confirm this then I would dig the subject.
Thanks


Jim Teeuwen(Posted 2009) [#13]
I'm not Plash, but i'll throw in my 2 cents anyways after some playing with FBo's in the past few days :)

Doing complex drawing operations will slow down your app regardless of what you render to, however I find I can greatly increase the overall performance of my application by having the actual redrawing of the textures done a fixed amount of times per second, and just draw a quad with the generated texture as fast as the computer can.

In my own situation, this involves drawing UI controls. The actual controls are only drawn when they change and a maximum of 60 times per second. The part that actually draws the textures to the screen is executed as fast as possible though. This loop only has to draw textures on fixed-size quads though, so it is pretty darn fast. On my hardware, it allows me to have real framerates of 700+ while keeping the actual complex drawing to 60 times per second yet still have smooth graphics.

The general idea is this:

MyUpdateFps = 60;
Interval = 1000 / MyUpdateFps; '// maximum number updates per second.

while(MainLoopRunning)
  if(Time_since_last_update >= Interval) then
    check_for_input_etc();

    redraw_textures_to_fbos_if_required();
  end if

  draw_simple_quads_with_dynamic_textures_from_fbos();
wend



This pretty much doubled the performance of my code as opposed to recreating the entire textures every frame, or even if only when it had to be updated. Limiting it by framerate works really well, and 60 times per second is more than enough to give the appearance of smooth, continuous updates. You can, of course, change the update framerate as you see fit.

Next to that, it helps to cache as many complex calculations as possible. If it is even remotely possible to pre-calculate specific bits at program start, or even just supplying a look-up-table with pre-calculated results, it is definitely worth using it.


beanage(Posted 2009) [#14]
Thanks for sharing. Right the ref i need atm.


ImaginaryHuman(Posted 2009) [#15]
Yah that's a nice idea about having a different `draw to texture` framerate separate from the `copy to backbuffer` rate. It's a pity you can't just sort of bind the texture to the backbuffer so that it doesn't need to be copied, and so that when you Flip() it will just use that fbo/texture as the source of the flip.

Note that in GL it is possible to set the front buffer to be the one drawn to, and perhaps you could wait for a vertical blank somehow and then simply copy directly from the fbo-bound texture to the front buffer - a `manual flip`, to skip the copy to the backbuffer?


beanage(Posted 2009) [#16]
I did some reformatting of Jim T's Code (replacement of space indents with tabs, removed [Then], aligning var declarations, relaced "' //" with "'", some more white lines, removed unnecessary semicolons etc).. I seem to be a little bit strict about this^^: