Building custom effects

Panda3D comes with a handy feature that enables you to conveniently define off-screen render buffers that can be used to render scene information into one or more textures. This allows you to redirect rendering output to intermediate textures that can be used as a base for exciting visuals. Filtering and recombining the previously generated texture data then create the end results of these image-based special effects. This recipe will show you how to use this feature, as it is the basis for any image based rendering effect you are going to build using this engine.

Getting ready

Set up your project folder as in Setting up the game structure found in Chapter 1. Add a directory called shaders at the same level as the src and models directories. Make sure these directories are in Panda3D's asset search path.

How to do it...

Follow these steps to implement a custom post-processing effect:

  1. Open Application.py and add the following listed code:
    from direct.showbase.ShowBase import ShowBase
    from direct.actor.Actor import Actor
    from panda3d.core import *
    from direct.filter.FilterManager import *
    class Application(ShowBase):
    def __init__(self):
    ShowBase.__init__(self)
    self.setupScene()
    self.setupPostFx()
    def setupScene(self):
    self.panda = Actor("panda", {"walk": "panda-walk"})
    self.panda.reparentTo(render)
    self.panda.loop("walk")
    cm = CardMaker("plane")
    cm.setFrame(-10, 10, -10, 10)
    plane = render.attachNewNode(cm.generate())
    plane.setP(270)
    self.cam.setPos(0, -40, 6)
    ambLight = AmbientLight("ambient")
    ambLight.setColor(Vec4(0.2, 0.1, 0.1, 1.0))
    ambNode = render.attachNewNode(ambLight)
    render.setLight(ambNode)
    dirLight = DirectionalLight("directional")
    dirLight.setColor(Vec4(0.1, 0.4, 0.1, 1.0))
    dirNode = render.attachNewNode(dirLight)
    dirNode.setHpr(60, 0, 90)
    render.setLight(dirNode)
    pntLight = PointLight("point")
    custom effectsbuildingpntLight.setColor(Vec4(0.8, 0.8, 0.8, 1.0))
    pntNode = render.attachNewNode(pntLight)
    pntNode.setPos(0, 0, 15)
    self.panda.setLight(pntNode)
    sptLight = Spotlight("spot")
    sptLens = PerspectiveLens()
    sptLight.setLens(sptLens)
    sptLight.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
    sptLight.setShadowCaster(True)
    sptNode = render.attachNewNode(sptLight)
    sptNode.setPos(-10, -10, 20)
    sptNode.lookAt(self.panda)
    render.setLight(sptNode)
    render.setShaderAuto()
    
  2. After you are done with that big pile of code, add another method to your Application class:
    def setupPostFx(self):
    self.filterMan = FilterManager(self.win, self.cam)
    colorTex = Texture()
    finalQuad = self.filterMan.renderSceneInto(colortex = colorTex)
    finalTex = Texture()
    interQuad = self.filterMan.renderQuadInto(colortex = finalTex, div = 8)
    interQuad.setShader(loader.loadShader("filter.cg"))
    interQuad.setShaderInput("color", colorTex)
    finalQuad.setShader(loader.loadShader("pass.cg"))
    finalQuad.setShaderInput("color", finalTex)
    
  3. Add new files called filter.cg and pass.cg to the shaders directory.
  4. Open filter.cg in an editor and add the following shader code:
    //Cg
    void vshader(float4 vtx_position : POSITION,
    out float4 l_position : POSITION,
    out float2 l_texcoord : TEXCOORD0,
    uniform float4 texpad_color,
    uniform float4x4 mat_modelproj)
    {
    l_position = mul(mat_modelproj, vtx_position);
    l_texcoord = (vtx_position.xz * texpad_color.xy) + texpad_color.xy;
    }
    void fshader(float2 l_texcoord : TEXCOORD0,
    uniform sampler2D k_color : TEXUNIT0,
    out float4 o_color : COLOR)
    {
    float4 color = tex2D(k_color, l_texcoord);
    o_color = float4(color.r * 1.8, color.g, color.b * 0.2, color.a);
    }
    
  5. Next, paste the following to pass.cg:
    //Cg
    void vshader(float4 vtx_position : POSITION,
    out float4 l_position : POSITION,
    out float2 l_texcoord : TEXCOORD0,
    uniform float4 texpad_color,
    uniform float4x4 mat_modelproj)
    {
    l_position = mul(mat_modelproj, vtx_position);
    l_texcoord = (vtx_position.xz * texpad_color.xy) + texpad_color.xy;
    }
    void fshader(float2 l_texcoord : TEXCOORD0,
    uniform sampler2D k_color : TEXUNIT0,
    out float4 o_color : COLOR)
    {
    o_color = tex2D(k_color, l_texcoord);
    }
    
  6. Everything is set. Now you can press F6 to run the program:
How to do it...

How it works...

Our program implements a very basic effect, pixelizing, and tinting the final image. But in the case of this recipe, it is about the way and not the goal.

After putting together our obligatory panda scene in the setupScene() method, we add setupPostFx(), which sets up all the buffers and textures we use for creating our effect, but let's take a closer look. First a new FilterManager is created, which is the engine's interface for managing render targets. Then we tell the engine to render our scene into colorTex using the renderSceneInto() method. Besides the colortex parameter, renderSceneInto() also takes the parameters depthtex for storing scene depth and auxtex for rendering to an auxiliary color buffer, which can be used for storing scene normals for example. This also creates and returns a quad that fills the entire screen onto which we will render the final image.

We then use renderQuadInto() to add an intermediate processing step to our little pipeline. The result of this step will be stored in finalTex, sampled down to an eighth of its original edge lengths, thanks to the div parameter. We apply the filter.cg shader to this temporary quad, using colorTex as the input. The shader itself just samples from the data found in colorTex and gives the red color channel a boost, while the blue channel values are decreased, giving the scene a warm look.

In the last lines of setupPostFx(), we set finalQuad to use finalTex as the input for the pass.cg shader, which simply takes the color it finds in the texture and outputs it to the screen.

There's more...

To see the contents of your render buffers for debugging, just add the line show-buffers #t to your Config.prc file. You can also turn buffer visualization on by adding the line loadPrcFileData('', 'show-buffers 1') below the import section of Application.py.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset