ShaderVariant variant

What is ShaderVariant

  When writing a shader, multiple macros are often defined in the shader, and the rendering process of the object when the macro is turned on or off is controlled in the shader code. In the final compilation, the shader source code in various combinations is also compiled and generated according to these different macros. To put it bluntly, it is similar to if. Each of these combinations is a variant of the shader (Variant).

Material ShaderKeywords与ShaderVariant

  The Shader Keywords contained in Material indicate that the corresponding macro in the shader is enabled, and Unity will call the variant corresponding to the current macro combination to render for Material.

  In the Editor, you can view the keywords defined by the current material by switching the inspector of the material to Debug mode, or you can directly define the keywords in this mode, and separate the keywords with spaces. as shown in the picture


Figure 1: Setting keywords

  In the program, you can use Material.EnableKeyword(), Material.DisableKeyword(), Shader.EnableKeyword(), Shader.DisableKeyword() to enable/disable the corresponding macro.

  The Enable function should correspond to the Disable function. If a macro is turned on by Material.EnableKeyword(), it should be turned off by Material.DisableKeyword(). Shader.DisableKeyword() cannot turn off this macro. The Keywords defined in Material are set by the function of Material.

multi_compile与shader_feature

  multi_compile and shader_feature can define macros in the shader. The difference between the two is shown in the figure below:

multi_compileshader_feature definition method #pragma multi_compile A#pragma shader_feature A macro scope of application All Shader All Shader variants are generated All variants can be customized which variants are generated The default defined macro is the first macro defined by default The first macro is defined by default (the default is nokeyword when there is only one macro definition)

1. How to define

  It is worth noting in the definition method that #pragma shader_feature A is actually an abbreviation of #pragma shader_feature _ A, and the underscore indicates that the macro (nokeyword) is not defined. Therefore, the shader actually corresponds to two variants at this time, one is nokeyword, and the other defines macro A.

  And #pragma multi_compile A does not have a shorthand, so the shader only corresponds to the variant of A at this time. To indicate that no variant is defined, it should be written as #pragma multi_compile__A.

2. Scope of application of macros

  The two definition methods can be used in any shader, but each has some suggested usage.

  The macros defined by multi_compile, such as #pragma multi_compile_fog, #pragma multi_compile_fwdbase, etc., are basically applicable to most shaders, regardless of the properties of the shader itself.

  The macros defined by shader_feature are mostly used for the properties of the shader itself. For example, there is a property (Property) of _NormalMap in the shader, which can be used to #pragma shader_feature _NormalMapdefine macros to realize that the shader can perform different processing when the material has _NormalMap or not.

3. Generation of variants

  multi_compile will generate all variants by default, so multi_compile should be used with caution, otherwise it will cause the number of variants to explode. Such as: #pragma multi_compile ABC #pragma multi_compile DE

  At this time, 6 variants of AD, AE, BD, BE, CD, and CE will be generated.

  The shader_feature to generate which variant can be customized with the shader variant collection.

4. Macros defined by default

  When the keywords in the material cannot correspond to the variant generated by the shader, Unity will define the first macro in the macro definition statement by default, and run the corresponding variant to render the material.

  Both multi_compile and shader_feature define the first macro by default , as shown in the following table:

macro definition statement macros defined by default
#pragma shader_feature A nokeyword (there is a shorthand problem)
#pragma shader_feature A B C A
#pragma multi_compile A A
#pragma multi_compile A B C A

How to control the generation of Shader variants in the project

  There are three main ways to generate shader variants in the project, and their advantages and disadvantages are shown in the following figure:

Generation method advantage shortcoming
Shader and material are in one package Variants are automatically generated based on the keywords in the material 1. The same shader variant may exist in multiple different material packages, resulting in resource redundancy.
2. If the keyword of the material is dynamically changed when the program is running, the variant may not be generated if the macro defined by shader_feature is used
Shader is packaged separately, using multi_compile to define all macros All variants are generated, no required variants are not generated 1. The number of variants generated is huge, which is a serious waste of resources
Shader is packaged separately, shader_feature (need to use ShaderVariantCollection to generate variants) combined with multi_compile (still generates all variants) Can effectively control the number of shader_feature variants 1. How to determine which variants need to be generated
2. It is easy to miss the variants that need to be generated, especially the variants that need to be dynamically replaced

  The result we want is to control the number of shader variants in the project as much as possible to avoid redundant resources while ensuring the correct rendering effect. Fortunately, Unity已经为我们准备好了解决方案:ShaderVariantCollection.

Solution using shader_feature: ShaderVariantCollection

Introduction to ShaderVariantCollection

  One of the functions of the Shader Variant Collection is to record the variants generated by the macro defined by shader_feature in the shader. It is possible to set which variants to generate, so as to avoid generating unnecessary variants; shader does not have to be packaged with material in the same package, avoiding the existence of the same variant resources in multiple packages; it clearly and intuitively shows which variants are required Generated.

  In Unity, you can create a new shader variant collection file through Create->Shader-> Shader Variant Collection. The use of shader variant collection is shown in Figure 2:


figure 2

  After clicking Add variant, a variant selection window will appear, as shown in Figure 3


image 3

  After configuring the variants that need to be generated, 将collection与shader打在同一个包中,便能准确生成面板中所配置的shader变体.

ShaderVariantCollection generates variant rules defined by shader_feature

  In addition to the variants configured in the collection will be generated, Unity also generates a few more variants for us in the background. These variants are "hidden" and are not displayed in the collection panel.

1. The variant corresponding to the opening of the first macro definition must be generated.

  In Shader, macro A is defined through #pragma shader_feature A, and the variant corresponding to macro A is added to the collection, as shown in Figure 4:


Figure 4

  In addition to the existing ForwardBase A in the collection, the generated variant will also generate the variant ForwardBase nokeyword. Because only a single macro is defined, A is shorthand for _A. In fact, the first defined macro is nokeyword, so the variant corresponding to nokeyword must be generated.

  Similarly, when #pragma shader_feature ABC is used to define the macro, even if the variant Forward A is not added to the collection, this variant must be generated (when the PassType of the shader is only ForwardBase).

2. Generation rules for variants when there are multiple passes in the Shader

  a. Read the existing variants in ShaderVariantCollection and get their Keywords.

  b. Intersect these keywords with multiple sets of keywords lists for each pass, and take the set with the largest number of keywords in the intersection set.

  c. Use the obtained Keywords and the corresponding PassType to generate a ShaderVariant, and add it to the ShaderVariantCollection.

  d. If there are new Keywords in the obtained intersection, return to b. The above process is similar to recursion. For example: Shader has three PassTypes: ForwardBase, ForwardAdd, and Normal (hereinafter referred to as Base, Add, and Normal for convenience). The defined macros are as follows:

Base Add Normal
#pragma shader_feature A
#pragma shader_feature B
#pragma shader_feature C
#pragma shader_feature A
#pragma shader_feature E
#pragma shader_feature A
#pragma shader_feature B
#pragma shader_feature E

  At this time, if the variant contained in the ShaderVariantCollection is Base ABC, Add AE. Then the variants generated at this time are: the variants (3) corresponding to the default defined macros (nokeyword) of these three PassTypes, and the Base ABC and Add AE directly contained in the original. In addition, Unity will additionally generate 6 variants of Add A, Base A, Normal A, Normal AB, Base AB, and Normal AE.

ABC ∩ Add AE -> Add A (A is NewKeyword)
  A ∩ Base ABC -> Base A
  A ∩ Normal ABE -> Normal A
ABC ∩ Normal ABE -> Normal AB (AB is NewKeyword)
 AB ∩ Base ABC -> Base AB
 AE ∩ Normal ABE -> Normal AE

Variant Calling Rules

  After the collection generates the variants accurately, it can call different variants by modifying the keywords in the material at runtime.

  Assuming that there are only three variants generated by a certain collection: Forward ABC, Forward ABE, and Forward nokeyword, the calling relationship at this time is as follows:

Keywords in Material variant of calling explain
A B C Forward A B C normal match
A B Forward nokeyword No matching variant, call the variant corresponding to the first defined macro
A B C D Forward A B C Call the variant
ABCD with a large number of keywords in the intersection ∩ ABC = ABC
ABCD ∩ ABE = AB
A B C E Forward A B C The number of keywords in the intersection is the same, whoever is the first in the collection will be called
A B E C Forward A B C Regardless of the order of definition in the material


Figure 5
以上均为根据测试结果总结归纳出来的规则,若有错误之处还请严加指正!

Specific implementation of shader_feature project

Addition of variants in the project

  So how does the project determine which variants need to be added to the collection? Our approach is:

  a. Traverse each Material and extract its shader keywords.

  b. Intersect the obtained keywords with the macro definitions contained in each PassType of the shader, and add the result to the collection.

  To give a simple example:

  The Keywords in Material are ABCD, then the PassType of the shader, the macros defined in PassType, and the variants that need to be added to the collection are shown in the following table:

PassType defined macro The variant that needs to be added to the collection
ForwardBase #pragma shader_feature A
#pragma shader_feature B
Forward A B
(ABCD ∩ AB = AB)
ForwardAdd #pragma shader_feature _ C D Add C
(ABCD ∩ C = C, ABCD ∩ D = D, but C is defined before D, so only add C)
Normal #pragma shader_feature _ E F Normal NoKeyword
(ABCD ∩ E = NoKeyword)
(ABCD ∩ F = NoKeyword)

  It should be noted that in our own code, in order to reduce the complexity of the variant generation logic and keep the variants on the collection panel intuitive, we do not add the additional variants generated by Unity to the collection panel, but Remember that Unity will generate additional variants for us.

Reproduced address: Shader variant collection and packaging - Zhihu Shader variant collection and packaging Author: Xiaoming Shader variant collection and packaging basic knowledge What is ShaderVariantMaterial ShaderKeywords and ShaderVariantmulti_compile and shader_feature1. Definition method2. The scope of application of macros3. Variant Generation 4. Default defined... https://zhuanlan.zhihu.com/p/68888831

Guess you like

Origin blog.csdn.net/Ling_SevoL_Y/article/details/129995966