SRP Batch合批失败


Q:最近尝试用SRP Batch,但感觉非常难以捉摸。这里所有的物体都是用的同一个Shader,而且关键字也都一样,也就是同一个变体。按说应该可以合并Batch的,但第二个提示Shader的关键字不同,SRP合并失败。

在网上查了一些资料,没有有用的信息。自己试了一下,只要改其中的一些RenderQueue,SRP Batch的结果就有变化(不能合并的数量变少了)。

但是,RenderQueue不能随便改吧?会影响性能。而且对于半透的来讲,渲染结果都会错。更何况改起来非常麻烦。各路大神们,想了解一下大家是怎么解决这个问题的?另外,大家的项目有没有用SRP Batch?还是用的传统的Static Batch?

有人提醒,把光图去掉试试,虽然的确有用,但总不能去掉光图吧?

又试了一下,专门把这些结点单独烘一下,也有用。看来跟光图也没直接关系。

A:这个应该是mat序列化文件里残留的关键字导致,Unity的Bug。

比如说:美术先用创建一个材质a,用的lit shader,调整自发光设置,导致材质里多了一个关键字_EMISSION,然后美术把这个材质改成了自定义Shader。这时候就算你的自定义Shader里没有_EMISSION这个关键字,材质的序列化文件里还是会记录下来。然后再创建一个材质b,和材质a属性完全一样,只是没有残留的关键字_EMISSION,你会惊讶的发现材质a与材质b不能SRP Batcher,原因写:Node use different keywords。并且这时候你用Frame Debuggger去看这两个材质的DrawCall,你是看不到_EMISSION这个关键字的,因为实际上确实没用到。如图:

修改关键字后,两个SRP Batch变成一个了:

另外,补充一下,之前说的改RenderQueue就能解决部分问题,中间确实是这样。但后来经过测试,我把整个的光图重新烘了一下,问题就解决了。中间做了什么已经不清晰,原因未知。不过,半透的东西我还需要再确认一下是不是改keywords就没问题了。

现在大致摸到门路了,如果Batch和Batch之间上下紧挨着,变体又是一个,就是关键字的bug问题。如果并不是紧挨着,变体虽然是同一个,也不能合并Batch。这时,需要先调整RenderQueue,让它们渲染次序紧挨着,Batch才可能会合并,和NGUI调整图层可以优化性能意思差不多。

其实只要把材质使用的Shader里没有的关键字删除就可以了:

public static void RemoveRedundantMaterialShaderKeywords(Material material)
    {
        List<string> materialKeywordsLst = new List<string>(material.shaderKeywords);
        List<string> shaderKeywordsLst = new List<string>();
        var getKeywordsMethod =
            typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords", BindingFlags.Static | BindingFlags.NonPublic);
        string[] keywords = (string[]) getKeywordsMethod.Invoke(null, new object[] {material.shader});
        shaderKeywordsLst.AddRange(keywords);

        getKeywordsMethod =
            typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords", BindingFlags.Static | BindingFlags.NonPublic);
        keywords = (string[]) getKeywordsMethod.Invoke(null, new object[] {material.shader});
        shaderKeywordsLst.AddRange(keywords);

        List<string> notExistKeywords = new List<string>();
        foreach (var each in materialKeywordsLst)
        {
            if (!shaderKeywordsLst.Contains(each))
            {
                notExistKeywords.Add(each);
            }
        }

        foreach (var each in notExistKeywords)
        {
            materialKeywordsLst.Remove(each);
        }

        material.shaderKeywords = materialKeywordsLst.ToArray();
    }

用工具改完之后,在编辑器上看,效果还是可以的。一个相机看整个场景,CPU渲染性能提升接近20%。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注