【问题标题】:LibGDX assigning a specific shader to a ModelInstanceLibGDX 将特定着色器分配给 ModelInstance
【发布时间】:2015-02-18 18:10:46
【问题描述】:

我最近一直在学习如何在 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);

如前所述,我的其他着色器正在工作并且在我分配了匹配用户数据的对象上也是可见的,所以我 99.9% 确定正在调用提供程序,并且至少在某些情况下,为正确选择正确的着色器目的。 我的直觉是,一旦“DefaultShader”被添加到内部着色器列表中,它就会出错。

【问题讨论】:

  • 好吧,你永远不会实例化你的提供者,检查stackoverflow.com/help/how-to-ask。您可能还想考虑使用材料而不是 userData,请参阅:github.com/libgdx/libgdx/wiki/ModelBatch#shaderprovider
  • 也许我应该提到我已经阅读了很多教程,包括官方网站上的教程。我还没有找到任何涵盖这一点的内容。很多根本不涉及 ModelInstances,而是纯粹处理基于 sprite 的使用。如前所述,我的着色器系统设置适用于许多对象。提供者是从主类实例化和分配的。您提供的第二个链接确切地说明了我已经在做什么。我已经设置了一个自定义着色器提供程序,并且正在使用 userData 来选择要使用的着色器。我不明白为什么使用不同的材料场会改变结果。
  • 说您已阅读教程无助于澄清问题。就像说的那样,您永远不会实例化提供程序。也许从一个工作示例 (github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/…) 开始,然后对其进行修改以满足您的需求。至少它会帮助您创建一个 mcve (stackoverflow.com/help/mcve) 以包含在您的问题中。
  • 我有一个工作示例。我提到的其他着色器工作。我们必须在这里谈论不同的目的:-/我有 1 个提供者。它目前正在分配一组着色器,如上所述。如果它没有设置肯定没有他们会工作?我的动画噪声着色器?我的反转着色器?如果没有工作提供者,其中任何一个将如何工作:?我确实很困惑,但我的问题很简单; 如何将特定着色器分配给特定的 ModelInstance
  • 顺便说一句,该工作示例仅使用 BaseShaderProvider,甚至没有显示正在使用的自定义。显示一个默认提供程序和一个着色器的示例并不能回答某人有多个工作着色器但没有选择一个特定着色器的问题。

标签: libgdx shader


【解决方案1】:

有多种方法可以指定Shader 以用于ModelInstance。其中之一是在ModelBatch上调用render方法时指定Shader:

modelBatch.render(modelInstance, shader);

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

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

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

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

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

  1. 对于 ModelInstance 的每个 Renderable,userData 都是相同的。因此,实际上,您使设计过于复杂是徒劳的。您不妨使用modelBatch.render(modelInstance, shader)
  2. DefaultShader 知道并且不能知道任何用户特定的数据。它只是查看它所知道的信息(材质、网格、环境等),并在 canRender 中返回 true,如果它应该用于基于该信息进行渲染。

为了解决第二点,libGDX 3D API 附带了attributes(用于环境和材质)。通过设计,这些允许您仅将 Shader 和 Renderable 与两个数字进行比较,这两个数字是属性的按位掩码。因此,首选、最简单和最快的方法是使用自定义属性。这不仅可以让您明确地确定要使用哪个着色器,还可以让您指定使用着色器所需的信息(您想使用不同的着色器是有原因的)。

可以在herehere 中找到如何执行此操作的示例。

【讨论】:

  • 我一直在尝试按照您的建议使用自定义属性来实现此技术,但我的可渲染对象似乎继续使用默认着色器进行渲染。是否需要在我的自定义属性中指定某些内容,以确保其位掩码导致默认着色器的 canRender 方法为具有该属性的可渲染对象返回 false?
  • 不看代码很难说。我稍微修改了测试以扩展 DefaultShaderProvider 等,也许这会有所帮助:github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/…
  • 很好的解释虽然清楚了很多,但我确实会为我的着色器选择切换到自定义属性。希望我不会遇到 snapfractualpop 遇到的相同问题,因为该链接中的解决方案可能会遇到与 userdata 相同的问题 - 如果已经存在通过 canuse 条件的 DefaultShader,“createShader”甚至不会触发。 DefaultShaderProvider 在创建新的着色器之前检查所有以前制作的着色器。
  • 我不确定您为什么认为它可能会出现问题,但我可以向您保证,当 Renderable 包含时,DefaultShader 不会在 canRender 中返回 true不打算渲染的材质属性:github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/…
  • 这让人放心。只是为了确认“materialMask = renderable.material.getMask()”检查请求的可渲染对象是否具有与第一次传递给 DefaultShader 的可渲染对象相同的材质 ID?我看到设置第一个“materialMask”的唯一地方是第一次创建着色器时。 github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/… 因此,如果在创建时赋予着色器的第一个可渲染对象具有自定义属性,它以后不会也接受相同的属性吗?也许这就是@snapfractalpop 的问题。
猜你喜欢
  • 2016-12-20
  • 2015-08-27
  • 1970-01-01
  • 2021-10-06
  • 2018-09-27
  • 2016-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多