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:
- Giving the
ModelBatch
aString
orFileHandle
vertex/fragment
-shader - Creating a
ShaderProgram
with vertex and fragmentShader
. - Creating a new
DefaultShader
with this vertex and fragmentShader
. - Creating a class, which implements
Shader
and use thisShaderClass
.
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:
- A
ModelBatch
which should draw everything. - An
Array<ModelInstance>
instances, to which i wanna apply theShader
. - A
Texture
and itsNormalMap
, both stored asTexture
s. - The
position
, thedirection
and thecolor
of my light, given asVector3
and its strength given asfloat
. - The
color
of the ambient light and its strength, given asVector3
andfloat
. - The light "Falloff" given as
Vector3
.
I need:
- To bind the
Texture
and itsNormal Map
to theShader
. - Give the light pos, direction, color and strength to the
Shader
. - Give the ambientlight color and strength to the
Shader
. - Give the
Falloff
Vector to theShader
. - Use this
Shader
for all "instances" in theArray
, which are all drawn with myModelBatch
.
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 useMaterial
Attributes
and whichAttributes
theDefaultShader
supports.In LibGDX there's a difference between a
Shader
andShaderProgram
. AShaderProgram
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 onShaderProgram
and use it to render a mesh. But theShaderProgram
itself does not "know" how it should render a model.The
Shader
interface is intended to bridge the gap between theShaderProgram
and theRenderable
(the latter being the smallest renderable part of a model). Thus aShader
most commonly encapsulates aShaderProgram
and makes sure it sets the correct uniforms etc. (Note that strictly speaking aShader
doesn't have to encapsulate aShaderProgram
. E.g. before GLES1 support was removed, there also was a GLES1 shader which managed the fixed rendering pipe instead of encapsulating aShaderProgram
)The
BaseShader
class is anabstract
helper class which implements theShader
interface, encapsulated aShaderProgram
and adds some helper methods to easily set uniforms. If you extend this class, you can easilyregister
andset
uniform, e.g. like this:This will set the unifom called "u_falloff" to the specified value
15f
(it will call setUniformX). If theShaderProgram
(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 */ }
. TheBaseShader
also adds the possibility to use aValidator
andSetter
for each uniform.The
DefaultShader
extends theBaseShader
and adds a default implementation for most of theMaterial
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(...)
, theModelBatch
will query theShaderProvider
for aShader
to use. This is because every possible combination of vertex attributes and material attributes might require a differentShader
. For example if you have twoModelInstance
s (or to be more precise twoRenderable
s), one with aTextureAttribute.Diffuse
and one without texture but with aColorAttribute.Diffuse
. Then most commonly, the ShaderProvider needs to create two differentShader
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 ownShader
(either by implementing it from scratch, extendingBaseShader
or extendingDefaultShader
). You can extendsDefaultShaderProvider
for this and fall back to theDefaultShader
if needed, e.g.: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.