Shader变体与渲染材质异常

前言

这篇博文是对最近遇到的一次渲染材质异常的分析和梳理,打算以简练的形式记录下来以备后续查阅。PS. 个人深感像渲染引擎这样庞大的工程,要想驾驭得又好又快,还是得靠点滴积累的经验。

正文

原本的问题现象是这样的:某项目组在美术工程中正常表现的材质,将资源打包bundle导入到程序工程后出现了表现上的异常(变暗),同时原本支持的实例化能力也消失了。初步排查后发现,程序工程并没有读取到AssetBundle内正确的着色器资源变体(shader variant),项目所需的变体看来并没有成功打包到bundle内!

Unity在无法正确获取目标着色器变体时会按照某种“Fallback”规则定位到另一个存在的备胎,具体可以参考这篇博文中的“变体调用规则”部分。我们知道材质“变暗”是由于这种Fallback机制所致,不过我们并不关心Unity最终选择了哪个shader,真正关心的是为何项目所需的变体没有进入bundle。

第一步是去美术工程确认变体收集情况,可以在目标场景下通过 ProjectSettings->Graphics->Save to asset按钮激活收集逻辑,保存当前场景所有着色器变体信息到本地。参考如下图示:

shadervariants 文件内容示例

这里面每一个shader可能对应复数个变体,每个变体是由一种不同与其他变体的KeyWords组合决定的,如果你在编写shader时使用“shader_feature”去控制/定义关键字,那么Unity在收集变体时会判断该材质实际可能使用到的关键词,自动组合,生成如上图这样的变体列表。

在美术工程中收集的变体列表中,我们发现了所需的变体,参考下面节选代码标注部分。

unity_collected.shadervariants

  - first: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
    second:
      variants:
      - keywords:
        passType: 8
      - keywords: INSTANCING_ON _ALPHATEST_ON
        passType: 8
      - keywords: _ALPHATEST_ON
        passType: 8
      - keywords:
        passType: 13
      //以下是问题变体以及构成该变体的关键词组合:
      - keywords: DIRLIGHTMAP_COMBINED DYNAMICLIGHTMAP_ON LIGHTMAP_ON _ADDITIONAL_LIGHT_SHADOWS 
          _MAIN_LIGHT_SHADOWS INSTANCING_ON _NORMALMAP _OCCLUSIONMAP _SHADOWS_SOFT 
        passType: 13
      - keywords: _ADDITIONAL_LIGHT_SHADOWS _ALPHATEST_ON _MAIN_LIGHT_SHADOWS _METALLICSPECGLOSSMAP
          _NORMALMAP _OCCLUSIONMAP _SHADOWS_SOFT
        passType: 13
      - keywords: _ADDITIONAL_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS _METALLICSPECGLOSSMAP
          _NORMALMAP _OCCLUSIONMAP _SHADOWS_SOFT
        passType: 13
      - keywords: _ALPHATEST_ON
        passType: 13

美术工程能够收集到目标信息,但是打包时没有打入AssetBundle,这种情况下只有可能是打包逻辑在作祟,于是决定利用Vistual Studio的挂载断点功能单步下具体打包逻辑。

首先是构造出打包的目标资源列表,通过官方提供的 “AssetBundlesBrowser”插件触发Unity自己的打包逻辑,具体不赘述。

AssetBundlesBrowser 打包界面

接下来是要点,打包方法的入口逻辑在 URP自己的Editor工程目录下的 ShaderPreprocessor.cs文件内,具体而言是一个叫“OnProcessShader”的方法。我们可以通过实现 “IPreprocessShaders”这个接口中的“OnProcessShader”来重载shader variant打包处理逻辑,这是后话了。总之成功断点进入目标逻辑:

断点到打包时对ShaderVariant处理的逻辑入口函数

这个函数的入参很清晰,shader就是目前在处理的待打包的着色器本体,snipperData则是一系列Unity收集好的属性信息,最后一个List存放了所有在变体收集阶段获得到的变体信息。该方法内有嵌套的两次循环,外层循环负责遍历所有变体,内层循环负责校验参与构成当前变体的所有关键字是否“合法可用”,由“StripUnused”负责处理。

//URP-Editor::ShaderPreprocessor.cs
public void OnProcessShader(Shader shader, ShaderSnippetData snippetData,  IList<ShaderCompilerData> compilerDataList)
{
    ...    
    for (int i = 0; i < inputShaderVariantCount;)
    {
        bool removeInput = true;

        foreach (var supportedFeatures in ShaderBuildPreprocessor.supportedFeaturesList)
        {
            if (!StripUnused(supportedFeatures, shader, snippetData, compilerDataList[i]))
            {
                removeInput = false;
                break;
            }
        }
        ...
    }
    ...
}

然后很快就在如下位置发现了蹊跷:打包代码阻止了关键词“Additional_Light_Shadows”进入变体,这会使得所有包含了该条关键词的着色器变体文件无法生成!

//URP-Editor::ShaderPreprocessor.cs
bool StripUnusedFeatures(ShaderFeatures features, Shader shader, ShaderSnippetData  snippetData, ShaderCompilerData compilerData)
{
    ...
    // No additional light shadows
    if (IsFeatureEnabled(ShaderFeatures.ShadowsKeepOffVariants, features))
    {
        if (stripTool.StripMultiCompileKeepOffVariant(m_AdditionalLightShadows,  ShaderFeatures.AdditionalLightShadows))
            return true;
    }
    else
    {
        if (stripTool.StripMultiCompile(m_AdditionalLightShadows,  ShaderFeatures.AdditionalLightShadows))
            return true;
    }
    ...
}

通过进一步追踪,在如下代码中发现,美术工程并没有开启对AdditionalLightShadows的支持!

//URP-Editor::ShaderPreprocessor.cs
private static ShaderFeatures GetSupportedShaderFeatures(UniversalRenderPipelineAsset  pipelineAsset, int rendererIndex)
{
    ...
    if (pipelineAsset.additionalLightsRenderingMode == LightRenderingMode.PerPixel || clusteredRendering)
    {
        if (pipelineAsset.supportsAdditionalLightShadows)
        {
            shaderFeatures |= ShaderFeatures.AdditionalLightShadows;
        }
    }
    
    return shaderFeatures;
}

该项(supportAdditionalLightShadows)可以通过查看pipelineAsset文件获知,而这个Asset就是URP渲染管线的管线资源文件,可以在Grapics页签下的“Scriptable Render Pipeline Settings”中找到。

URPipelineAsset

我们在Inspector页签中观察目标Asset文件,记得只有切换到Debug模式才能看到所有设置数据。

下图中红框标注的地方就是所谓的控制标识符“supportsAdditionalLightShadows”数值的地方,美术工程中并没有开启额外光源的阴影渲染。

Pipeline Settings (Debug)

事实上出于性能考量,项目组并不打算要开启主光源之外灯光的阴影渲染,那么为何我们在变体收集阶段还是能收集到这一项关键词呢?经过进一步诊断后发现:

Unity收集shader变体的工作是在运行时进行的,urp管线默认总是会尝试添加 AdditionalLightsShadowCastPass,如果在收集变体的美术场景中不小心放入了额外的光源,使得场景总光源数大于1,那么如下运行时代码的Setup操作就会返回true,这样如果你的shader中带有 shader_feature Additional_Light_Shadow 这样的变体关键字,那么就会被成功收录到变体序列中,与其他重要且不可或缺的KeyWords黏合到一个变体中去。

//URP-Runtime::UniversalRenderer.cs
public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
{
    ...
    bool additionalLightShadows = m_AdditionalLightsShadowCasterPass.Setup(ref  renderingData);
    ...
    if (additionalLightShadows)
        EnqueuePass(m_AdditionalLightsShadowCasterPass);
}

最后我们在美术场景中找到多余的那一盏“点光源”,将之去除后再次收集+打包,成功输出了目标shader variant。

后记

我们最好能通过覆写 “IPreprocessShaders”的方式,给shader打包逻辑增加必要的告警,尽量避免或提早发现某些变体关键词被拒绝后导致的连坐其他重要关键词(比如 Instance_On)的情况。

以上!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容