- Shader declaration
- Technique declaration
- An example of a complete technique file
- Things to do…
Pretty much since I started programming computer graphics I have spent time writing small rendering engines
for learning purposes. For the past few years I have been working on an engine dubbed “The BigGun Engine”, like the AC/DC song
which is a large scale rendering engine that I probably won’t ever complete, but will serve me well as a learning tool for most of
the modern rendering techniques that we see on Triple-A titles today.
One of the goals was to implement a portable shading technique or effect system, based on description files such as the ones used by frameworks like CgFx and HLSL. Since the idea was to have a hands-on approach, the tools used were C++ (which is my programming language of choice) and GLSL. In a more real-life scenario, using CgFx would be a better option, since you can achieve the same, or better results, with orders of magnitude less coding and debugging, so take this post as the description of a purely learning project where I will outline some details of implementing a shading effect system from scratch. I’m in no way saying my solution aims to be better than the existing ones.
I won’t go into the details of the parser here; just show the basic structure of the BON language. The BON language is built around two basic elements: An Object and a Property.
An object is declared as:
An object can contain properties and other child objects. The object is defined by a name followed by
A property, unlike an object must be followed by a value, which can be a number, a string, a vector (a 2D, 3D or 4D vector,
not to be confused with an array. There is no generic array support) and a matrix (again a matrix in the geometry sense,
not a multi-dimensional array).
As you can see, the structure of the BON is identical to the XML and JSOM structures: A name (Object) followed by set of values (Properties) that can turn out to also be child objects.
The goals of the shading technique system were the following:
Support several versions of the OpenGL Shading Language, easing the task of providing fall-back support for older hardware.
Have a way to mix several GLSL source files together, since GLSL didn’t have an
#includemechanism until very recently (see
Simplify the declaration of vertex formats, fragment output formats and uniform variables/buffers.
Provide support for multi-pass shading techniques, just like in CgFx and HLSL.
Other small things like reloading shaders on-the-fly and caching to avoid duplicates and unnecessary loading.
Adding support for dynamically building shaders during runtime from code fragments is definitely doable, but I didn’t get to this point, since I don’t really need such feature right now, but with the basic shading technique system available, it is a lot easier to do so.
A shader block in a technique file is declared as the following:
A native shader source file will contain only the GLSL code necessary for the shading algorithm, and no uniform variable or vertex input declarations. These things are automatically generated by the “GLSL preprocessor” based on the data declared in a technique file. This way we can easily have a fallback for platforms that, for example, don’t support uniform buffers. The preprocessor can break the uniform buffer declaration in the technique file into individual uniform variable declarations.
Other things that we can do after the technique files are parsed is to concatenate the contents
of all native source files that were declared in the technique and replace non portable things like
in/out attributes with the correct names for the platform or shading language version.
Vertex formats (or vertex inputs if you will) are also generated based on information from the shading technique file.
A technique block is declared as the following:
A technique can also have a Geometry Shader (
gs property) and in the future I intend to add support for tessellation control shaders.
New render states can also be added at will, according to the needs of the engine. A technique can have an arbitrary number of passes.
An example of a complete technique file
And the two GLSL shaders used with it:
Things to do…
One thing that I will very likely implement in the future will be some sort of inheritance mechanism.
For example: Shading technique
RenderWithAnimation inherits from technique
That way I can reuse uniform variable declarations, vertex format and source files from a base
technique and only overwrite parts of it.