Commit 5060aa96 authored by Jan Möbius's avatar Jan Möbius
Browse files

Updated Documentation for nonFixed Pipeline and the shaderGenerator by Christopher

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@19745 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 9b05421c
/** \page NonFixedPipelinePage The non-fixed OpenGL rendering pipeline
On this page we will explain how to use the non-fixed (modern) OpenGL rendering pipeline in OpenFlipper.
First let us introduce the necessary classes.
The ACG library supports two different approaches to rendering the scenegraph: direct fixed-function based and data-driven.
This page focuses on how to make use of the latter.
The data-driven approach makes it possible to have complete control over the rendering pipeline in a custom render-plugin.
\section baseNode The BaseNode in the Scenegraph
Every node in the scenegraph is able to provide the renderer with data by creating RenderObject.
A RenderObject is a collection of data, which is necessary to execute a draw-call.
This data consists of information about: shaders, textures, uniforms, OpenGL-states, vertex-layout, vertex buffer etc.
These RenderObjects are created in the getRenderObjects() function of a node.
Finally, a node is not supposed to execute draw-calls on its own in getRenderObjects(), but should only provide render-object data.
\section iRenderer The IRenderer interface
The IRenderer interface can be derived from and used to attach a RenderObject via the addRenderObject function from within each node within the Scenegraph that is
to be rendered.
Custom render plugins for the data-driven rendering approach have to implement the IRenderer interface.
This interface provides basic functionality such as collecting render-object data from the scenegraph and executing a draw-call for a render-object.
\subsection renderObject Defining RenderObjects
The RenderObject class can be used to define the vertex buffer objects and set further information for the scene nodes.
This is the collection of rendering-data that is necessary for a draw-call.
It should be noted that any memory addresses set to a RenderObject have to remain valid until used in the render-plugin.
Thus, addresses set in the getRenderObjects() method should point to permanent data in the heap, not to local data in the stack!
Typically, RenderObjects are initialized somewhat similar to the following manner.
\code
RenderObject ro;
ro.initFromState(&_state); // copy opengl-states and transforms from GLState
ro.depthTest = true; // force depth-testing
ro.shaderDesc.shadeMode = SG_SHADE_UNLIT; // disable lighting
ro.emissive = Vec3f(1.0f, 1.0f, 1.0f); // base color
ro.vertexBuffer = myVBO; // vertex/index buffer
ro.indexBuffer = myIBO;
ro.vertexDecl = myVertexDecl; // vertex layout
ro.glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); // same as glDrawElements, but does not execute draw-call! stores data only instead
_renderer->addRenderObject(&ro);
\endcode
\section vertexDeclaration Defining the layout of a vertex element
The VertexDeclaration class is used to specify the layout of a vertex element (VertexElement)
......@@ -144,15 +175,9 @@ void Renderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _propert
// collect renderobjects + prepare OpenGL state
prepareRenderingPipeline(_glState, _properties.drawMode(), PluginFunctions::getSceneGraphRootNode());
// clear back buffer
ACG::Vec4f clearColor = _properties.backgroundColor();
glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render every object
for (int i = 0; i < getNumRenderObjects(); ++i)
renderObject(sortedObjects_[i]);
renderObject( getRenderObject(i) );
// restore common opengl state
// log window remains hidden otherwise
......@@ -163,18 +188,26 @@ void Renderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _propert
The code begins and ends by calling prepareRenderingPipeline() and finishRenderingPipeline()
which are helper-functions of IRenderer.
Here, prepareRenderingPipeline traverses the scenegraph to collect render-objects, sorts them
Here, prepareRenderingPipeline traverses the scenegraph to collect render-objects and sorts them
by priority in ascending order.
The sorted list of renderobjects is then stored in the sortedObjects_ array, inherited of IRenderer.
We then proceed to clear the back buffer and render each object with another helper-function: renderObject().
renderObject() prepares OpenGL states (vertex/index source, boolean glEnable states..),
sets shader uniforms according the data in the RenderObject structure and executes the draw call.
The sorted list of renderobjects can be accessed via getRenderObject().
Another helper function: renderObject() prepares OpenGL states (vertex/index source, boolean glEnable states..),
sets shader uniforms according the data in the RenderObject structure and executes the draw call of a RenderObject.
Furthermore it is possible to force the use of a shader or disallow any changes made via glEnable/glDisable
by specifying the 2nd and 3rd parameter of renderObject().
Note that renderObject() is only a helper-function and may also be ignored and implemented on your own.
Finally, finishRenderingPipeline() resets the OpenGL state machine such that it does not
interfere with following draw-calls made by Qt.
The advantage of this data-driven design is that a custom render-plugin has complete control over the scene-rendering.
For instance, it is possible to setup custom shaders, modify provided shaders, perform multiple render-passes into custom FBOs etc.
If custom FBOs are used in a renderer, the input FBO can be easily restored by calling restoreInputFbo().
Also, the input FBO should not be cleared by a render-plugin, as this is done already by OpenFlipper!
The depth-peeling renderer plugin is a more complex example, but showcases the flexibility of the renderer interface.
It makes use of global shader effects which are fully integrated to the shader-generation pipeline.
Only small changes to an existing shader have to be made in order to implement depth peeling and
......
/** \page ShaderGenPage Dynamic shader assembly overview
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 with a vertex, fragment and eventually geometry shader.
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.
Supported GLSL versions are 1.5 and higher.
GLSL versions 1.3 and higher are supported.
\section shaderFixed Fixed shader code
......@@ -34,17 +34,16 @@ The following naming conventions for uniforms, stage input/output and local vari
- vertex shader output: prefix "outVertex"
- geometry shader input: prefix "outVertex"
- tessellation-control shader output: prefix "outTc"
- geometry shader output: prefix "outGeometry"
- tessellation-evaluation shader output: prefix "outTe"
- fragment shader input: prefix either "outVertex" or "outGeometry", depends if a geometry shader is active.
- geometry shader output: prefix "outGeometry"
- 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.
****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.
\section shaderDefines Default defines
......@@ -76,6 +75,33 @@ Additionally the light setup is passed to the shader with the following defines.
\endcode
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.
\code
SG_INPUT_POSOS // input object space position
SG_INPUT_POSVS // input view space position
SG_INPUT_POSCS // input clip space position
SG_INPUT_NORMALOS // input object space normal
SG_INPUT_NORMALVS // input view space normal
SG_INPUT_TEXCOORD // input texcoord
SG_INPUT_VERTEXCOLOR // input vertex color
SG_OUTPUT_X // output corresponding to SG_INPUT_X, ie. SG_OUTPUT_POSOS ..
\endcode
Request defines provided by shader modifiers or templates.
Add these somewhere at the beginning of the shader.
\code
SG_REQUEST_POSOS // shader requires object space position
SG_REQUEST_POSVS // shader requires view space position
SG_REQUEST_NORMALOS // shader requires object space normal
SG_REQUEST_NORMALVS // shader requires view space normal
SG_REQUEST_TEXCOORD // shader requires texcoord
SG_REQUEST_VERTEXCOLOR // shader requires vertex color
\endcode
\section shaderUniforms Default uniforms
The following default uniforms are guaranteed to be added to any generated shader:
......@@ -139,72 +165,114 @@ samplerY g_TextureX; // X: texture stage id = 0,...,numTextures-1 Y: samplerT
ShaderModifiers can add custom uniforms to a generated shader.
\section vertexIO Vertex shader IO
\section ShaderAttributeIO Shader attribute IO
\code
// inputs from vertex buffer
in vec4 inPosition;
in vec3 inNormal; // only added iff shadeMode != SG_SHADE_UNLIT
in vecX inTexcoord; // only added iff textured, X = texture dimension
in vec4 inColor; // only added iff vertexColors were enabled in the ShaderGenDesc
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.
// outputs
out vec4 outVertexPosCS; // position in clip space
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.
out vecX outVertexTexcoord; // only added iff textured, X = texture dimension
out vec4 outVertexColor; // only added iff shadeMode == SG_SHADE_GOURAUD or SG_SHADE_FLAT
The implementation of this function looks as follows for each stage:
out vec3 outVertexNormal; // only added iff shadeMode == SG_SHADE_PHONG
out vec3 outVertexPosVS; // position in view space, only added iff shadeMode == SG_SHADE_PHONG
Geometry shader:
\code
void sg_MapIO(int inIdx)
{
gl_Position = gl_in[inIdx].gl_Position;
gl_PrimitiveID = gl_PrimitiveIDIn;
SG_OUTPUT_X = gl_in[inIdx].SG_INPUT_X
}
\endcode
Tess-control shader:
\code
void sg_MapIO(int inIdx, int outIdx)
{
gl_out[outIdx].gl_Position = gl_in[inIdx].gl_Position;
gl_out[outIdx].SG_OUTPUT_X = gl_in[inIdx].SG_INPUT_X
}
\endcode
ShaderModifers can add custom shader IO to any stage.
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:
\code
void sg_MapIOBarycentric()
{
SG_OUTPUT_X = gl_TessCoord.x * SG_INPUT_X[0] + gl_TessCoord.y * SG_INPUT_X[1] + gl_TessCoord.z * SG_INPUT_X[2];
// same for gl_Position
}
void sg_MapIOBilinear()
{
// bilerp for SG_OUTPUT_X <- SG_INPUT_X[0..3] by gl_TessCoord.xy
// same for gl_Position
}
\endcode
\section geometryIO Geometry shader IO
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:
\code
// inputs from vertex shader
in vec4 outVertexPosCS[];
in vecX outVertexTexCoord[]; // only added iff textured, X = texture dimension
in vec4 outVertexColor[]; // only added iff vertexColors were enabled in the ShaderGenDesc, or shadeMode == SG_SHADE_GOURAUD or SG_SHADE_FLAT
in vec3 outVertexNormal[]; // only added iff shadeMode == SG_SHADE_PHONG
in vec3 outVertexPosVS[]; // only added iff shadeMode == SG_SHADE_PHONG
SG_OUTPUT = gl_TessCoord.x * SG_INPUT[0] + gl_TessCoord.y * SG_INPUT[1] + gl_TessCoord.z * SG_INPUT[2];
\endcode
The generator then replaces the SG_OUTPUT, SG_INPUT keywords with each available default attribute.
// outputs
out vec4 outGeometryPosCS; // position in clip space
Example 1: Displace vertices along normals in a vertex shader
out vecX outGeometryTexCoord; // only added iff textured, X = texture dimension
out vec4 outGeometryColor; // only added iff vertexColors were enabled in the ShaderGenDesc, or shadeMode == SG_SHADE_GOURAUD or SG_SHADE_FLAT
\code
// this is a vertex shader template!
#define SG_REQUEST_NORMALOS
uniform float factor;
out vec3 outGeometryNormal; // only added iff shadeMode == SG_SHADE_PHONG
out vec3 outGeometryPosVS; // position in view space, only added iff shadeMode == SG_SHADE_PHONG
void main()
{
SG_VERTEX_BEGIN;
SG_VERTEX_END;
vec4 newPosOS = SG_INPUT_POSOS + vec4(SG_INPUT_NORMALOS, 0) * factor;
vec4 newPosCS = g_mWVP * newPosOS;
SG_OUTPUT_POSCS = gl_Position = newPosCS;
#ifdef SG_OUTPUT_POSVS
SG_OUTPUT_POSVS = g_mWV * newPosOS;
#endif
#ifdef SG_OUTPUT_POSOS
SG_OUTPUT_POSOS = newPosOS;
#endif
}
\endcode
\section fragmentIO Fragment shader IO
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:
\code
// inputs from vertex shader, only if there is no geometry shader
in vec4 outVertexPosCS;
in vecX outVertexTexCoord; // only added iff textured, X = texture dimension
in vec4 outVertexColor; // only added iff vertexColors were enabled in the ShaderGenDesc, or shadeMode == SG_SHADE_GOURAUD or SG_SHADE_FLAT
in vec3 outVertexNormal; // only added iff shadeMode == SG_SHADE_PHONG
in vec3 outVertexPosVS; // only added iff shadeMode == SG_SHADE_PHONG
// if there is a geometry shader, fragment inputs are instead:
in vec4 outGeometryPosCS;
in vecX outGeometryTexCoord; // only added iff textured, X = texture dimension
in vec4 outGeometryColor; // only added iff vertexColors were enabled in the ShaderGenDesc, or shadeMode == SG_SHADE_GOURAUD or SG_SHADE_FLAT
in vec3 outGeometryNormal; // only added iff shadeMode == SG_SHADE_PHONG
in vec3 outGeometryPosVS; // only added iff shadeMode == SG_SHADE_PHONG
// outputs
out vec4 outFragment; // fragment color
void modifyVertexIO(ACG::ShaderGenerator* _shader)
{
_shader->addDefine("SG_REQUEST_NORMALOS");
}
void modifyFragmentEndCode(QStringList* _code)
{
_code->push_back("outFragment = vec4(normalize(SG_INPUT_NORMALOS) * 0.5 + 0.5, 1);");
}
\endcode
\section vertShaderGen Vertex shader code generation
Some parts of a generated shader contain fixed code snippets, which are assembled according to specification in ShaderGenDesc.
......@@ -278,36 +346,14 @@ for each modifier:
\note Note that if directives are not generated at all if they do not change the compiled code.
\section geomShaderGen Geometry shader code generation
\section geomShaderGen Tessellation/Geometry shader code generation
Geometry shaders have a different layout and due to their complexity, there is no clear begin or end code marker for geometry shaders.
The ShaderProgGenerator does not generate a main() function for a geometry shader.
However, it provides a convenient passthrough mapping function from vertex shader outputs to fragment shader inputs:
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.
\code
void sg_MapIO(const int inIdx)
{
gl_Position = gl_in[inIdx].gl_Position;
outGeometryPosCS = outVertexPosCS[inIdx];
#if SG_TEXTURED
outGeometryTexCoord = outVertexTexCoord[inIdx];
#endif
#if SG_SHADE_GOURAUD or SG_SHADE_FLAT
outGeometryColor = outVertexColor[inIdx];
#endif
#if SG_SHADE_PHONG
outGeometryNormal = outVertexNormal[inIdx];
outGeometryPosVS = outVertexPosVS[inIdx];
#endif
}
\endcode
The rest of geometry shader including the main() body has to be provided in the ShaderGenDesc in form of a template.
\section fragShaderGen Fragment shader generation
......@@ -404,4 +450,14 @@ The modifyLightingCode function of a modifier receives a light index [0,...,numL
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.
\code
SG_FRAGMENT_BEGIN;
sg_vNormalVS = g_mWV * (mTangentSpace * vNormalTS); // dot3
sg_cColor = vec4(g_cEmissive, SG_ALPHA);
SG_FRAGMENT_LIGHTING;
SG_FRAGMENT_END;
\endcode
*/
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment