Multiple fragment shader-pass on texture example


#1

Hey embers,

I’ve been trying to find some example among the many awesome samples in Cinder that gives me a bit of a direction for what I’m trying to do, but I’ve not been able to track one down so far.

Essentially I’m interested in having a series of fragment shaders perform operations on a textured quad. My limited understanding of the OpenGL and GLSL pipeline leads me to believe that in order for this to happen before a single frame is rendered, I’ll have to get each of the fragment shaders to write the result of their work into a new texture which the next fragment shader can then read from… Does that sound correct, or am I way off?

Furthermore, does any of the existing samples do anything like this?

Cheers,
Gazoo


#2

The technique is call “fbo ping pong”. If you search the old forum you’ll find a metric crap-ton of posts about it. The deferred shading sample and the VoronoiGpu sample have implemenations of the technique too.


#3

Hi,

@Gazoo : you’re correct. You can use a series of fragment shaders to create a single, complicated effect. For instance, a glow effect like in the image below is created by first rendering the scene as-is to an Fbo, then blurring the scene horizontally by reading the scene from the Fbo and applying the blur using a shader, the result of which is then blurred vertically. Finally, you’d combine the original scene with the fully blurred scene to create the glow effect.

Top: the bloom effect applied to the scene. Bottom: original scene.

A sample that uses this technique can be found here.

The ping-pong technique mentioned by @lithium.snepo takes this approach a bit further: you’d use the result of a previous step or frame as the starting point for the next frame. One example of this technique is this 2D water effect.

-Paul


#4

Hey @lithium.snepo and @paul.houx - thank you both for taking the time to respond to me.

I’ve learned a whole lot from looking at various samples, and websites. The one thing that I’ve yet to figure out, is how to do the ping pong technique using only a single FBO. The various threads I’ve read lead me to believe that this is supposedly the ideal approach.

Thanks to the FboMultipleRenderTargets sample, I’ve seen how to attach more than one texture to an FBO as follows:

auto fboFormat = ci::gl::Fbo::Format()
		.attachment( GL_COLOR_ATTACHMENT0, gl::Texture2d::create( renderWidth, renderHeight ) )
		.attachment( GL_COLOR_ATTACHMENT1, gl::Texture2d::create( renderWidth, renderHeight ) );
	
mFboRef = ci::gl::Fbo::create( renderWidth, renderHeight, fboFormat );

Using the code below, I can attach either of the two and render into either texture succesfully:

{
		ci::gl::ScopedFramebuffer fbScp( mFboRef );
		ci::gl::ScopedGlslProg glslScp( mGlslPixelate );
		mGlslPixelate->uniform( "uPixelSize", 50.0f );

		ci::gl::ScopedTextureBind texScp( mTexRef );

		gl::drawBuffer( GL_COLOR_ATTACHMENT0 );
		gl::clear( Color( 0, 0, 1 ) );

		ci::gl::drawSolidRect( ci::Rectf( 0, 0, 300, 300 ) );
	}

ci::gl::draw( mFboRef->getTexture2d( GL_COLOR_ATTACHMENT0 ), ci::Rectf( 0, 0, 640, 480 ) );

Exchanging GL_COLOR_ATTACHMENT0 for GL_COLOR_ATTACHMENT1 works fine, which I take it means rendering into either attachment is fine. However, taking things one step further fails :frowning: Here’s my attempt at rendering 2 different passed with a different fragment shader:

{
	ci::gl::ScopedFramebuffer fbScp( mFboRef );
	ci::gl::ScopedGlslProg glslScp( mGlslPixelate );
	mGlslPixelate->uniform( "uPixelSize", 50.0f );

	//ci::gl::ScopedGlslProg glslScp( mGlslDither );

	//ci::gl::ScopedGlslProg glslScp( mGlslC64 );
	ci::gl::ScopedTextureBind texScp( mTexRef );

	gl::drawBuffer( GL_COLOR_ATTACHMENT0 );
	gl::clear( Color( 0, 0, 1 ) );

	ci::gl::drawSolidRect( ci::Rectf( 0, 0, 300, 300 ) );
}

// Switch attachments, hook up next new shader
{
	ci::gl::ScopedFramebuffer fbScp( mFboRef );
	ci::gl::ScopedGlslProg glslScp( mGlslC64 );
	ci::gl::ScopedTextureBind( mFboRef->getTexture2d( GL_COLOR_ATTACHMENT0 ) );

	gl::drawBuffer( GL_COLOR_ATTACHMENT1 );
	gl::clear( Color( 1, 0, 0 ) );
	// No clear this time

	ci::gl::drawSolidRect( ci::Rectf( 0, 0, 640, 480 ) );
}

ci::gl::draw( mFboRef->getTexture2d( GL_COLOR_ATTACHMENT1 ), ci::Rectf( 0, 0, 640, 480 ) );

As you can see, I attach the FBO’s previous output texture (GL_COLOR_ATTACHMENT0), and try to draw in the GL_COLOR_ATTACHMENT1. This unfortunately just spits out black. Any ideas as to what might be going wrong would be much appreciated!

Cheers!


#5

this line should be

ci::gl::ScopedTextureBind txtScp( mFboRef->getTexture2d( GL_COLOR_ATTACHMENT0 ) );

If this does not solve the issue, and you want to make sure that the gl::clear does not work, leave the glsl shader and the rectangle draw. Those can also cause black output, but the gl::clear should work.

-Gabor


#6

@gabor_papp - You are my hero. Another reason to not code at around 22-23 pm :E


#7

Probably you have found your way for implementing this. I did it too a while ago, maybe you want to check it:

L