Shader Usage in Candera
Description
This chapter demonstrates how customized shader programs can be used with Candera. Subject of this chapter depicts how to use the Candera classes to set custom shaders for each node, and how to apply shader parameters. Further, it shows how to realize different classes of effects, namely single pass-, local multi pass- and global multi pass effects.
Chapters
Shader Related Class Usage
This section is about on how the already introduced Candera::Appearance class and its members can be used to realize customized shader effects.
Loading shader programs
First step to realize shader programs is to actually write them. See the GLSL chapter for details on that. For a developer it is recommended to develop using SceneComposer, where the effects can be seen immediately in the Scene Editor window.
This tutorial further focuses on programming with Candera, but the concepts are supported as well in collaboration with SceneComposer. Beside of SceneComposer's shader editor, shader authoring tools like AMD's RenderMonkey can also be used. Benefits from using either SceneComposer or shader authoring tools are syntax highlighting, seeing compiler and linker errors and having a preview scene where the effect can be examined. One unique advantage of using SceneComposer is the support for device dependent shader compilers.
After having finished your shader program you need to load it in your application based on Candera. Use your favorite file I/O libraries among the available ones. It depends on the platform if the underlying OpenGL ES implementation needs the shader source or pre-compiled shader objects. On host you typically load the source of your shader program directly, it will be compiled and linked at runtime by the OpenGL driver invoked by Candera's Upload mechanisms. On targets you might need to provide pre-compiled shader objects, where pre-compiling is done using dedicated shader compile tools. Please refer to the device manufacturers manual.
Using Candera::Shader
Candera::Shader is the class that abstracts from OpenGL ES shader interfaces and manages the shaders handle after creation. For a small description see the tutorial on the Appearance class. See below how to use the class.
First you need to create an instance of the Candera::Shader class using its Create() method.
static MemoryManagement::SharedPointer<Shader> Create();
Next step is to provide the loaded shader sources or objects to the class using the according setters:
bool SetVertexShader(const void* vertexShader, DisposerFn disposerFn); bool SetFragmentShader(const void* fragmentShader, DisposerFn disposerFn);
The shaders are passed as void* to ensure that platform dependent representations of the shader program can be passed. The parameter DisposerFn hands over lifetime responsibility of shader passed: If it's set to 0 you'll have to take care of the provided void* by yourself.
Finally, the shader needs to be associated to a node's appearance in order to leave the remaining steps (like Uploading and further Activating) to Candera. Upload takes care of compiling, linking, and retrieving the shaders handle. Vertex attribute binding is done in the appearances activation method, automatically.
The following code snippet demonstrates how a Billboard node with a custom shader can be created.
void Initialize() {
Char* vertexShader = "";
Char* fragmentShader = "";
// Do File I/O here, filling vertexShader and fragmentShader with the read data.
Scene* scene = Scene::Create();
//Do camera setup here, add it to scene graph.
SharedPointer<Shader> shader = Shader::Create();
shader->SetVertexShader(vertexShader, 0); //vertexShader needs to be disposed manually.
shader->SetFragmentShader(fragmentShader, 0); //fragmentShader needs to be disposed manually.
Billboard* billboard = Billboard::Create(2.0f, 2.0f);
billboard->SetAppearance(Appearance::Create());
billboard->GetAppearance()->SetShader(shader); //Here the previously instantiated and configured shader is added.
//Set and configure remaining appearance members.
scene->AddChild(billboard);
scene->UploadAll(); //All nodes, all their Appearance objects and therefore all set shader objects get uploaded.
}
void Render() {
Renderer::RenderAllCameras(); //Here the scene graph gets traversed, for every node the shaders get activated, and the vertex buffers attribute get bound
//to the shaders attributes.
}
Auto Uniforms
Candera::ShaderParamSetter and derived classes are used to set uniform parameters of shaders, they abstract from OpenGL ES's glUniform_ interfaces. They are part of a node's appearance, how to use them is already described in the Appearance Tutorial.
As described the derived class Candera::GenericShaderParamSetter offers to automatically compute and set shader parameters from the configured scene graph. The following table shows which uniforms are reserved for this purpose, what Candera classes they affect and what their semantics are.
The table is structured as follows:
If no instance of the affected classes exist, the corresponding shader parameters will not be set, even if they are enabled in Candera::GenericShaderParamSetter.
Attribute Names
Candera uses pre-defined attribute names to bind a vertex buffers attributes. When writing shader programs the following attribute names shall be used:
Single Pass Effects
Single pass effects can be achieved in one camera pass only. They are simply realized with a single Candera::Appearance attached to a Candera::Node.
Examples for single pass effects can be found in the Special Render Techniques chapter.
Local Multi Pass Effects
Local multi pass effects describe effects with local impact only, thus, they belong to one certain node. They can be realized using Candera::MultiPassAppearance. These appearances are processed in a sequence; for each appearance the node is rendered once and the results are blended together using the different RenderMode settings.
Examples for local multi pass effects can be found in the Special Render Techniques chapter.
Global Multi Pass Effects
Global multi pass (also known as screen-based effects) typically operate on render targets, often they are also referred to as post-processing effects. To realize post-processing effects first images have to be rendered into an offscreen render target. How this is done is presented in Render Targets Tutorial.
The resulting image can further be processed in any desired way.
Examples for global multi pass effects can be found in chapter Special Render Techniques.
Creating Full Screen Effects
You can of course process the resulting texture on any mesh or object you like, but often post processing effects are applied to full screen images. For that purpose a dedicated scene with only a camera and a billboard can be used. The trick is to give the Billboard a size of 1.0 unit in both dimensions.
Billboard* fullScreenBB = Billboard::Create(1.0F, 1.0F);
This causes the billboard to have a vertex buffer with coordinates that match the corners of the window/screen in Canderas viewport space. To draw the billboard full screen you just need to use a vertex shader that doesn't transform the vertex coordinates, use ReferenceShaders\Core\RefViewportSpace.vertp for this purpose. The fragment shader then does the actual post processing. As texture for the billboard you need to use the image provided by the offscreen render target.
RefViewportSpace.vertp takes two float uniforms (u_offsetLeft, u_offsetTop) as inputs, which determine the top left corner of the billboard to render. These values are defined as being in normalized viewport space, where values range from 0.0 to 1.0, where 0.0 is interpreted as the left border respectively top border of the viewport, and 1.0 as the right, respectively bottom border.
The vertex position attribute of a vertex buffer used with this vertex shader is not transformed using a model-view-projection matrix, but simply translated into OpenGL's clip coordinate space with the specified offset applied.
The following examples demonstrate how to use the RefViewportSpace.vert shader in conjunction with the depth-of-field global multi pass effect.
Specify a full screen rectangle:
//Do Shader File I/O here and initialize m_dofCombineShader shader object. Float m_viewportLeft = 1.0F; Float m_vieportTop = 1.0F; m_dofCombineSps = GenericShaderParamSetter::Create(); m_dofCombineSps->SetModelViewProjectionMatrix4Enabled(false); m_dofCombineSps->SetUniform("u_offsetLeft", Shader::Float, &m_viewportLeft); m_dofCombineSps->SetUniform("u_offsetTop", Shader::Float, &m_viewportTop); m_fullScreenQuadDof = Billboard::Create(1.0f,1.0f); m_fullScreenQuadDof->GetAppearance()->SetShader(m_dofCombineShader); m_fullScreenQuadDof->GetAppearance()->SetShaderParamSetter(m_dofCombineSps);
This results in the following image:

Specify a centered rectangle with half viewport resolution:
//Do Shader File I/O here and initialize m_dofCombineShader shader object. Float m_viewportLeft = 0.25F; Float m_vieportTop = 0.25F; m_dofCombineSps = GenericShaderParamSetter::Create(); m_dofCombineSps->SetModelViewProjectionMatrix4Enabled(false); m_dofCombineSps->SetUniform("u_offsetLeft", Shader::Float, &m_viewportLeft); m_dofCombineSps->SetUniform("u_offsetTop", Shader::Float, &m_viewportTop); m_fullScreenQuadDof = Billboard::Create(0.5f,0.5f); m_fullScreenQuadDof->GetAppearance()->SetShader(m_dofCombineShader); m_fullScreenQuadDof->GetAppearance()->SetShaderParamSetter(m_dofCombineSps);
This results in the following image:

Specify a rectangle in bottom right corner with quarter viewport resolution:
//Do Shader File I/O here and initialize m_dofCombineShader shader object. Float m_viewportLeft = 0.75F; Float m_vieportTop = 0.75F; m_dofCombineSps = GenericShaderParamSetter::Create(); m_dofCombineSps->SetModelViewProjectionMatrix4Enabled(false); m_dofCombineSps->SetUniform("u_offsetLeft", Shader::Float, &m_viewportLeft); m_dofCombineSps->SetUniform("u_offsetTop", Shader::Float, &m_viewportTop); m_fullScreenQuadDof = Billboard::Create(0.25f,0.25f); m_fullScreenQuadDof->GetAppearance()->SetShader(m_dofCombineShader); m_fullScreenQuadDof->GetAppearance()->SetShaderParamSetter(m_dofCombineSps);
This results in the following image:

Overlapping Billboards (in screen space) would produce depth-fighting artifacts. Disabling depth test and using render order as well as using depth bias can be used to avoid them.
m_fullScreenQuadDof->GetAppearance()->SetRenderMode(RenderMode::Create()); m_fullScreenQuadDof->GetAppearance()->GetRenderMode()->SetDepthBias(0.1F);
For details on how to use render order please refer to Tutorial 3.
References
[1] AMD Render Monkey ToolSuite http://developer.amd.com/Resources/archive/ArchivedTools/gpu/rendermonkey/Pages/default.aspx (October, 4, 2012)