LibGDX将特定着色器指定给ModelInstance

LibGDX将特定着色器指定给ModelInstance,libgdx,shader,Libgdx,Shader,我最近一直在学习如何在libgdx中实现我自己的着色器。 到目前为止,我使用自定义着色器提供程序实现了这一点,该提供程序根据对象的userdata值在几个着色器之间进行选择 public class MyShaderProvider extends DefaultShaderProvider { public final DefaultShader.Config config; final static String logstag = "ME.MyShaderProvider"

我最近一直在学习如何在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”在“shader”列表中,它可以渲染任何内容,因此它永远无法创建我希望该对象使用的新着色器:-/

我假设之前只拾取了其他着色器,因为这些对象是在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

modelBatch = new ModelBatch(myshaderprovider);
如前所述,我的其他着色器也在为匹配的userdata指定的对象上工作并可见,因此我99.9%确定调用了提供程序,并且至少在某些情况下,为正确的对象选择了正确的着色器。
我的直觉是,一旦“DefaultShader”被添加到内部着色器列表中,它就会出错。

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

modelBatch.render(modelInstance, shader);
这将提示ModelBatch使用此着色器,除非指定的着色器不适合渲染,否则它几乎总是这样做。着色器是否适合(并且应该使用)渲染ModelInstance取决于对的调用

请注意与ModelInstance之间的区别。这是因为单个ModelInstance可以由多个部分(节点)组成,每个部分可能需要另一个着色器。例如,当你们有汽车模型时,它可能由不透明的底盘和透明的窗户组成。这将需要为windows和机箱使用不同的着色器

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

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

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

  • 对于ModelInstance的每个可渲染部分,userData都是相同的。因此,实际上,您的设计过于复杂,毫无益处。您还可以使用
    modelBatch.render(modelInstance,shader)
  • DefaultShader无法识别任何用户特定的数据。它只需查看它知道的信息(材质、网格、环境等),并在
    canRender
    中返回
    true
    ,如果它应该用于基于该信息进行渲染
  • 为了解决第二点,附带了libgdx3dapi(用于环境和材质)。通过设计,这些选项允许您仅使用两个数字来比较着色器和可渲染,这两个数字是属性的按位掩码。因此,首选、最简单和最快的方法是使用自定义属性。这不仅可以让您明确地标识要使用的着色器,还可以让您指定使用该着色器所需的信息(使用其他着色器是有原因的)


    您可以找到一个示例来说明如何执行此操作。

    如果您从未实例化过提供商,请检查。您可能还想考虑使用材料而不是用户数据,请参阅:也许我应该提到我已经阅读了许多教程,包括官方网站上的教程。我还没有找到任何关于这方面的资料。很多东西根本不涉及模型实例,而是纯粹涉及基于sprite的使用。如前所述,我的着色器系统