unity shader 变种(多重编译 multi_compile)

一、定义

在unity中我们可以通过使用#pragma multi_compile或#pragma shader_feature指令来为shader创建多个稍微有点区别的shader变体。这个Shader被称为宏着色器(mega shader)或者超着色器(uber shader)。实现原理:根据不同的情况,使用不同的预处理器指令,来多次编译Shader代码。
在运行时,Unity从Material宏Material.EnableKeyword和Shader.DisableKeyword或全局着色器宏Shader.EnableKeyword和Shader.DisableKeyword中选择适当的着色器变体。如果这两个宏都未启用,则Unity使用第一个宏。

二、使用

//定义两个TEST_1,TEST_2两个宏
#pragma multi_compile TEST_1 TEST_2
//在shader中使用
#ifdef TEST_1//Todo      
#endif
#ifdef TEST_2//Todo      
#endif

上面这个命令会产生2种着色器变种:TEST_1,TEST_2。
要生成未定义预处理器宏的着色器变体,请添加一个仅为下划线(__)的名称。这是避免使用两个宏的常用技术,因为对项目中可以使用的宏数量有限制,例如:

#pragma multi_compile __ TEST_1

在脚本中控制使用:

//使用TEST_1变种
Shader.EnableKeyword ("TEST_1");
Shader.DisableKeyword ("TEST_2");

三、组合

#pragma multi_compile TEST_1 TEST_2
#pragma multi_compile TEST_3 TEST_4 TEST_5

它产生总共六个着色器变体(TEST_1_TEST_3,TEST_1_TEST_4,TEST_1_TEST_5,TEST_2_TEST_3,TEST_2_TEST_4,TEST_2_TEST_5)。
所以如果有10行multi_compile,每行2个选项,那么将一共产生1024个着色器变体。
请记住,着色器变体数量将以这种方式疯狂增长。

四、pragma shader_feature

shader_feature非常相似multi_compile。唯一的区别是Unity shader_feature在最终版本中不包含未使用的着色器变体。所以shader_feature适用于在我们在编辑器中,选中材质,设置它使用的shader的宏,如果在程序中动态的去设置可能无效(原因下面说明)。而对于multi_compile,会把所有的变体都编译进程序里,所以适合需要在程序运行中动态改变状态的宏,适合全局设置 。
材质中设置位置截图:

五、宏限制

在unity中限制了全局的宏个数为265个,而unity内部使用了大约60个,所以在多个不同的着色器中定义全局宏时需要注意宏数量不要超过限制。
使用本地宏替代一部分全局宏:使用shader_feature_local和multi_compile_local。
shader_feature_local:类似于shader_feature,但枚举宏是本地的。
multi_compile_local:类似于multi_compile,但枚举宏是本地的。
在项目中除非是希望通过全局API启用的那些特定宏,否则应尽量使用本地宏,
使用更多本地宏和更少的全局宏,以减少每个着色器的宏总计数。如果存在具有相同名称的全局和本地宏,则Unity会优先使用local宏。
注意:
(1)不能将本地宏与进行全局宏更改的API一起使用(例如Shader.EnableKeyword或CommandBuffer.EnableShaderKeyword)。
(2)每个着色器最多有64个唯一的本地宏。
(3)如果Material启用了本地宏,并且其着色器更改为不再声明的宏,则Unity会创建一个新的全局宏。

六、内置multi_compile快捷方式

unity中提供一些内置的宏用于编译多个着色器变体。这些主要用于处理Unity中不同的光照,阴影和光照贴图类型。
multi_compile_fwdbase:编译PassType.ForwardBase所需的所有变体。变体处理不同的光照贴图类型,并启用或禁用主方向光的阴影。
multi_compile_fwdadd:为PassType.ForwardAdd编译变体。这将编译变体以处理Directional,Spot或Point Light类型及其变体与Cookie纹理。
multi_compile_fwdadd_fullshadows:同样multi_compile_fwdadd,但也包括灯具有实时阴影的能力。
multi_compile_fog:扩展为多个变体以处理不同的雾类型(off / linear / exp / exp2)。

大多数内置快捷方式都会产生许多着色器变体。如果您知道项目不需要它们,您可以使用#pragma skip_variants跳过编译它们中的一些。例如:

#pragma multi_compile_fwdadd
#pragma skip_variants POINT POINT_COOKIE

该指令跳过包含POINT或POINT_COOKIE的所有变体。

七、查看shader变种数量

#pragma multi_compile TEST_1 TEST_2 TEST_3
#pragma multi_compile TEST_4 TEST_5
#pragma multi_compile TEST_6 TEST_7

查看变体数量.png

 

上面的组合会产生3x2x2=12种变体,我们可以点击show查看具体的变体组合名称。

// Total snippets: 1
// -----------------------------------------
// Snippet #0 platforms ffffffff:
Keywords always included into build: TEST_1 TEST_2 TEST_3 TEST_4 TEST_5 TEST_6 TEST_712 keyword variants used in scene:TEST_1 TEST_4 TEST_6
TEST_1 TEST_4 TEST_7
TEST_1 TEST_5 TEST_6
TEST_1 TEST_5 TEST_7
TEST_2 TEST_4 TEST_6
TEST_2 TEST_4 TEST_7
TEST_2 TEST_5 TEST_6
TEST_2 TEST_5 TEST_7
TEST_3 TEST_4 TEST_6
TEST_3 TEST_4 TEST_7
TEST_3 TEST_5 TEST_6
TEST_3 TEST_5 TEST_7

这里查看的是所有会被编译的变体的数量,也就是#pragma multi_compile声明的宏的全部组合。

#pragma multi_compile TEST_1 TEST_2 TEST_3
#pragma multi_compile TEST_4 TEST_5
#pragma shader_feature TEST_6 TEST_7

查看变种数量.png

// Total snippets: 1
// -----------------------------------------
// Snippet #0 platforms ffffffff:
Keywords stripped away when not used: TEST_6 TEST_7
Keywords always included into build: TEST_1 TEST_2 TEST_3 TEST_4 TEST_56 keyword variants used in scene:TEST_1 TEST_4 TEST_6
TEST_1 TEST_5 TEST_6
TEST_2 TEST_4 TEST_6
TEST_2 TEST_5 TEST_6
TEST_3 TEST_4 TEST_6
TEST_3 TEST_5 TEST_6

上面的组合会产生3x2x1=6种变体,#pragma shader_feature没有特别处理的话只有会默认包括第一个宏。

八、编译

(1)material的ShaderKeywords

Material所包含的Shader Keywords表示启用shader中对应的宏,Unity会调用当前宏组合所对应的变体来为Material进行渲染。在Editor下,可以通过将material的inspector调成Debug模式来查看当前material定义的Keywords,也可在此模式下直接定义Keywords,用空格分隔Keyword。

 

设置ShaderKeywords.png

 

优点:根据material中的ShaderKeywords自动生成变体。无需额外设置
缺点:多个不同的material包中可能存在相同的shader变体,造成资源冗余。若在程序运行时动态改变material的keyword其变体可能并没有被生成
如上图设置:如果ShaderKeywords中没有设置TEST_6,这是如果我们想在程序中通过代码动态使用TEST_6这个宏(Shader.EnableKeyword("TEST_6"))。可能不能得到想要的效果,因为TEST_6这个变种没有生成。

(2)把Shader加入到Always Include Shaders列表里

找到ProjectSetting->Graphics->Always Include Shaders列表,将我们需要的shader添加到里面,这样unity将会把这个shader的所有的变种都生成出来。

 

AlwaysIncludeShaders.png

优点:我们不用担心项目发布出去以后有些变种没有生成,不能在程序中动态的去控制我们的宏。
缺点:生成的变体数量庞大,导致发布时间变长,游戏包体过大。比如你把standardShader放进去,由于它有大量的keyword,全部变种都生成的话大概有几百兆。

(3)使用ShaderVariantCollection是生成指定变体

ShaderVariantCollection是unity5.x以后用来记录shader的哪些变体需要被生成。这样做的好处就是在shader_feature与multi_compile结合使用时,能够设置生成何种变体,从而避免生成不必要的变体;shader不必和material打在一个包中,避免了多个包中存在相同的变体资源;明确直观的显示了哪些变体是需要生成的。

生成方式:

(1)通过Create->Shader-> Shader Variant Collection,就可以新建一个shader variant collection文件,手动添加需要编译的变种

 

ShaderVariantCollection.png

 

选择需要生成的变种.png

 

(2)通过Edit->Project Settings->Graphics中的save to asst...按钮,生成unity帮我们自动收集的,使用到的变种信息。

 

自动生成.png

这时候只需要先Clear一下,然后依次打开我们的所有场景,把需要的物体都显示一遍,Unity就会自动记录下来哪些着色器的哪些着色器变体已经被使用到。统计完后只需点击下面的保存按钮就可以生成我们所需要的ShaderVariantCollection资源。当然你也可以为你的每一个场景或者按需生成足够多的ShaderVariantCollection资源。自动收集的功能不一定百分百可靠,最好事后多检查。

ShaderVariantCollection加载:

启动时预加载:

 

最简单最粗暴的使用方式就是在游戏启动的瞬间就直接加载ShaderVariantCollection资源并编译里面的着色器变体,Unity已经为我们做好这一步了,依然还是在图形设置面板里,只需把需要启动是就编译的ShaderVariantCollection添加在Preloaded Shaders里面

预加载.png


代码加载:

由于ShaderVariantCollection也是一种资源,可以跟纹理、模型等等资源一起打包和加载等,只需在加载之后调用一句WarmUp。

ShaderVariantCollection shaderVariantCollection = Resources.Load <ShaderVariantCollection>( "MainShaderVariant");
if (shaderVariantCollection )shaderVariantCollection.WarmUp ();

也可以把ShaderVariantCollection放在Resources目录下,好像会被自动加载。



 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246231.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AndroidStudio导出aar文件给Unity使用

AndroidStudio导出aar文件给Unity使用 本文参考 &#xff1a;http://www.devacg.com/?post548 Demo地址&#xff1a;https://github.com/JulyNine/AndroidToUnity 一、用Android Studio创建个空工程 注意&#xff1a;包名要与Unity中工程的包名不一致&#xff0c;不然打包时…

Unity C# Job System介绍(四) 并行化Job和故障排除(完结)

并行化job ParallelFor jobs​docs.unity3d.com 当调度Jobs时&#xff0c;只能有一个job来进行一项任务。在游戏中&#xff0c;非常常见的情况是在一个庞大数量的对象上执行一个相同的操作。这里有一个独立的job类型叫做IJobParallelFor来处理此类问题。ParallelFor jobs当调…

C# Job System

概述 设计目的&#xff1a;简单安全地使用多线程&#xff0c;随便就能写出高性能代码 收益&#xff1a;FPS更高&#xff0c;电池消耗更低&#xff08;Burst编译器&#xff09; 并行性&#xff1a;C# Job System和Unity Native Job System共享工作线程worker threads&#xf…

Unity游戏开发——C#特性Attribute与自动化

这篇文章主要讲一下C#里面Attribute的使用方法及其可能的应用场景。 比如你把玩家的血量、攻击、防御等属性写到枚举里面。然后界面可能有很多地方要根据这个枚举获取属性的描述文本。 比如你做网络框架的时候&#xff0c;一个协议号对应一个类的处理或者一个方法。 比如你做…

Unity c#中Attribute用法详解

举两个例子&#xff0c;在变量上使用[SerializeFiled]属性&#xff0c;可以强制让变量进行序列化&#xff0c;可以在Unity的Editor上进行赋值。 在Class上使用[RequireComponent]属性&#xff0c;就会在Class的GameObject上自动追加所需的Component。 以下是Unity官网文档中找…

走进LWRP(Universal RP)的世界

走进LWRP&#xff08;Universal RP&#xff09;的世界 原文&#xff1a;https://connect.unity.com/p/zou-jin-lwrp-universal-rp-de-shi-jie LWRP自Unity2018发布以来&#xff0c;进入大家视野已经有一段时间了&#xff0c;不过对于广大Unity开发者来说&#xff0c;依然相对…

Unity 2017 Game Optimization 读书笔记(1)Scripting Strategies Part 1

1.Obtain Components using the fastest method Unity有多种Getcomponet的方法&#xff1a; GetComponent(string), GetComponent<T>() GetComponent(typeof(T)) 哪种效率最高会跟随Unity版本的变化而变化&#xff0c;对于Unity 2017&#xff0c;本书作者的测试是Ge…

C# 多态相关的文章

一 C# 多态的实现 封装、继承、多态&#xff0c;面向对象的三大特性&#xff0c;前两项理解相对容易&#xff0c;但要理解多态&#xff0c;特别是深入的了解&#xff0c;对于初学者而言可能就会有一定困难了。我一直认为学习OO的最好方法就是结合实践&#xff0c;封装、继承在…

C++ 虚函数和虚表

几篇写的不错的文章&#xff0c;本文是整合了这几篇文章&#xff0c;感谢这些大佬 https://www.jianshu.com/p/00dc0d939119 https://www.cnblogs.com/hushpa/p/5707475.html https://www.jianshu.com/p/91227e99dfd7 多态: 多态是面相对象语言一个重要的特性,多态即让同一…

Unity 2017 Game Optimization 读书笔记(2)Scripting Strategies Part 2

1. Share calculation output 和上一个Tip很像&#xff0c;可以缓存计算结果或者各种信息&#xff0c;避免多次重复的计算&#xff0c;例如在场景里查找一个物体&#xff0c;从文件读取数据&#xff0c;解析Json等等。 容易忽略的点是常常在基类了实现了某个方法&#xff0c;在…

Unity 2017 Game Optimization 读书笔记(3)Scripting Strategies Part 3

1.Avoid retrieving string properties from GameObjects 通常来讲&#xff0c;从C#的object中获取string 属性没有额外的内存开销&#xff0c;但是从Unity中的Gameobject获取string属性不一样&#xff0c;这会产生上一篇讲到的 Native-Managed Bridge&#xff08;Native内存和…

Unity 2017 Game Optimization 读书笔记(4)Scripting Strategies Part 4

1.Avoid Find() and SendMessage() at runtime SendMessage() 方法和 GameObject.Find() 相关的一系列方法都是开销非常大的。SendMessage()函数调用的耗时大约是一个普通函数调用的2000倍&#xff0c;GameObject.Find() 则和场景的复杂度相关&#xff0c;场景越复杂&#xff0…

Unity HDRP中的光照烘焙测试(Mixed Lighing )和间接光

部分内容摘抄自&#xff1a;https://www.cnblogs.com/murongxiaopifu/p/8553367.html 直接光和间接光 大家都知道在Unity中&#xff0c;我们可以在场景中布置方向光、点光、聚光等类型的光源。但如果只有这些光&#xff0c;则场景内只会受到直接光的影响&#xff0c;而所谓的…

聊聊Unity项目管理的那些事:Git-flow和Unity

感谢原作者https://www.cnblogs.com/murongxiaopifu/p/6086849.html 0x00 前言 目前所在的团队实行敏捷开发已经有了一段时间了。敏捷开发中重要的一个话题便是如何对项目进行恰当的版本管理。项目从最初使用svn到之后的Git One Track策略再到现在的GitFlow策略&#xff0c;中…

聊聊网络游戏同步那点事

写的非常好的一篇博文&#xff0c;转载自https://www.cnblogs.com/murongxiaopifu/p/6376234.html 0x00 前言 16年年底的时候我从当时的公司离职&#xff0c;来到了目前任职的一家更专注于游戏开发的公司。接手的是一个platform游戏项目&#xff0c;基本情况是之前的团队完成…

Unity 2017 Game Optimization 读书笔记 Dynamic Graphics(1)

The Rendering Pipeline 渲染表现差有可能取决于CPU端&#xff08;CPU Bound&#xff09;也有可能取决于GPU(GPU Bound).调查CPU-bound的问题相对简单&#xff0c;因为CPU端的工作就是从硬盘或者内存中加载数据并且调用图形APU指令。想找到GPU-bound的原因会困难很多&#xff…

Unity 2017 Game Optimization 读书笔记 Dynamic Graphics(2)

Lighting and Shadowing 现代的游戏中&#xff0c;基本没有物体能在一步就完成渲染&#xff0c;这是因为有光照和阴影的关系。光照和阴影的渲染在Fragment Shader中需要额外的pass。 首先要设置场景中的Shadow Casters和Shadow Receivers&#xff0c;Shadow Casters投射阴影&…

Unity 2017 Game Optimization 读书笔记 The Benefits of Batching

batching&#xff08;合批&#xff09; 和大量的描述一个3D物体的数据有关系&#xff0c;比如meshes&#xff0c;verices&#xff0c;edges&#xff0c;UV coordinates 以及其他不同类型的数据。在Unity中谈论batching&#xff0c;指的是用于合批mesh数据的两个东西&#xff1a…

Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (3)

Rendering performance enhancements Enable/Disable GPU Skinning 开启GPU Skinning可以减轻CPU或GPU中Front End部分中某一个的负担&#xff0c;但是会加重另一个的负担。Skinning是mesh中的顶点根据动画中骨骼的当前位置进行计算&#xff0c;从而让角色摆出正确的姿势。 …

Unity手游开发札记——布料系统原理浅析和在Unity手游中的应用

原文&#xff1a;https://zhuanlan.zhihu.com/p/28644618 0. 前言 项目技术测试结束之后&#xff0c;各种美术效果提升的需求逐渐成为后续开发的重点&#xff0c;角色效果部分的提升目标之一便是在角色选择/展示界面为玩家提供更高的品质感&#xff0c;于是可以提供动态效果的…