Libgdx how to use shader in 3D

后端 未结 1 1886
梦如初夏
梦如初夏 2020-12-15 01:22

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

相关标签:
1条回答
  • 2020-12-15 02:20

    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 ModelInstances (or to be more precise two Renderables), one with a TextureAttribute.Diffuse and one without texture but with a ColorAttribute.Diffuse. Then most commonly, the ShaderProvider needs to create two different Shaders. 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.

    0 讨论(0)
提交回复
热议问题