The Filter Manager
Class FilterManager is designed to make it easier to apply filters to your
scene. Of course, the easiest way to apply filters to your scene is to use
class CommonFilters. But if that utility does not
contain the filters you need, then the FilterManager is your next best
choice. The main function of the FilterManager is to help you set up the offscreen buffers, the quads, and the textures.
The Simplest Filter
The simplest possible code that uses the FilterManager looks like this:
manager = FilterManager(base.win, base.cam)
tex = Texture()
quad = manager.renderSceneInto(colortex=tex)
quad.setShader(Shader.load("myfilter.sha"))
quad.setShaderInput("tex", tex)
The first line creates an object of class FilterManager. We have told
it that we want to apply filtering to the contents of the main window.
We have also told it that we want to filter the stuff that's being
rendered by the main camera, and not, for instance, the 2D camera.
The second line creates a texture - this is the texture that we're going
to render the scene into.
The third line does most of the work. This removes the scene from the window,
and instead, directs the rendering of the scene into 'tex'.
It puts a quad into the window in place of the scene. The quad is
returned.
Finally, we apply a shader to the quad, and pass the scene texture
to the shader. Presumably, the shader is rendering the scene
onto the quad, which covers the window. Presto, filtered scene.
There's one tricky aspect of all this. Usually, the window is usually not a
power of two. The texture will end up being bigger than the window: for
instance, if the window is 800x600, then the texture will be 1024x1024.
The scene will be rendered into the lower-left 800x600 pixels of the
texture. The shader needs to compensate for this. If you forget this,
you will see an empty band above and to the right of the texture.
Here is a basic shader code example, it applies a simple black and white effect :
//Cg
void vshader(
float4 vtx_position : POSITION,
float2 vtx_texcoord0 : TEXCOORD0,
out float4 l_position : POSITION,
out float2 l_texcoord0 : TEXCOORD0,
uniform float4 texpad_tex,
uniform float4x4 mat_modelproj)
{
l_position=mul(mat_modelproj, vtx_position);
l_texcoord0 = vtx_position.xz * texpad_tex.xy + texpad_tex.xy;
}
void fshader(float2 l_texcoord0 : TEXCOORD0,
out float4 o_color : COLOR,
uniform sampler2D k_tex : TEXUNIT0)
{
float4 c = tex2D(k_tex, l_texcoord0);
// To have a useless filter that outputs the original view
// without changing anything, just use :
//o_color = c;
// basic black and white effet
float moyenne = (c.x + c.y + c.z)/3;
o_color = float4(moyenne, moyenne, moyenne, 1);
}
Extracting More Information from the Scene
In addition to fetching the color buffer of the scene, you can also fetch
the depth buffer:
manager = FilterManager(base.win, base.cam)
tex = Texture()
dtex = Texture()
quad = manager.renderSceneInto(colortex=tex, depthtex=dtex)
The depth buffer is particularly useful for filters like depth-of-field. You can pass the depth-texture to the shader too.
Sometimes, scene rendering may generate not just a color buffer and a depth
buffer, but also an auxiliary buffer. If so, you can fetch that too:
manager = FilterManager(base.win, base.cam)
tex = Texture()
atex = Texture()
quad = manager.renderSceneInto(colortex=tex, auxtex=atex)
Doing this would really only make sense if you've asked the renderer
to put something of interest into the auxiliary buffer. To do this,
see AuxBitplaneAttrib.
Using Intermediate Stages
The setup shown above works for any filter that can be computed in one stage.
However, for certain filters, you want to perform intermediate computations
before putting the output into the window.
The method renderQuadInto
creates a quad, and then causes that
quad to be rendered into a texture. This is the classic intermediate
processing step for image postprocessing. Using renderQuadInto
,
we can create a simple two-stage filter:
manager = FilterManager(base.win, base.cam)
tex1 = Texture()
tex2 = Texture()
finalquad = manager.renderSceneInto(colortex=tex1)
interquad = manager.renderQuadInto(colortex=tex2)
interquad.setShader(Shader.load("stage1.sha"))
interquad.setShaderInput("tex1", tex1)
finalquad.setShader(Shader.load("stage2.sha"))
finalquad.setShaderInput("tex2", tex2)
So tex1 will contain the raw, unfitered scene. Tex2 will contain a scene
that has been filtered through stage1.sha. The window will contain a
scene that has been filtered through both stage1.sha and stage2.sha.
The function 'renderQuadInto' accepts the keywords 'colortex', 'auxtex0', and 'auxtex1'. It does not accept 'depthtex,' since no depth buffer is used
when rendering a quad.
Resolution Management
Unless you specify otherwise, all textures will be the same resolution
as the window. The FilterManager will preserve this condition - it will
automatically resize the offscreen textures if the window gets resized.
The intermediate stages created by renderQuadInto
can be
the same size as the window, but they can also be larger or smaller by
a constant factor. The function takes the following keyword arguments:
- mul - The 'mul' option multiplies the size by an integer constant.
- div - The 'div' option divides the size by an integer constant.
- align - Relevant only when using the 'div' option - the window size is aligned to a specified alignment before dividing. This is useful to minimize resampling artifacts.
Cleaning Up
This function will cause the FilterManager to put everything back the way
it started:
manager.cleanup()