I reached a point in my wip game, where I want to make it more eye-appealing. Currently I add some Ambientlight
and a Directionla-light
to an Environment
and render my scene with it. But now I want to add a custom Shader
to it. So I have been looking for some tutorials and for some reason in almost every tutorial they have used another "version" of using Shader
in their game:
ModelBatch
a String
or FileHandle
vertex/fragment
-shaderShaderProgram
with vertex and fragment Shader
.DefaultShader
with this vertex and fragment Shader
.Shader
and use this ShaderClass
.I think there are more possibilitis, because there is also ShaderProvider
and other classes as well.
I am a bit confused cause of all this posibilities. so I am looking for someone who can point me in the right direction.
To make it easier for you i tell you what i have and what I need:
I have:
ModelBatch
which should draw everything.Array<ModelInstance>
instances, to which i wanna apply the Shader
.Texture
and its NormalMap
, both stored as Texture
s.position
, the direction
and the color
of my light, given as Vector3
and its strength given as float
.color
of the ambient light and its strength, given as Vector3
and float
.Vector3
.I need:
Texture
and its Normal Map
to the Shader
.Shader
.Shader
.Falloff
Vector to the Shader
.Shader
for all "instances" in the Array
, which are all drawn with my ModelBatch
.Another thing I would like to be able to do is, using different Texture
s for different models.
I have all my ModleInstance
s with a textured Material
. And I have the normal map for all those different Texture
s. Now i want to be able to bind the right Texture
and Normal Map
to the Shader
, depending on the Texture
in the Material
of the Modelinstance
.
A more "advanced" thing, I would like to if it is possible, is using different Shader
s for different ModelTypes
in my game (Stonewall uses a Shader
without "Specularity" and "Reflection", while Metalwalls use Specularity
for example).
What would be the best way to do this things in Libgdx?
How can i bind the different variables i have? (With ShaderProgram i can use setUniformi
for example).
Thanks a lot. If you need more information or if it is hard to understand let me know and i will try to create a better question.
Edit: I think the best way in my case creating a new Shader
class, which implements Shader
. But i would like to hear about all the other possibilities, their pros and their cons, regarding design, performance and possible restrictions.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With