Developer Documentation
|
ACG supports shader generation at runtime and offers a modular shader assembly method. The main class of interest is ShaderProgGenerator, which generates a complete GLSL program composed of vertex, fragment and optionally geometry and tessellation shaders. This page documents the assembly process of generated shaders and how ShaderModifers and templates will affect the process. GLSL versions 1.3 and higher are supported.
Some parts of shader code are fixed and will be available in any generated shader. These include, but are not limited to:
The following naming conventions for uniforms, stage input/output and local variables are used:
fragment shader output: prefix "outFragment"
Modifiers and templates can not actually control any shader code provided by the ShaderGenerator, but they can add additional code lines that may overwrite or extend the results of the generated code.
The ShaderGenerator writes the current configuration given in ShaderGenDesc to a shader with the help of defines. These defines in the generated shader reflect the state of the active ShaderGenDesc.
Shading mode: defines in which stage lighting should be performed. One of the following defines is generated based on the state of ShaderGenDesc::shadeMode.
If textures, vertex colors and or lighting is enabled in the descriptor, these defines are specified in the shader.
Additionally the light setup is passed to the shader with the following defines.
Furthermore, there are stage-independent IO defines for default vertex attribute IO. It is recommended to make use of these, instead of using the direct names.
Request defines provided by shader modifiers or templates. Add these somewhere at the beginning of the shader.
The following default uniforms are guaranteed to be added to any generated shader:
The light configuration is added to a shader if it is supposed to do lighting. If the shader descriptor defines that lighting has to be done via gouraud or flat shading, these are added to the vertex shader. Othwerise, in phong shading these are included in the fragment shader.
additionally for directional lights:
additionally for point lights:
additionally for spot lights:
where X is replaced by the light index in range[0,.., numLight-1].
TextureSampler uniforms for a fragment shader are generated based on the number of specified textures in ShaderGenDesc:
ShaderModifiers can add custom uniforms to a generated shader.
Custom attributes have to be handled manually and are ignored by the generator. In contrast, default attributes consisting of position, normal, texcoord and vertexcolor can be conveniently accessed via the SG_INPUT_X, SG_OUTPUT_X defines. Default attributes can be requested in a template or modifier via the SG_REQUEST_X defines. It does not matter in what stage the attribute request is defined; the attribute will be available in all stages. In general, all used default attributes in a program are passed down from the vertex shader through all following stages. The fragment shader has only one default output though: vec4 outFragment! Also, they are passed down without modifications from the vertex-shader; that is, if not done explicitly by a modifier or template afterwards.
However, this is difficult for geometry and tessellation shaders due to their freedom of having multiple inputs or outputs. There is a convenience attribute mapping function sg_MapIO() to help with this problem for each of these stages, which maps all default inputs to their corresponding outputs without any modifications.
The implementation of this function looks as follows for each stage:
Geometry shader:
Tess-control shader:
Tess-evaluation shader: This is somewhat tricky, since there is no "default" way to interpolate. Still, there are some helpers for barycentric interpolation for triangle patches and bilinear interpolation for quad patches:
Full flexibility for the interpolation stage is possible with special keywords ("SG_INPUT", "SG_OUTPUT") recognized by the generator exclusively for the tess-eval stage: However, this only works if the whole instruction does not exceed more than a single line of code.
For instance, a barycentric interpolation of default attributes in a triangle can also be expressed as:
The generator then replaces the SG_OUTPUT, SG_INPUT keywords with each available default attribute.
Example 1: Displace vertices along normals in a vertex shader
Example 2: Write color-encoded normals in a fragment shader
Object-space normals have to be requested here! This example is implemented as a modifier for a change:
A shader template is glsl code with additional keywords that are interpreted by the shader generator.
Apart from the previously mentioned default defines, additional keywords are:
These keywords are location markers, where generated begin- or endcode of the vertex/fragment shader should be generated. However, it is not necessary to use them if the user wants to perform manual lighting and texturing etc. Templates for the tessellation and geometry stage do not have any exclusive keywords, but they can make use of defines, IO keywords and the mapping functions as described in the "Shader Attribute IO" section.
The vertex shader template without any user defined behaviour is:
Likewise, the fragment shader without special behaviour is:
Some parts of a generated shader contain fixed code snippets, which are assembled according to specification in ShaderGenDesc.
A vertex shader has the following code layout.
Tessellation and geometry shaders have a different layout and due to their complexity, there is no clear begin or end code marker for these shaders. The ShaderProgGenerator does not generate a main() function in these cases. However, it provides a convenient passthrough mapping function sg_MapIO() for default attribute IO. The sg_MapIO function can be called in a geometry shader template. The rest of the shader including the main() body has to be provided in form of a template file.
Fragment shaders are assembled similar to vertex shaders:
The ShaderGenerator implements a forward lighting routine that supports any number of lights (within hardcoded SG_MAX_SHADER_LIGHTS limit) and any combination of light types. However, it is possible to extend or even completely replace the default lighting routine with ShaderModifiers.
The default lighting routine performs the following steps.
A ShaderModifier can now choose to keep, extend or completely replace the lighting routine provided by the generator. Keep in mind that since modifiers simply add more shader code to the existing routine, only commutative operations are supported correctly.
The modifyLightingCode function of a modifier receives a light index [0,...,numLights-1] and a light type [directional,point,spot] as arguments and is supposed to compute a lighting color based on the uniforms provided by the generator.
Refer to the toon renderer plugin for an example, that implements custom cel lighting functions with a modifier.
Finally, it is possible to perform default lighting in the fragment shader template via the SG_FRAGMENT_LIGHTING keyword.