Shader 基础之 Unity Shader概念

目录

目录

Shader compilation

Conditionals in shaders

Different types of conditionals

Switch code branch at runtime

Branching in shaders

Static branching

How to use static branching

Dynamic branching

How to use dynamic branching

Shader variants

Deduplication of shader variants

Check how many shader variants you have

获取editor下使用的变体数量

获取build时的变体数量

Shader keywords

Definition type: “multi compile” or “shader feature”

Local or global scope

Stage-specific keywords

Create a shader variant for disabled keywords

Using shader keywords with C# scripts

Local Shader Keywords:

Global Shader Keywords: 

How Unity loads and uses shaders

Prewarming shader variants

Profiler 中对shader loading 的标记:

Shader compilation

1. 编译分为两种:Editor、Runtime

Editor:

  • 预编译,编译好的会缓存到Library/ShaderCache 下,下次用到该变体的时候,直接重缓存里取,删除Library文件夹,会重新编译。
  • 编译在Editor 下是异步的,即在后台编译,在完成之前,物体呈cyan色;
  • Unity 编辑器右下角可以看到编译的进度,在preference ->Editor 下可以开启/关闭是否异步编译,一般CPU的一个核对应一个编译job

Runtime:

  • 在build的时候,如果使用shader_feature 来定义变体,会剔除没有用到的变体,然后编译所有用到的变体,放到包体里面

2. 不同的平台,Shader编译器会有所不同

3. 一个shader包含在多个material里面,打进assetbundle中,会导致有多份shader,同时也打断了合批,可以使用 Asset Bundle Browser 来检查bundle的依赖

Conditionals in shaders

  • 有时候,想要相同的着色器在不同的情况下做不同的事情。
  • 例如,为不同的materail配置不同的设置,为不同的硬件定义功能,或者在运行时dynamic改变着色器的行为。可能还希望避免在不需要时执行计算开销较大的代码,例如纹理读取、顶点输入、插值器或循环。
  • 使用条件来定义GPU只在特定条件下执行的行为。

Different types of conditionals

shader 使用条件的方法有下面三种:

  • Static branching: the shader compiler evaluates conditional code at compile time.Use preprocesser constants and macros.
  • Dynamic branching: the GPU evaluates conditional code at runtime.
  • Shader variants
    : Unity uses static branching to compile the shader source code into multiple shader programs. Unity then uses the shader program that matches the conditions at runtime.

使用 shader variants 这种方法,build的时候会剔除未使用的变体,所以这种情况,应该避免在c#中enable/disable  shader_feature keywords,因为有可能启用的变体,没有打到包里,如果该变体没有,则会选择一个默认的使用

如果需要在c#中enable/disable shader_feature keywords,则应该让所有的shader变体都包含在包体里面,采用下面两种方法:

  • 创建一个 shader variant collection 资源,包所有的变体,添加进去
  • Include a Material in your build for every combination of shader_feature keywords you want to use.

Switch code branch at runtime

在c#代码中切换shader的分支,有两种方法:

  • 在shader中声明multi_compile 关键字,缺点是编译的变体非常多,占内存
  • 使用dynamic branch,缺点是耗费GPU
#pragma shader_feature REFLECTION_TYPE1 REFLECTION_TYPE2 REFLECTION_TYPE3
Shader directiveBranching typeShader variants Unity creates
shader_featureStatic branching

Variants for keyword combinations you enable at build time

(只是enable的shader_feature的组合)

multi_compileStatic branchingVariants for every possible combination of keywords
dynamic_branchDynamic branchingNo variants

Branching in shaders

根据shader 执行branch 的方式,分为两种:

Static branching

在运行之前就已经编译好的叫静态branch

  • 优点:由于构建的时候,会剔除掉未使用的branch,所以构建的包体小,时间短,对性能没影响
  • 缺点:因为剔除的原因,一些branch 不能在runtime 中使用

How to use static branching

三种方法:

  • 手写的 shaders:
    • Use #if, #elif, #else, and #endif preprocessor directives, or #ifdef and #ifndef preprocessor directives to create static branches.
    • Use an if statement that evaluates a compile-time constant value. Although if statements can also be used for dynamic branches, the compiler detects the compile-time constant value and creates a static branch instead.
    • Unity provides built-in macros for some compile-time constants that you can use with static branches.

Note: Static branching is available only in hand-coded shaders. You cannot create static branches in Shader Graph.

Dynamic branching

它又分为两种:

  • 基于uniform variables
  • 基于任何其他other runtime value

基于uniform variables通常更有效,因为uniform variables对于整个drawcall是恒定的

  • 优点:可以实时的根据动态的值,来采取不同的策略
  • 缺点:耗费GPU,因为GPU执行过程是一个单元一个单元并行的,non-uniform variables会打断这个并行,因为有的单元去处理另一个分支,有的去处理相反的分支,如果一个处理完,另一个就会等待另一个,uniform variables的值,是所有的单元都去处理其中一个分支,性能会相对好

Note:所有的分支都会编译成程序,写进shader里面,这会增加文件的大小,但是相比较多个变体来说,它还是小的

How to use dynamic branching

You can use dynamic branching in your shaders in the following ways:

  • In hand-coded shaders, use an if statement that evaluates runtime state. You can use attributes to force the GPU to execute both branches, or to execute only one branch.
  • In Shader Graph, use a Branch Node. This always executes both branches.

Shader variants

使用 shader keywords.来配置variants,构建的时候,每一个变体,都是一段shader 代码

  • 优点:比dynamic branch 耗费更低的GPU性能
  • 缺点:多了,会增加内存,和加载时间

Deduplication of shader variants

  • After compilation, Unity 自动识别相同通道内的相同变体,并确保这些相同的变体指向相同的字节码. This is called deduplication.
  • Deduplication 防止同一pass中的相同变体增加文件大小,但是也会增加编译的时间,需要踢除不必要的变体 strip unneeded variants.

Check how many shader variants you have

获取editor下使用的变体数量

Select Save to asset… to create a shader variant collection asset.

获取build时的变体数量

打开Editor.log 文件(menu: Window > General > Console and select Open Editor Log from the Console window menu.),搜索 Compiling shader

Shader keywords

通过shader keywords 的方法,可以在shader 里面使用条件语句

一个集合的keywords 叫作Keyword,里面的单个keyword叫作state

Definition type: “multi compile” or “shader feature”

shader 中声明keywords的type有两种:

1.multi compile:这里面声明的任何keyword 都会编译成变体

2.shader feature:只有enable的keyword才会编译成变体打进包里,其它的都被裁剪了

Note: 如果把shader添加projectsetting->graphics-> Always Included Shaders 里面,unity 会把它里面的所有keyword都包含在包体里面,即使type 是 shader_feature.

Local or global scope

默认声明的keyword 都是global的,但是可以在声明keyword type的时候,加上后缀_local 来表示是local的,加上之后表示不可override的,也就是同名的不能复写它

Stage-specific keywords

默认,Unity为每个stage声明相同的变体,比如, shader中包含 vertex stage and a fragment stage, Unity 会为它们声明同样的变体,假如只在一个stage中用到了keywords,就会导致变体在另一个stage中是重复的,Unity 会自动识别,并裁剪它们,所以不会增加包体大小, but they still result in wasted compilation time, increased shader loading times, and increased runtime memory usage.

为了避免这个问题,在声明keywords type的时候,加上特定的后缀,表示是哪个stage用的

比如:

  • _vertex
  • _fragment
  • _hull
  • _domain
  • _geometry
  • _raytracing

#pragma shader_feature_fragment RED GREEN BLUE--表明是在fragment stage用的

Create a shader variant for disabled keywords

如果用 shader_feature 创建 single keyword, 当该keyword disable 的时候Unity 会自动创建second variant. This helps reduce the number of keywords you need to enable and disable. For example, the following code creates 2 variants:

#pragma shader_feature EXAMPLE_ON

如果使用 multi_compile/ shader_feature 创建 two or more keywords, 使用 _ ,当该集合中的所有关键字被禁用时,创建一个着色器变体

#pragma multi_compile _ EXAMPLE_ON
#pragma shader_feature _ RED GREEN BLUE WHITE

Using shader keywords with C# scripts

  • c#中shader keyword的概念分为两种:Local Shader Keywords、Global Shader Keywords,这是在c#中的概念,在shader中,local 和global 的区别在于,local 需要在type后面加后缀_local 来声明其是local的,否则是global的。
  • c# 中shader 的keywords存储在  LocalKeywordSpace 结构体内,通过Shader.keywordSpace,compute shader中通过ComputeShader-keywordSpace.来访问
  • local 和 global通过属性  isOverridable 表明,为true 说明是global,false 是local,global不用主动声明,不是local 就是global

Local Shader Keywords:

Global Shader Keywords: 

在运行时,切换不同的变体,容易对性能造成影响,如果该变体之前没有加载过,且是复杂的变体,容易造成画面卡顿的情况。如果使用global keywords,同时修改多个shader,也容易造成同样的问题

How Unity loads and uses shaders

  • Unity 在应用内加载编译好的变体
  • 加载场景或者assetbundle(resource 内的资源)的时候,会加载该场景或资源用到的所有变体到CPU内,然后再解压到CPU的另一块内存的地方,所占内存的大小 可以在 Player settings->Other Settings > Shader Variant Loading  内更改
  • 当第一次用到一个变体的时候,CPU会把资源数据发送给GPU,而不是一次性把所有变体都放到GPU,GPU会对相应的变体,生成一个GPU-specific version of the shader variant,缓存下来,下次再用就不会重新发送了
  • 如果该变体在场景内没有任何引用,Unity 会从CPU、GPU移除掉它

Prewarming shader variants

为了避免使用时,加载出停滞的情况,可以采用预加载的方式:

也可以在projectsetting->graphics  里面配置Preloaded shaders section of the Graphics Settings window. Unity uses the ShaderVariantCollection.WarmUp API to load and prewarm the shader variant collections when your built application starts.

Profiler 中对shader loading 的标记:

  • Shader.ParseThreaded、 Shader.ParseMainThread for Unity loading the shader object from serialized data.
  • Shader.CreateGPUProgram for Unity creating a GPU-specific version of a shader variant
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Unity中,Shader是用来定义物体表面的着色和渲染效果的一种程序。在Unity5.2及以上的版本中,Unity提供了四种Shader模板供我们选择。其中,Standard Surface Shader会产生一个包含了标准光照模型的表面着色器模板;Unity Shader会产生一个不包含光照(但包含雾效)的基本顶点/片元着色器;Image Effect Shader为我们实现各种屏幕后处理效果提供一个基本模板;Computer Shader则产生一种特殊的Shader文件,用于进行一些与常规渲染流水线无关的计算。一个单独的Unity Shader不能单独发挥作用,必须要和材质结合起来使用。 每一个Unity Shader文件可以包含多个SubShader语义块,但最少要有一个。当Unity需要加载一个Unity Shader时,会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持的话,Unity会使用Fallback语义指定的Unity Shader。这是因为不同的显卡具有不同的能力,一些旧的显卡仅能支持一定数目的操作指令,而一些更高级的显卡可以支持更多的指令数。因此,我们希望在旧的显卡上使用计算复杂度较低的着色器,而在高级的显卡上使用计算复杂度较高的着色器,以提供更出色的画面效果。 SubShader的标签是一个键值对,它的键和值都是字符串类型。这些键值对是SubShader和渲染引擎之间的沟通桥梁,用来告诉Unity的渲染引擎应该以怎样的方式和何时渲染这个对象。具体情况可以查看相关的表格。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TO_ZRG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值