UnityShaderVariant的一些探究心得
最近遇到了一个问题,角色在Unity编辑器里运行渲染结果都是好的,打包到IOS上却发现,角色身上渲染的很黑.花了些时间查了查,又试了试,把这方面算是初步弄清楚了。
先说出现问题的原因,由于我们把shader打包进了AssetBundle中,并且在Shader中使用了shader_feature来定义了宏。
为了完整起见,先从unity的shader variant说起。
ShaderVariant
举个例子,对于一个支持法线贴图的Shader来说,用户肯定希望无论是否为材质提供法线贴图它的Shader都能正确的进行渲染处理。一般有两种方法来保证这种需求:
1.在底层shader(GLSL,HLSL等)定义一个由外部传进来的变量(如int),有没有提供法线贴图由外部来判断并给这个shader传参,若是有则传0,否则传1,在Shader用if对这个变量进行判断,然后在两个分支中进行对应的处理。
2.对底层shader封装,如Unity的ShaderLab就是这种,然后在上层为用户提供定义宏的功能,并决定宏在被定义和未被定义下如何处理。最终编译时,根据上层的宏定义,根据不同的组合编译出多套底层shader.
上述两种方法,各有利弊,对于前者由于引入了条件判断,会影响最终shader在GPU上的执行效率。而后者则会导致生成的shader源码(或二进制文件)变大。Unity中内置的Shader往往采取的是后者,所以这里只讨论这种情况。
Unity的Shader中通过multi_compile和shader_feature来定义宏(keyword)。最终编译的时候也是根据这些宏来编译成多种组合形式的Shader源码。其中每一种组合就是这个Uniy Shader的一个Variant。
Material与ShaderVariant的关系
一个Material同一时刻只能对应它所使用的Shader的一个variant。进行切换的要使用Material.EnableKeyword()和Material.DisableKeyword()来开关对应的宏,然后Unity会根据你设定的组合来匹配响应的shader variant进行渲染。如果你是在编辑器非运行模式下进行的修改那么这些keyword的设置会被保存到材质的.mat文件中,尝试用NotePad++打开.mat文件,你应该会看到类似于下面的一段内容(需要在编辑器设置里把AssetSerializationMode设置为Force Text):
%YAML 1.1
%TAG !u! tag:unity3d.com,:
--- !u! &
Material:
serializedVersion:
m_ObjectHideFlags:
m_PrefabParentObject: {fileID: }
m_PrefabInternal: {fileID: }
m_Name: New Material
m_Shader: {fileID: , guid: 3e0be7fac8c0b7c4599935fa92c842a4, type: }
m_ShaderKeywords: _B
m_LightmapFlags:
m_CustomRenderQueue: -
…
其中的m_ShaderKeywords就保存了这个材质球使用了哪些宏(keyword).
如果你手头有built-in Shader的源码可以打开里面的StandardShaderGUI.cs看一下Unity自己事怎么处理对于StandardShader的keyword设置的。
另外Shader.EnableKeyword,和Shader.DisableKeyword是对Shader进行全局宏设置的,这里不提了。
multi_compile和shader_feature的区别
完全没接触过它们的同学可以先看官方文档的介绍,multi_compile是一直都有的,shader_feature是后来的unity版本中加入的关键字。
举例介绍一下multi_compile和shader_feature:
1.如果你在shader中添加了
#pragma multi_compile _A _B
#pragma multi_compile _C _D
那么无论这些宏是否真的被用到,你的shader都会被Unity编译成四个variant,分别包含了_A _C,_A _D, _B _C,_B _D四种keyword组合的代码
2.如果是
#pragma shader_feature _A _B
#pragma shader_feature _C _D
那么你的shader只会保留生成被用到的keyword组合的variant,至于如何判定哪些组合被用到了,等后面提到Assetbundle时候再说。
ShaderVariant与Assetbundle的关系
我所遇到的问题正是和Assetbundle(简称AB)有关,原因是打成AB包之后shader_feature所定义的宏没有被正确包含进去。
上面说了multi_compile定义的keyword是一定能正确的生成对应的多种组合的shaderVariant,但shader_feature不尽然,Unity引入shader_feature就是为了避免multi_compile那种完整编译所导致组合爆炸,很多根本不会被使用的shader_variant也会被生成。Unity在处理shader_feature时会判断相应的keyword组合是否被使用。需要区分一下几种情况:
1.如果shader没有与使用它的材质打在一个AB中,那么shader_feature的所有宏相关的代码都不会被包含进AB包中(有一种例外,就是当shader_feature _A这种形式的时候是可以的),这个shader最终被程序从AB包中拿出来使用也会是错误的(粉红色).
2.把shader和使用它的材质放到一个AB包中,但是材质中没有保存任何的keyword信息(你在编辑器中也是这种情况),shader_feature会默认的把第一个keyword也就是上面的_A和_C(即每个shader_feature的第一个)作为你的选择。而不会把_A _D,_B _C,_B _D这三种组合的代码编译到AB包中。
3.把shader和使用它的材质放到一个AB包中,并且材质保存了keyword信息(_A _C)为例,那么这个AB包就只包含_A _C的shaderVariant.
可以看到shader_feature所定义的keyword产生的ShaderVariant并不是全部被打包到AB中,特别是你想在游戏运行时动态的通过EnableKeyWorld函数来进行动态修改材质使用的shaderVariant,如果一开始就没有把对于variant放进AB包,自然也就找不到。
ShaderVariantCollection
要正确的让各种variant正确的在游戏运行时正确处理,
最直接暴力的两种方法:
1.把Shader放到在ProjectSetting->Graphics->Always Include Shaders列表里,Unity就会编译所有的组合变种。
2.把Shader放到Resources文件夹下,也会正确处理,我猜也应该是全部keyword组合都编译,有知道的同学,麻烦留言告诉我。
但是这两种情况最大的问题就是组合爆炸的问题,如果keyword比较少还好,要是多了那真是不得了,比如你把standardShader放进去,由于它有大量的keyword,全部变种都生成的话大概有几百兆。另外一个问题就是这种办法没法热更新。自然不如放到AB包里的好控制。
放到AB包就又涉及到shader_feature的处理,为了在运行时动态切换材质的shadervariant,可以在工程里新建一堆材质,然后把每个材质设置成一种想要的keyword组合,把他们和shader放到一起打到一个AB中去,这样虽然能让shadervariant正确生成,但是这些Material是完全多余的。
为了解决这种问题,Unity5.0以后引入了ShaderVariantCollection(下面简称SVC),这里不讲用法,只说问题,这个SVC文件可以让我指定某个shader要编译都要编译带有哪些keyword的变种。并且在ProjectSetting->Graphics界面新加了一个Preloaded Shaders列表,可以让你把SVC文件放进去,编译时指定的Shader就会按照SVC中的设置进行正确的variant生成,而不会像Always Include Shaders列表中的那样全部变种都生成。
但是它在AB中的表现可就不尽如人意了,要让它起作用,就必须把它和对应的shader放在一个AB中,而且除了5.6以外版本,我试了几个都不能正确使用,不是一个variant都没生成,就是只生成一个shadervariant(和放一个没有设置keyword的材质效果一样).你可以自己用UnityStudio打开查看一下生成的AB内容。
写在最后
应该正确的理解Unity提供multi_compile和shader_feature以及ShaderVariantCollection的意图,根据自己的情况来选择合理的解决方案。
在查这个问题的过程中也google了一些,发现国外在这方面的讨论远没国内多,应该是因为老外很少使用热更新这种东西,也自然很少用AB。
尊重他人智慧成果,若要转载,请注明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/Shader_Variant.html
UnityShaderVariant的一些探究心得的更多相关文章
- Activity是如何挂载Pargment的Day35
Activity是如何挂载Pargment的Day35 mebile5.0 1.Fragment优化早上任务 思路 一个主Activity 短信群发器 中秋给朋友发短信,都能够加上他们的姓名,这样他们 ...
- iOS中的预编译指令的初步探究
目录 文件包含 #include #include_next #import 宏定义 #define #undef 条件编译 #if #else #endif #if define #ifdef #i ...
- windows类书的学习心得(转载)
原文网址:http://www.blogjava.net/sound/archive/2008/08/21/40499.html 现在的计算机图书发展的可真快,很久没去书店,昨日去了一下,真是感叹万千 ...
- Activity栈与任务管理探究1——栈与任务的概述
Activity栈与任务管理探究1--栈与任务的概述 内容概览 1. 前言 2. Activity中的Stack 3. Activity中的Task 4. Activity栈与任务管理基本原则 1. ...
- BUG心得
在<程序员,你会从 Bug 中学习么?>一文中,我写了我是怎样追踪这些年遇到的最有趣 bug 的.最近我重新浏览了这所有的 194 个条目(历时 13 年),看看我从这些 bug 中学到了 ...
- sed从入门到深入的使用心得
本人已经此系列的sed文章整理到pdf中,欢迎下载:玩透sed:探究sed原理 sed系列文章: sed修炼系列(一):花拳绣腿之入门篇sed修炼系列(二):武功心法(info sed翻译+注解)se ...
- windows类书的学习心得
原文网址:http://www.blogjava.net/sound/archive/2008/08/21/40499.html 现在的计算机图书发展的可真快,很久没去书店,昨日去了一下,真是感叹万千 ...
- BackBone及其实例探究
摘要 我们小组对MVC框架进行了学习.我的队友们已经在博客中对MVC的设计模式及优缺点进行了详细的探讨与分析,因此我的博客中只对MVC进行简单的介绍,而我将把重心放在Backbone MVC框架一 ...
- Weka 二次开发使用心得
Weka 二次开发使用心得 一.weka数据挖掘流程 使用weka图形界面,初步尝试了下数据的预处理.分类.关联等操作,因为weka本身就是一个开源的机器学习库,于是想自己尝试下利用weka的api进 ...
随机推荐
- aiohttp AppRunner的用法
参考廖雪峰的aiohttp教程,会出现两个DeprecationWarning, loop argument is deprecated Application.make_handler(...) i ...
- java Boolean和boolean的区别
Boolean b1=new Boolean("false"); Boolean b2=new Boolean("tRue"); Boolean b3=new ...
- 前端---js02
主要内容 1.数组 2.字符串 3.Date日期对象 4.内置对象 5.定时器 6.DOM 7.伪数组 内置对象: 1 数组(列表) Array (1) 数组的创建 <script>//字 ...
- day08文件操作
今日内容 文件操作 内容详细 1.打开文件 1.open("作业讲解.py",mode="r",encoding="utf-8") 2.文件 ...
- 2018-2019-2 20165313 Exp2 后门原理与实践
实践基础问题回答 (1)例举你能想到的一个后门进入到你系统中的可能方式? 应用程序设定后门,用户安装后,后门就可以进入.例如课堂上讲到的百度. (2)例举你知道的后门如何启动起来(win及linux) ...
- HDFS基本Shell命令
bin目录下: 1. hadoop fs 基本操作命令,类似linux shell 2. hadoop dfsadmin 管理命令 3. hadoop fsck (1. 检查hdfs中文件的健康 ...
- python glob模块使用笔记(更新)
glob模块是通配用的,用于列出符合通配格式的文件 glob.glob(path) path是用于匹配的字符串,类似简单版的正则吧 其中 * 匹配任意多个字符 ? 匹配一个字符 [1-9] 匹配指定范 ...
- 在安卓手机上安装完整kali linux系统
俗话说,没图说个JB.好我马上上图 提醒:我在这里只是提供一个思路过程,希望可以帮到你,同时我也做一个记录,有任何问题欢迎 0.0.:I87OI94664 威信 :Z2tsYmI1MjA= (b ...
- 使用ghost装完系统后出现“引用了一个不可用的位置”
用GHOST版光盘安装完系统后,只有一个C盘,无法点桌面刷新,然后提示“D:/我的文档引用了一个不可用的位置.... 1.在管理员账户下定位到如下键值:“HKEY_CURRENT_USER\Softw ...
- JavaScript中的ononline事件和onoffline事件
关于这个时间的描述到处都有,但基本上都是说离线在线什么的我一下子还没反应过来.后再在这里看到了一句话:"断开网络再联网试试,就可以看到连线的提示."这才反应过来,原来指的是网络状态 ...