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%。
