LibGDX将特定着色器分配给ModelInstance

时间:2015-02-18 18:10:47

标签: libgdx shader

我最近一直在学习在libgdx中实现自己的着色器。 到目前为止,我使用自定义着色器提供程序执行此操作,该提供程序根据对象的userdata值在几个着色器之间进行选择;

public class MyShaderProvider extends DefaultShaderProvider {
    public final DefaultShader.Config config;
    final static String logstag = "ME.MyShaderProvider";
    //known shaders
    static public enum shadertypes {
        prettynoise,
        invert,
        standardlibgdx, 
        noise,
        distancefield,
        conceptbeam
    }

    public MyShaderProvider (final DefaultShader.Config config) {
        this.config = (config == null) ? new DefaultShader.Config() : config;
    }

    public MyShaderProvider (final String vertexShader, final String fragmentShader) {
        this(new DefaultShader.Config(vertexShader, fragmentShader));


    }

    public MyShaderProvider (final FileHandle vertexShader, final FileHandle fragmentShader) {
        this(vertexShader.readString(), fragmentShader.readString());
    }

    public MyShaderProvider () {
        this(null);
    }

    public void testListShader(Renderable instance){

        for (Shader shader : shaders) {

            Gdx.app.log(logstag, "shader="+shader.getClass().getName());

            Gdx.app.log(logstag, "can render="+shader.canRender(instance));

        }
    }

    @Override
    protected Shader createShader (final Renderable renderable) {

        //pick shader based on renderables userdata?
        shadertypes shaderenum = (shadertypes) renderable.userData;

        if (shaderenum==null){
                return super.createShader(renderable);
        }
        Gdx.app.log(logstag, "shaderenum="+shaderenum.toString());


        switch (shaderenum) {

        case prettynoise:
        {           
            return new PrettyNoiseShader();

        }
        case invert:
        {
              String vert = Gdx.files.internal("shaders/invert.vertex.glsl").readString();
              String frag = Gdx.files.internal("shaders/invert.fragment.glsl").readString();


            return new DefaultShader(renderable, new DefaultShader.Config(vert, frag)); 
        }
        case noise:
        {
            return new NoiseShader();
        }
        case conceptbeam:
        {
            Gdx.app.log(logstag, "creating concept gun beam ");
            return new ConceptBeamShader();
        }
        case distancefield:
        {
            return new DistanceFieldShader();
        }
        default:
            return super.createShader(renderable);

        }
        //return new DefaultShader(renderable, new DefaultShader.Config());

    }
}

这似乎有效。 我有一个应用了噪声着色器的对象,动画很好 我有一个带有倒置纹理着色器的物体,再次看起来很好 我有一大堆其他对象使用普通的默认着色器进行渲染。 我设置的提供程序似乎正在使用基于userData的不同着色器正确呈现不同的对象。

但是,我最近发现我使用新的着色器类型(ConceptBeamShader)创建的新对象仅使用默认着色器进行渲染。

对象用户数据设置与其他对象相同;

newlazer.userData = MyShaderProvider.shadertypes.conceptbeam;

然而,在任何时候都不会创建或使用conceptbeamshader。

实际上,createShader()似乎根本没有运行...暗示着色器数组中的现有着色器足够好。

使用上面的testListShader()函数,我看到“DefaultShader”位于“着色器”列表中,可以使用任何内容,因此它永远不会创建我希望该对象使用的新着色器: - /

我假设其他着色器之前只被选中,因为这些对象是在DefaultShader添加到该内部着色器列表之前创建的。

当使用DefaultShader时,它会立即存储在该提供程序列表中,并“吞噬”任何其他着色器。 MyShaderProvider类中的getShader函数是;

    public Shader getShader (Renderable renderable) {
    Shader suggestedShader = renderable.shader;
    if (suggestedShader != null && suggestedShader.canRender(renderable)) return suggestedShader;
    for (Shader shader : shaders) {
        if (shader.canRender(renderable)) return shader;
    }
    final Shader shader = createShader(renderable);
    shader.init();
    shaders.add(shader);
    return shader;
}

正如您所看到的那样,着色器已经循环,并且使用了第一个为“canRender”返回true的着色器。

所以......嗯...... 你应该怎么说“用这个着色器渲染这个ModelInstance”

我在网上阅读的这些教程似乎都没有涵盖这一点 - 事实上,官方网站上的那个教程似乎正在推荐我正在做的事情,所以这显然是我所缺少的。

谢谢,


修改的 它被实例化的地方被要求。不知道这有什么帮助,但在这里;

public static MyShaderProvider myshaderprovider = new MyShaderProvider();

然后将其分配给游戏设置中的模型补丁

modelBatch = new ModelBatch(myshaderprovider);

如上所述,我的其他着色器在我分配匹配的userdata的对象上工作并且可见,因此我99.9%确定正在调用提供程序,并且至少在某些情况下,为右侧选择正确的着色器宾语。 一旦“DefaultShader”被添加到内部着色器列表中,我的预感就会出错。

1 个答案:

答案 0 :(得分:9)

有几种方法可以指定用于ShaderModelInstance。其中之一是在ModelBatch上调用render方法时指定着色器:

modelBatch.render(modelInstance, shader);

这将提示ModelBatch使用此着色器,除非指定的Shader不适合渲染,否则它几乎总是这样做。是否适合(并且应该使用)着色器来呈现ModelInstance取决于对Shader#canRender(Renderable)的调用。

注意Renderable和ModelInstance之间的区别。这是因为单个ModelInstance可以包含多个部分(节点),每个部分可能需要另一个Shader。例如,当您拥有汽车模型时,它可能包含不透明机箱和透明窗口。这将需要为窗口和机箱设置不同的着色器。

因此,为整个ModelInstance指定着色器并不总是非常有用。相反,您可能需要更多地控制Shader用于模型的每个特定部分(每个render call)。为此,您可以实现ShaderProvider接口。这允许您使用您喜欢的每个可渲染的着色器。当然,您应该确保您使用的着色器的Shader#canRender(Renderable)方法为指定的true返回Renderable

扩展DefaultShaderProvider非常有用,这样当您不需要自定义着色器时,您就可以使用DefaultShader。在这种情况下,您必须确保在应该使用默认着色器和应该使用自定义着色器之间存在明确且一致的区别。也就是说,当应该使用自定义着色器时,DefaultShader#canRender方法不应该返回true,并且当使用DefaultShader时,您的Customshader#canRender方法不应返回true。 (本身并不特定于自定义或默认着色器,您始终需要知道要使用哪个着色器)

您正尝试使用ModelInstance#userData区分自定义着色器和默认着色器。这有两个问题:

  1. 对于ModelInstance的每个Renderable,userData都是相同的。因此,实际上你无法使你的设计复杂化。您也可以使用modelBatch.render(modelInstance, shader)
  2. DefaultShader是且无法识别任何用户特定数据。它只是查看它所知道的信息(材料,网格,环境等),并在true中返回canRender,如果它应该用于基于该信息进行渲染。
  3. 要解决第二点,libGDX 3D API附带attributes(用于环境和材质)。通过设计,这些允许您将Shader和Renderable与仅两个数字进行比较,这两个数字是属性的按位掩码。因此,首选,最简单和最快速的方法是使用自定义属性。这不仅可以让您明确地识别要使用的着色器,还可以指定使用着色器所需的信息(这是您希望使用其他着色器的原因)。

    可以找到herehere

    的示例