This addons begins while I was working on Efecto Mariposa and I need some extra GPU processing power in order to make fast simulation of complex ecosystems. On does days I found GLSL Shaders a hard and cryptic topic. That´s why the main goal of this addon it´s to make super easy to use and edit shaders on your projects. That´s why it becomes a sort of common used shader library. Recently I start playing with them new GLSL editors like Ricardo Caballero´s WebGL SandBox, Inigo Quilez´s ShaderToy or Kode80´s GLSL Studio that let you edit the shader on-the-fly. So right now I´m working on making a standard ofxFXObject that could receive fragment shader and know how to deal with them while the program it´s running.
It´s the parent class of all the other effect. If you want to make a new filter you may want to start looking at the ofxFXObject.h .
The structure it´s easy.
-
Constructor: here it´s necessary to set three vital variables:
- passes: the number of passes or itineration of the main ping pong betweens FBO´s
- internalFormat: if it use GL_RGB, GL_RGBA, GL_RGB16f, GL_RGBA16f, GL_RGB32f, GL_RGBA32f, etc...
- fragShader: it´s the code of the shader. Note that some changes have to be made in order to fill everything on a string
-
allocate(width,height,GL_RGBA)
: This usually it´s no need to bee re-define. It´s basically allocate the FBO´s and loads the shader by using injectShader(); -
injectShader(string fragContent)
: here is where the shaders are loaded. See the example bellow. -
begin(int texN)
andend(int texN)
: remember nTextures variable? you can passthrough information to it by using this end() and begin() and giving the argument the number of texture you want to open. This texture can be access from the shader by theuniform sample2DRect tex0
ortex1
ortex2
and so on. Also you can access to the previous FBO of the ping pong by callinguniform sample2DRect backbuffer
. -
update()
: this is the core of the class, where the magic happens. If you check for theofxFXObject::update()
you will see how the tex´s, the backbuffer and other default uniforms variables (time, mouse, resolution) are loaded. -
draw(x,y,w,h)
: after all you definitely want to look at it.
An example of an implementation of an ofxFXObject could be:
On setup:
ofxFXObject fxObject = ofxFXObject();
fxObject.allocate(800,600); // At this point it will load a default timer shader
//Let´s play a little injecting a new one
fxObject.injectShader("#version 120\n\
\
uniform sampler2DRect backbuffer;\
uniform sampler2DRect tex0;\
uniform sampler2DRect tex1;\
uniform sampler2DRect tex2;\
\
uniform float time; \
uniform vec2 mouse;\
uniform vec2 resolution;\
\
void main( void ) {\
\
... \
\
gl_FragColor = ... ;\
}");
On update:
fxObject.begin(); // for tex0 you don´t need to pass the 0 as an argument
//What ever you want to render to tex0
fxObject.end();
fxObject.begin(1);
//What ever you want to render to tex1
fxObject.end(1);
fxObject.setTexture(image.getTextureReference(),2); // For object that have ofTexture you could just pass the ofTexture reference
fxObject.update();
On draw:
fxObject.draw();
You may notice that I´m not dealing with .frag of .vert files. This have to main reasons. First, make your binnary excecutables more compact. And Secondly, to work in the on-the-fly edition mode.
Thanks for Zach Lieberman tip you have to options one it´s to use normal " " with \ on the end of each line. Witch it´s very hard to read and your IDE couldn´t help you. Or by adding #define STRINGIFY(A) #A
at the .h and using it like this:
#define STRINGIFY(A) #A
const char shader[] =
STRINGIFY(
//multiline code goes here. no newline markers needed
);
If you are curious and want to learn the best way it´s to see and edit code. You could go to Ricardo Caballero´s webGL Sandbox or Inigo Quilez´s ShaderToy to found some inspiration. Explore. Make changes. Inject. Compile. May be it´s the case things not compile as you spect. Some times (most of them when you are dealing with other textures) you will need to make some changes. As far as I know openGL and GPU hardware it´s makes lot´s of changes and improves year after year. So lot´s of problems related to compatibility will happened. Also openFrameworks works fine with openGL 1.2 and it use by default the ARB Rect Textures.
Having that in mind the process of adapting code it´s a matter of searching on google. Fortunately you can see the ones I already implement and make them work.
General tips:
- Casting: openGL it will not cast for you. So if you assign an int to a float it will not compile properly.
float f = 1; // FAIL
float f = 1.0; // GOOD
- Norm coordinates: as far as I know there are two types of textures sample2D and sample2DRect. The first ones have the same length in both sides while the second one not. When you are using texture2D( tex, pos) the position have to be normalized while on texture2DRect(tex, pos) it´s not normalized.
ofxFXObject have some handy operators that let you combine them really easily. At the Mix´s Example you will find how to do it. Basicaly it´s somethin like this
ofxFXObject objA;
ofxGrayScott objB;
ofxBlur blur;
objA.allocate(width, height);
objB.allocate(width, height);
blur.allocate(width, height);
...
objA >> objB >> blur;
objA.update();
objB.update();
blur.update();
...
blur.draw();
If you are thinking on making your own filter or generative shader. You may want to look at the ofxFXObject parent class. There you will find some handy function that could help you.
-
renderFrame(width,height)
makes a frame where the textures could be draw; -
initFbo(ofxFbo, width, height, internalFormat )
: it takes the work of allocating and cleaning an FBO. -
ofxSwapBuffer.h
: this is actually a class for making easy dealing with ping-pongs.
In the src/ directory of the addon you will find lot´s of subClasses that inherit from ofxFXObject. Most of them are there for two reasons.
First case, the ones that in some point breaks the structure of ofxFXObject with some extra tweaks. Like the way the pingPong works. The number of shaders need and how they pass the data to each other. Or if the implement vertex or Geometry shader as well. That´s the case of:
- ofxFlocking: a GPU flocking system that implement two different types of fragment shaders, plus one vertex and geometry shader VIDEO
- ofxFluid: fluid simulation based on this article of Mark Harris. Look at the video example that use a lot of shader on a very complex and crazy way
- ofxGrayScott: based on ones Cinder´s Reaction Diffusion example that it´s based on Gray-Scott model. VIDEO
- ofxWater: a regular water waves effect based on Hugo Elias´s Tutorial. VIDEO
The second case, are the ones could be use as filters. That means, they could be use for many things. Giving lot of flexibility and freedom when you use it on your project. Like using the blur and the glow combined with mask and things like that.
-
Filters: ofxBloom, ofxBlur, ofxBokeh ( by Tim Scaffidi, ofxGlow, ofxUnsharp and ofxOldTv ( from ShaderToy postprocessing. VIDEO)
-
Composers: ofxClone: maden by Arturo Castro and Kyle McDonald for their brillant project call FaceSubstitution. And ofxMask: based on ofxAlphaMaskShader made by James George in collaboration with FlightPhase
On this addon you will find examples of the classes I just describe. Some of them are combined together in order to show clearly how to use them in the following examples:
- sandbox: diferent eyecandy shaders from Ricardo Caballero´s webGL Sandbox and Inigo Quilez´s ShaderToy together and then passed to post-processing GLSL shaders that acts as filters ( blur, glow, bloom, etc )
- conway: life game made by Kalwalt