【Unity3D Shader编程】之十 深入理解Unity5中的Standard Shader(二)&屏幕油画特效的实现

 


本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

文章链接: http://blog.csdn.net/poem_qianmo/article/details/49719247

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

本文工程使用的Unity3D版本: 5.2.1 

 

概要:本文讲解了Unity中着色器编译多样化的思路,并对Standard Shader中正向基础渲染通道的源码进行了分析,以及对屏幕油画特效进行了实现。

 

众所周知,Unity官方文档对Shader进阶内容的讲解是非常匮乏的。本文中对Stardard Shader源码的一些分析,全是浅墨自己通过对Shader源码的理解,以及Google之后理解与分析而来。如有解释不妥当之处,还请各位及时指出。

 

依然是附上一组本文配套工程的运行截图之后,便开始我们的正文。本次的选用了新的场景,正如下图中所展示的。

城镇入口(with 屏幕油画特效):



城镇入口(原始图):



 图依然是贴这两张。文章末尾有更多的运行截图,并提供了源工程的下载。先放出可运行的exe下载,如下:


【可运行的本文配套exe游戏场景请点击这里下载】

 

提示:在此游戏场景中按F键可以开关屏幕特效。

着色器编译多样化算是Unity5中Shder书写的新特性,标准着色器之所以能独当一面,正是得益于这种特性,在这里先对此特性进行一个简单的说明与讲解。

 

 




一、关于着色器编译多样化


 

此部分参考自Unity5.2.1版官方文档(http://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html),经翻译&理解后而成。如有解释不妥当之处,还请各位及时指出。

Unity5中使用了一种被称为着色器编译多样化(Multiple shader program variants)的新技术,常被称为“megashaders”或“uber shaders”,并通过为每种情况提供不同的预处理指令来让着色器代码多次被编译来实现。

在Unity中,这可以通过#pragmamulti_compile或者#pragma shader_feature指令来在着色器代码段中实现。这种做法对表面着色器也可行。

在运行时,相应的着色器变体是从材质的关键词中取得的(Material.EnableKeyword和 DisableKeyword),或者全局着色器关键词(Shader.EnableKeyword和 DisableKeyword)。



1.1 multi_compile的用法简析


若我们定义如下指令:

#pragma multi_compile FANCY_STUFF_OFFFANCY_STUFF_ON

也就表示定义了两个变体:FANCY_STUFF_OFF和FANCY_STUFF_ON。在运行时,其中的一个将被激活,根据材质或者全局着色器关键词(#ifdef FANCY_STUFF_OFF之类的宏命令也可以)来确定激活哪个。若两个关键词都没有启用,那么将默认使用前一个选项,也就是关闭(OFF)的选项FANCY_STUFF_OFF。

需要注意,也可以存在超过两个关键字的multi_compile编译选项,比如,如下代码将产生4种着色器的变体:

#pragma multi_compile SIMPLE_SHADINGBETTER_SHADING GOOD_SHADING BEST_SHADING

当#pragma multi_compile中存在所有名字都是下划线的一个指定段时,就表示需在没有预处理宏的情况下产生一个空的着色器变种。这种做法在着色器编写中比较常见,因为这样可以在不影响使用的情况下,避免使用两个关键词,这样就节省了一个变量个数的占用(下面会提到,Unity中关键词个数是有129个的数量限制的)。例如,下面的指令将产生两个着色器变体;第一个没有定义,第二个定义为FOO_ON:

#pragma multi_compile __ FOO_ON

这样就省去了一个本来需要定义出来的 FOO_OFF(FOO_OFF没有定义,自然也不能使用),节省了一个关键词个数的占用。

若Shader中有如上定义,则可以使用#ifdef来进行判断:

#ifdef FOO_ON
//代码段1
#endif

根据上面已经定义过的FOO_ON,此#ifdef判断的结果为真,代码段1部分的代码就会被执行到。反之,若#pragma multi_compile __FOO_ON一句代码没有交代出来,那么代码段1部分的代码就不会被执行。

这就是着色器编译多样化的实现方式,其实理解起来很容易,对吧。

 


1.2 shader_feature和multi_compile之间的区别

 

#pragma shader_feature 和#pragma multi_compile非常相似,唯一的区别在于采用了#pragmashader_feature语义的shader,在遇到不被使用的变体的时候,就不会将其编译到游戏中。所以,shader_feature中使得所有的设置到材质中的关键词都是有效的,而multi_compile指令将从全局代码里设置关键词。

另外,shader_feature还有一个仅仅含有一个关键字的快捷表达方式,例如:

#pragma shader_feature FANCY_STUFF


此为#pragma shader_feature _ FANCY_STUFF的一个简写形式,其扩展出了两个着色器变体,第一种变体自然为不定此FANCY_STUFF变量(那么若在稍后的Shader代码中进行#ifdef FANCY_STUFF的判断,则结果为假),第二种变体为定义此FANCY_STUFF变量(此情况下#ifdef FANCY_STUFF的判断结果为真)。



1.3 多个multi_compile连用会造成指数型增长

 

可以提供多个multi_compile流水线,然后着色器的结果可以被编译为几个流水线的排列组合,比如:

#pragma multi_compile A B C
#pragma multi_compile D E

第一行中有3种选项,第二行中有两种选项,那么进行排列组合,总共就会有六种选项(A+D, B+D, C+D, A+E, B+E, C+E)。

容易想到,一般每以个multi_compile流水线,都控制着着色器中某一单一的特性。请注意,着色器总量的增长速度是非常快的。

比如,10条包含两个特性的multi_compil指令,会得到2的10次方,也就是1024种不同的着色器变体。

 


1.4 关于Unity中的关键词限制Keyword limit

 

当使用着色变量时,我们应该记住,Unity中将关键词的数量限制在了128个之内(着色变量算作关键字),且其中有一些已经被Unity内置使用了,因此,我们真正可以自定义使用关键词的数量以及是小于128个的。同时,关键词是在单个Unity项目中全局使用并计数的,所以我们要千万小心,在同一项目中存在的但没用到Shader也要考虑在内,千万不要合起来在数量上超出Unity的关键词数量限制了。

 


1.5 Unity内置的快捷multi_compile指令


如下有Unity内置的几个着色器变体的快捷多编译指令,他们大多是应对Unity中不同的光线,阴影和光照贴图类型。详情见rendering pipeline 

  • multi_compile_fwdbase - 此指令表示,编译正向基础渲染通道(用于正向渲染中,应用环境光照、主方向光照和顶点/球面调和光照(Spherical Harmonic Lighting))所需的所有变体。这些变体用于处理不同的光照贴图类型、主要方向光源的阴影选项的开关与否。
  • multi_compile_fwdadd - 此指令表示, 编译正向附加渲染通道(用于正向渲染中;以每个光照一个通道的方式应用附加的逐像素光照)所需的所有变体。这些变体用于处理光源的类型(方向光源、聚光灯或者点光源&#
  • 18
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值