A simple, robust and extendible postprocessing class

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
Post Reply
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

A simple, robust and extendible postprocessing class

Post by DavidJE13 »

I've seen a couple of these around, but none of them were neat enough for my liking, so I made my own;

http://www.dje.me.uk/misc/PostProcessor.zip
(project is in XCode and might/might not work as-is. just stick all the files in one project, add Irrlicht and build)

It's a collection of post processing effects, which regresses on old hardware, works in an optimized way and is easy to setup and maintain in the main code. It has:

* Black (a test shader which shows only black)
* Direct (a way of getting anti-aliasing - combine these to get 2x, 4x, 8x, etc.)
* Darken (multiplies maintaining black=black)
* Lighten (multiplies maintaining white=white)
* Range (narrows contrast)
* Invert (reverses rgba channels)
* Tint (converts to monochrome and adds tint, preserves black & white)
* Greyscale (Tint with grey)
* Sepia (Tint with brown)
* Curves (raise/lower red, green, blue. Preserves black & white)
* Noise (Pseudo-random noise, changes each render. Random function is poor)
* Coloured noise (like Noise, but red, green, blue are separate)
* Horizontal blur (basic 5-sample blur)
* Vertical blur
* Bi-directional blur (combines above 2)
* Linear blur
* Radial blur (with movable centre and x/y blur level)
* Radial beam (kind'a like sun rays, but not high enough quality to pass for them. Inspired by the halo currently-online globe)
* Rotational blur
* Horizontal & Vertical blurring by depth (background blurred)
* Horizontal & Vertical blurring by depth (foreground blurred - some problems)
* Depth of field (background & foreground blurred)
* Depth (converts alpha channel to rgb)
* Heat Haze (Adds a haze in certain parts of the render, based on a second texture)
* Heat Haze with depth (Heat Haze, but with the size of the effect dependent on the distance from heat source to object)
* Overlay (adds one texture to another with a multiplier)
* Negative Overlay (subtracts one texture from another with a multiplier Warning: Not optimized. Will cause serious performance loss)
* Motion blur (by past frames, no interpolation)
* Ambient occlusion (based on some code on these forums, heavily mutilated)
* Bloom (a mix of Lighten, Blur and Overlay)
* Gloom (like bloom, but dark. a mix of Darken, Blur and Negative Overlay)
* Night Vision (makes a grainy, green tinted, brightened view. a mix of Range, Blur, Noise and Tint)
* Transitions (a simple blend-by-texture is included and others can easily be made by subclassing IPostProc)
* Lens flare (uses pixel testing, so can be slow)
* Custom - Supply a gl and directX pixel shader, from code or external file, and specify a shader version to add any effect, with up to 4 input textures and 8 float parameters

* Old Monitor (provided as a custom effect, not part of the main code. An old, pixelated, fuzzy, distorted, de-saturated monitor. Fairly intensive so not recommended for real use)

That's over 30 built-in effects!

See the main.cpp file for an example of how to use it. It's really easy to add, change and remove effects at runtime, and each effect can be independently disabled/enabled, and has a quality setting to control how much processing power it uses.

There are a couple of limitations;
* Shaders are OpenGL only. I'm on a mac so DirectX isn't a priority. (The code does support directX - all that's needed is the shader code)
* Alpha channel gets messed up by some of the shaders. Not a concern unless you're trying to transfer data with it.
* You can't add an effect between 2 others easily (though it is possible). I'm not sure how I should implement this.

Hope this helps someone :)

latest update added: too much to list again - see the thread. Page 5 has the latest update.
Last edited by DavidJE13 on Sun Sep 20, 2009 1:13 am, edited 8 times in total.
Valmond
Posts: 308
Joined: Thu Apr 12, 2007 3:26 pm

Post by Valmond »

This seems very cool, great job!
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Might have to give this a peek!
Image Image Image
link3rn3l
Posts: 81
Joined: Wed Nov 15, 2006 5:51 pm

Post by link3rn3l »

thanks
Bennu (Best 2d and 3D dev-tool)
http://bennupack.blogspot.com

Pixtudio (Best 2D development tool)
http://pixtudiopack.blogspot.com

Bennu3D(3D Libs for bennu)
http://3dm8ee.blogspot.com/

Colombian Developers - Blog:
http://coldev.blogspot.com/
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Thanks for posting this. :D This will help noobs like me start on with shaders and post-process effects.
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Post by Ovan »

thanks but have a crash in :
void IPostProc::removeOut( IPostProc* o, u8 n );
with irrlicht 1.6
Last edited by Ovan on Tue Feb 23, 2010 1:13 am, edited 1 time in total.
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

Ovan wrote:thanks have a crash in :
void IPostProc::removeOut( IPostProc* o, u8 n );
with irrlicht 1.6
yeah, I got that crash when I upgraded to OSX 10.6 today. I've fixed it now and I'll upload the fix soon (need to un-break something else first!)

Meantime - the fix is to find the IPostProc::init function in IPostProc.cpp and add these 2 lines:

for( u8 i = 0u; i != 4; ++ i )
follows[ i ] = NULL;

(they can go anywhere you like - I added them just above the outs.reallocate line)
Seems I was previously able to get away without initializing the class properly :oops:

Edit: download is updated now. Bug should be fixed :)
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

I'm updating this an awful lot it seems. Well it's now got every post-processing effect I can think of and seems to be completely stable (fingers crossed), so maybe I'll stop for a while :D

if anyone else has an effect they'd like adding please tell me and I'll have a go!
sp00n
Posts: 114
Joined: Wed Sep 13, 2006 9:39 am

Post by sp00n »

Great work, man!
Thanks for doing it for us.

Don't you have a 'hot air" ("haze") effect? :) I don't know exactly if it is doing as a postprocess effect, but think that yes :)
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Hey man, cool thing! :)

Just a simple question: How do I implement a single effect? I tried to remove the smgr2 and transition and monitor effect from the sample but that just resulted in no rendered image at all.

greetings
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

freetimecoder; I think the easiest way for me to answer that is by posting an example, so here's the bare minimum code to render a single effect (in this case bloom):

Code: Select all

#ifdef _IRR_WINDOWS_
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#pragma comment(lib, "Irrlicht.lib")
#include <windows.h>
#endif

#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// Include the headers for post processing
#include "CRendererPostProc.h"
#include "CEffectPostProc.h"
#include "CTransitionPostProc.h"
#include "CSplitPostProc.h"

int main( ) {
	// Boring generic stuff from the original example
	IrrlichtDevice *device = createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 16, false, false, false, 0);
	if (!device) return 1;
	device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();
	guienv->addStaticText(L"Hello World!", rect<s32>(10,10,260,22), true);
	IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
	if (!mesh)
		return 1;
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
	if (node)
	{
		node->setMaterialFlag(EMF_LIGHTING, false);
		node->setMD2Animation(scene::EMAT_STAND);
		// Using the lighter fireball bitmap so we can actually see something!
		node->setMaterialTexture( 0, driver->getTexture("../../media/fireball.bmp") );
	}
	smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
	
	// Now the interesting stuff:
	IPostProc* ppRenderer = new CRendererPostProc( smgr, dimension2di( 1024, 512 ), true, true, SColor(255,100,101,140) );
	CEffectPostProc* ppBloom = new CEffectPostProc( ppRenderer, dimension2di( 1024, 512 ), PP_BLOOM /* Parameters here if needed */ );
	
	while(device->run())
	{
		driver->beginScene( false, false );
		ppBloom->render( NULL );	// Tell the last effect to render
		guienv->drawAll( );
		driver->endScene( );
	}
	
	device->drop();
	
	return 0;
}
and sp00n; a haze.. interesting. It could be done, although I think it would need 2 inputs - the main scene, and a second scene telling it where to put the haze. I'll give it a go.
(also, since the last update I've added depth of field. it's still a little primative so I'll try to patch that up before the next release)
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Thanks, I guess i figured out how it works now :)

Another question, how do I tell the PP_OVERLAY shader what texture to use to overlay? When changing Bloom to overlay the overlay texture is the texture applied to sydney.

Edit:
I figured out that I can set effect inputs. So I tried to combine two effects (glow and direct) in an overlay effect:

Code: Select all

int main( ) {
   // Boring generic stuff from the original example
   IrrlichtDevice *device = createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 16, false, false, false, 0);
   if (!device) return 1;
   device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
   IVideoDriver* driver = device->getVideoDriver();
   ISceneManager* smgr = device->getSceneManager();
   IGUIEnvironment* guienv = device->getGUIEnvironment();
   guienv->addStaticText(L"Hello World!", rect<s32>(10,10,260,22), true);
   IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
   if (!mesh)
      return 1;
   IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
   if (node)
   {
      node->setMaterialFlag(EMF_LIGHTING, false);
      node->setMD2Animation(scene::EMAT_STAND);
      // Using the lighter fireball bitmap so we can actually see something!
}
   smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

   // Now the interesting stuff:
   IPostProc* ppRenderer = new CRendererPostProc( smgr, dimension2di( 1024, 512 ), true, true, SColor(255,0,0,0) );
   ITexture *glowTex = driver->addRenderTargetTexture(dimension2d<s32>(1024,512),"rendertex");
   CEffectPostProc* ppBloom = new CEffectPostProc( ppRenderer, dimension2di( 1024, 512 ),PP_BLOOM,1 /* Parameters here if needed */ );

   IPostProc* ppRenderer2 = new CRendererPostProc( smgr, dimension2di( 1024, 512 ), true, true, SColor(255,0,0,0) );
   CEffectPostProc* ppDirect = new CEffectPostProc( ppRenderer2, dimension2di( 1024, 512 ),PP_DIRECT,1 /* Parameters here if needed */ );
   CEffectPostProc* ppOverlay = new CEffectPostProc( ppDirect, dimension2di( 1024, 512 ),PP_OVERLAY,0.2 /* Parameters here if needed */ );

    ppOverlay->setInput(0,ppDirect);
    ppOverlay->setInput(1,ppBloom);


   while(device->run())
   {
      driver->beginScene(  false, false );

      node->setMaterialTexture( 0, driver->getTexture("../../media/fireball.bmp") );
      ppBloom->render(NULL);

      node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
      ppOverlay->render(NULL);


      guienv->drawAll( );
      driver->endScene( );
   }

   device->drop();

   return 0;
}
But it is all flickering like crazy :?

Any suggestion whats wrong?

greetings
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

ok, you're rendering 2 nodes to the screen each loop, hence the flicker. To render to the internal surfaces, just use Render( ) (no NULL)

You can fix your code by simply removing the NULL in the first Render call (but not the second)

This will work in this case, but is risky in general. I'll see if I can add built-in support for this type of rendering.

Edit:
Also, you don't need the setInput(0,ppDirect) line - input 0 is already set by the constructor. It's doing no harm though.
And the PP_DIRECT effect is doing absolutely nothing here - you can use ppRenderer2 in it's place and remove it (In the latest version - not uploaded yet - it will filter this out automatically but for now it's doing an extra render because of that)

Edit2:
I've now added a function specifically for this: preRender()
It does exactly the same as Render() but in a way which is safe for this type of use.
It'll be included when I next update, and I'd recommend changing the first render call in your code to it.
DavidJE13
Posts: 165
Joined: Tue Jan 09, 2007 7:17 pm

Post by DavidJE13 »

ok, enough's changed for a new version now, I think!

I've added depth of field blurring (near, far, and both), fixed a whole load of stuff, optimized a whole load of stuff, fixed (hopefully!) a cross-platform problem found by Seven, added preRender(), increased the constructor parameter limit to 8, and updated the example file to be split into several examples (each much simpler than the old all-in-one version)

Depth of field seems to work well, although foreground blurring is a bit poor around the edges and it's a little intensive compared to the other effects.

Still no haze, but I'm working on it!
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Hi, thanks for the help.

Here is what i accomplished: http://www.freetimestudio.de/glowshader.zip
Looks nice, but might be a little complicated for large scenes. I might come up with something easier (if I can figure out something :lol: ) Use as you please (Who am I to say that xD).

Depth Of Field sounds interesting, but how do I use it? I tried PP_DEPTH, but that just gives me a white screen.

greetings
Post Reply