Shaders
From Leadwerks Developer Wiki
Contents |
Introduction
Leadwerks Engine provides support for vertex and fragment (pixel) shaders. Shaders can be used to render a material or for full-screen post-processing effects.
Includes
Leadwerks Engine will pre-parse shader files. One feature the parse adds is the ability to include other files. This is performed with the keyword Include:
Include "MyShaderCode.frag"It does not matter what the file extension is called, but the .vert and .frag extensions are usually used for vertex and fragment shader, respectively.
This feature allows the programmer to work with a much smaller set of shader source code files. For example, the lighting routines are written in separate .glsl files, and both the mesh and terrain fragment shaders use the same code. The file path specified may use the abstract file system too (e.g. load "abstract::MyShaderCode.frag").
Leadwerks Engine allows the programmer to pass values to the shader during program execution. This allows dynamic values to be used in rendering, or real-time changes of colors or other settings.
Defines
Includes can be combined with defines. The shader pre-processor uses define statements to dismiss or keep sections of code before the shader is compiled. A Define statement tells the pre-compiler to "keep this chunk of code".
This fragment shader will draw a black fragment:
void main () { gl_FragColor=vec4(0.0,0.0,0.0,1.0); #ifdef LW_FOO gl_FragColor=vec4(0.0,0.0,1.0,1.0); #endif }
This fragment shader will draw a blue fragment:
#define LW_FOO void main () { gl_FragColor=vec4(0.0,0.0,0.0,1.0); #ifdef LW_FOO gl_FragColor=vec4(0.0,0.0,1.0,1.0); #endif }
We can use an include and a define to create new shaders from a base shader:
#define LW_FOO Include "foo.frag" }
Foo.frag:
void main () { gl_FragColor=vec4(0.0,0.0,0.0,1.0); #ifdef LW_FOO gl_FragColor=vec4(0.0,0.0,1.0,1.0); #endif }
The engine will load this shader and send the following source code to the GPU pre-processor:
#define LW_FOO void main () { gl_FragColor=vec4(0.0,0.0,0.0,1.0); #ifdef LW_FOO gl_FragColor=vec4(0.0,0.0,1.0,1.0); #endif }
The GLSL pre-processor will parse the GLSL define statements and the final code that gets compiled will look like this:
void main () { gl_FragColor=vec4(0.0,0.0,0.0,1.0); gl_FragColor=vec4(0.0,0.0,1.0,1.0); }
By using defines, we can greatly reduce the number of shader code files we have to edit, and eliminate redundant code. For example, all variations of the mesh shaders are packed into the main mesh.vert and mesh.frag files. Different features can be enabled by using a shader that declares define statements and then includes the main mesh shader files.
Compiling
A shader does not get compiled until it is used with the SetShader command (This might be done manually for post processing effects on images, or internally when objects get drawn). If a shader fails to compile, an error message will be added to the log containing information from the compile process. Unless there is a problem with the shader or the driver, shaders should always compile successfully. If a shader fails to compile, it is best to display a critical error immediately, rather than leave the programmer guessing which shaders might not be working correctly.
Post-processing Effects
Shaders can be used to render post-processing effects like bloom and depth of field. This is accomplished by render the scene to a texture-based buffer and then drawing the color texture onscreen with a shader applied.
Some shader post-processing effects include:
Non-Shader Based Post Processing Effects:
Built-in Uniforms
The following values will always be passed to a shader if they are declared as uniforms in the shader source code:
float apptime
- Application time.
float terrainsize
- The total terrain size in units (width = height).
vec3 terrainscale
- The terrain scale.
- This can be used to get the terrain resolution (128,256,512,1024, etc.): terrainsize/terrainscale.X = Terrain Resolution
vec4 ambientlight
- The ambient light color of the current world (while rendering).
float rnd
- A random value between 0 and 1.
float bumpscale, float gloss, float specular
- Bumpscale, gloss and specular. These are set based on the current material's "bumpscale", "gloss" and "specular" keys and are used (in the default shaders) to change the intensity of these effects. See also Materials.
vec2 buffersize
- Width and height of the current buffer.
float bufferaspect
- The current buffer's width divided by height.
float camerazoom
- The zoom of the current camera.
vec2 camerarange
- The near and far range of the current camera.
vec3 cameraposition
- The global position of the current camera.
mat3 cameramat3
- The 3x3 matrix of the current camera.
mat4 cameramat4
- The 4x4 matrix of the current camera.
mat3 camerainversemat3
- The inverse 3x3 matrix of the current camera.
mat4 camerainversemat4
- The inverse 4x4 matrix of the current camera.
Tutorials
Commands
See also SetMaterialShader/GetMaterialShader @ Materials.
LoadShader
- C: TShader LoadShader ( str vertpath, str fragpath, str defines="" )
- C++:
- Shader::Shader( const_str vertpath, const_str fragpath, const_str defines = "" )
- void Shader::Load( const_str vertpath, const_str fragpath, const_str defines = "" )
- BlitzMax: LoadShader:TShader( vertpath$, fragpath$ [ ,defines$="" ] )
- Pascal: function LoadShader ( vertpath:PAnsiChar; fragpath:PAnsiChar; defines:PAnsiChar ): THandle;
- This command loads a shader using the specified vert and fragment files. Defines can be used to pass any special parameters to the shader. The defines string will be added to the beginning of both shader source codes. It is not necessary to specify a fragment shader path if no fragment program is needed, as is the case for most shadow shaders. If a shader with the specified filename (and the same defines string) has already been loaded, it'll return a reference to that to avoid loading assets twice.
- [Examples]
SetShader
- C: void SetShader( TShader shader )
- C++: void Shader::Set( void )
- BlitzMax: SetShader( shader:TShader )
- Pascal: procedure SetShader ( shader:THandle );
- Sets the specified shader as the active shader.
- [Examples]
SetShader...
- C:
- void SetShaderFloat(TShader shader, str uniform, float x )
- void SetShaderFloatArray(TShader shader, str name, flt* ar, int count=1)
- void SetShaderVec2(TShader shader, str uniform, TVec2 v )
- void SetShaderVec3(TShader shader, str uniform, TVec3 v )
- void SetShaderVec4(TShader shader, str uniform, TVec4 v )
- void SetShaderInt(TShader shader, str uniform, int i )
- void SetShaderIntArray(TShader shader, str name, int* ar, int count=1)
- void SetShaderMat3(TShader shader, str uniform, mat3 mat )
- void SetShaderMat4(TShader shader, str uniform, mat4 mat )
- C++:
- void Shader::Set( const_str name, int value );
- void Shader::Set( const_str name, flt value );
- void Shader::Set( const_str name, const TVec2& value, int count=1 );
- void Shader::Set( const_str name, const TVec3& value, int count=1 );
- void Shader::Set( const_str name, const TVec4& value, int count=1 );
- void Shader::Set( const_str name, const TVec9& value, int count=1 );
- void Shader::Set( const_str name, const TVec16& value, int count=1 );
- void Shader::Set( const_str name, const int* pArray, int count=1 );
- void Shader::Set( const_str name, const flt* pArray, int count=1 );
- BlitzMax:
- void SetShaderFloat( name$, f# )
- void SetShaderVec2( name$, v:TVec2 )
- void SetShaderVec3( name$, v:TVec3 )
- void SetShaderVec4( name$, v:TVec4 )
- void SetShaderInt( name$, i )
- void SetShaderMat3( name$, mat:TMat3 )
- void SetShaderMat4( name$, mat:TMat4 )
- Pascal:
- procedure SetShaderInt ( shader:THandle; name:PAnsiChar; value:Integer=1 );
- procedure SetShaderFloat ( shader:THandle; name:PAnsiChar; value:Single=1 );
- procedure SetShaderVec2 ( shader:THandle; name:PAnsiChar; const value:TVec2; count:Integer=1 );
- procedure SetShaderVec3 ( shader:THandle; name:PAnsiChar; const value:TVec3; count:Integer=1 );
- procedure SetShaderVec4 ( shader:THandle; name:PAnsiChar; const value:TVec4; count:Integer=1 );
- procedure SetShaderMat3 ( shader:THandle; name:PAnsiChar; const value:TVec9; count:Integer=1 );
- procedure SetShaderMat4 ( shader:THandle; name:PAnsiChar; const value:TVec16; count:Integer=1 );
- procedure SetShaderIntArray ( shader:THandle; name:PAnsiChar; ar:Pointer; count:Integer=1 );
- procedure SetShaderFloatArray ( shader:THandle; name:PAnsiChar; ar:Pointer; count:Integer=1 );
- The SetShader... command set is used to pass a value to a shader uniform. Uniforms are stored in a map associated with the shader. A GPU sync is only required the first time a uniform is set. If the uniform does not exist in the shader, a warning will be written to the application log.
- [Examples]
FreeShader
- C: void FreeShader( TShader shader )
- C++: void Shader::Free( void ) Note: Also called from destructor
- Pascal: procedure FreeShader ( shader:THandle );
- Frees the specified shader from memory.
- [Examples]
