An overall explanation of rendering pipeline of the 3d api can be found here. This tutorial guides you in creating a new Shader
from scratch. And this tutorial show how to use custom attributes to pass data to the shader. This wiki page also explains how to use Material
Attributes
and which Attributes
the DefaultShader
supports.
In LibGDX there's a difference between a Shader
and ShaderProgram
. A ShaderProgram
is only the GPU implementation (both the vertex and fragment shader program), which is basically only the compiled GLSL files. You can, for example, set uniforms on ShaderProgram
and use it to render a mesh. But the ShaderProgram
itself does not "know" how it should render a model.
The Shader
interface is intended to bridge the gap between the ShaderProgram
and the Renderable
(the latter being the smallest renderable part of a model). Thus a Shader
most commonly encapsulates a ShaderProgram
and makes sure it sets the correct uniforms etc. (Note that strictly speaking a Shader
doesn't have to encapsulate a ShaderProgram
. E.g. before GLES1 support was removed, there also was a GLES1 shader which managed the fixed rendering pipe instead of encapsulating a ShaderProgram
)
The BaseShader
class is an abstract
helper class which implements the Shader
interface, encapsulated a ShaderProgram
and adds some helper methods to easily set uniforms. If you extend this class, you can easily register
and set
uniform, e.g. like this:
public class MyShader extends BaseShader {
public final int u_falloff = register("u_falloff");
...
@Override
public void render (final Renderable renderable) {
set(u_falloff, 15f);
...
super.render(renderable);
}
}
This will set the unifom called "u_falloff" to the specified value 15f
(it will call setUniformX). If the ShaderProgram
(glsl files) don't implement an uniform called "u_falloff", it will simply ignore the call. You could also check this using: if (has(u_falloff)) { /* calculate falloff and set it */ }
. The BaseShader
also adds the possibility to use a Validator
and Setter
for each uniform.
The DefaultShader
extends the BaseShader
and adds a default implementation for most of the Material
Attributes
. Have a look at the source if you want to see the naming of each uniform. In practice, if you use the same uniform naming as the DefaultShader does, it is possible to only specify the glsl files and let the DefaultShader take care of setting the uniforms. Of course it is possible to extend the DefaultShader to add additional uniforms.
When you call ModelBatch#render(...)
, the ModelBatch
will query the ShaderProvider
for a Shader
to use. This is because every possible combination of vertex attributes and material attributes might require a different Shader
. For example if you have two ModelInstance
s (or to be more precise two Renderable
s), one with a TextureAttribute.Diffuse
and one without texture but with a ColorAttribute.Diffuse
. Then most commonly, the ShaderProvider needs to create two different Shader
s. Note that they can be of the same class (e.g. DefaultShader), but that the underlying GLSL files might be different.
The DefaultShader
takes care of this using preprocessor macros (an ubershader). Depending on the vertex attributes and material attributes, it will #define
multiple flags, causing the glsl program to be compiled specifically for that combination of vertex and material attributes. Have a look at the glsl files if you want to see how this is done.
So, in practice you will likely need you own ShaderProvider
and your own Shader
(either by implementing it from scratch, extending BaseShader
or extending DefaultShader
). You can extends DefaultShaderProvider
for this and fall back to the DefaultShader
if needed, e.g.:
public class MyShaderProvider extends DefaultShaderProvider {
... // implement constructor
@Override
protected Shader createShader (final Renderable renderable) {
if (renderable.material.has(MyCustomAttribute.Type))
return new MyShader(renderable);
else
return super.createShader(renderable);
}
}
tl;dr If you want to use your own or a modified version of the ubershader, using the same uniforms as the default shader does, then simply provide the glsl files. If you want to use the same uniforms but add an additional uniform or two, then it might be easy to extend DefaultShader. Otherwise (or if you're learning shaders) I would advise to create the shader from scratch as described in This tutorial.