您当前的位置: 首页 > 

Peter_Gao_

暂无认证

  • 0浏览

    0关注

    621博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

自定义材质面板

Peter_Gao_ 发布时间:2020-09-24 15:33:19 ,浏览量:0

之前遇到过一些朋友问怎么在材质面板里定义类似于bool这种变量,控制一些代码的执行。我们当然可以写一个C#文件来自定义材质面板,就像Unity为Standard Shader写材质面板一样(你可以在built-in shader包里找到这些文件),但这样有时候太麻烦了。实际上,有一种更简单的方法,不需要写额外的C#文件就可以直接在shader里定义类似bool、enum这种简单的变量,Unity为这些变量提供了内置的材质面板。而这就是通过MaterialPropertyDrawer(当然我们也可以重写内置的MaterialPropertyDrawer,但太麻烦我就不写了)。

示例

总体来说,MaterialPropertyDrawers内置了4种最常见的面板:ToggleDrawer,EnumDrawer,KeywordEnumDrawer,PowerSliderDrawer。

类别描述ToggleDrawer把一个类型为float的属性显示为一个开关,它的值要么是0要么是1。当选中它时,Unity还会设置一个名为大写属性名_ON(可以自定义名字)的shader feature(需要在shader里使用”#pragma shader_feature”来定义它),我们可以在shader里用过#if、#ifdef或者#if defined关键词来判断它当前是否被开启。EnumDrawer把一个类型为float的属性显示为一个下拉列表。可以使用UnityEngine.Rendering命名空间下的各种状态来设置对应的渲染状态,例如ZWrite、ZTest、Blend等。据实验推测,这个值不可以随便定义,老老实实用UnityEngine.Rendering。KeywordEnum和EnumDrawer类似,也会把一个类型为float的属性显示为一个下拉列表,但不同的是它会定义一组shader keyword(需要在shader里使用”#pragma multi_compile”来定义它们)。这些keyword的名字是大写属性名_枚举名。同样,我们可以在shader里用过#if、#ifdef或者#if defined配合#elif来判断当前选择是哪个keyword。最多同时支持9个keywords。PowerSliderDrawer显示一个非线性响应的滑动条,其中PowerSliderDrawer中的参数指定了底数,然后我们再根据Range()来指定范围。其实简单说来就是滑动条上的数值不再是均匀变化了,而是进行变化,但我们在shader里还是可以直接访问到一个float数值。

下面的代码没有任何实际意义,只是为了演示四种面板:

 
  1.  
    Shader "Custom/Material Property Drawer Example"
  2.  
    {
  3.  
    Properties
  4.  
    {
  5.  
    // Header creates a header text before the shader property.
  6.  
    [Header(Material Property Drawer Example)]
  7.  
    // Space creates vertical space before the shader property.
  8.  
    [Space]
  9.  
     
  10.  
    _MainTex ("Main Tex", 2D) = "white" {}
  11.  
    _SecondTex ("Second Tex", 2D) = "white" {}
  12.  
     
  13.  
    // Large amount of space
  14.  
    [Space(50)]
  15.  
     
  16.  
    // Toggle displays a **float** as a toggle.
  17.  
    // The property value will be 0 or 1, depending on the toggle state.
  18.  
    // When it is on, a shader keyword with the uppercase property name +"_ON" will be set,
  19.  
    // or an explicitly specified shader keyword.
  20.  
    [Toggle] _Invert ("Invert color?", Float) = 0
  21.  
     
  22.  
    // Will set "ENABLE_FANCY" shader keyword when set
  23.  
    [Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
  24.  
     
  25.  
    // Enum displays a popup menu for a **float** property.
  26.  
    // You can supply either an enum type name
  27.  
    // (preferably fully qualified with namespaces, in case there are multiple types),
  28.  
    // or explicit name/value pairs to display.
  29.  
    // Up to **7** name/value pairs can be specified
  30.  
    [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend Mode", Float) = 1
  31.  
    [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend Mode", Float) = 1
  32.  
    [Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
  33.  
    [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 0
  34.  
    [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1
  35.  
     
  36.  
    // KeywordEnum displays a popup menu for a **float** property, and enables corresponding shader keyword.
  37.  
    // This is used with "#pragma multi_compile" in shaders, to enable or disable parts of shader code.
  38.  
    // Each name will enable "property name" + underscore + "enum name", uppercased, shader keyword.
  39.  
    // Up to **9** names can be provided.
  40.  
    [KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
  41.  
     
  42.  
    // PowerSlider displays a slider with a non-linear response for a Range shader property.
  43.  
    // A slider with 3.0 response curve
  44.  
    [PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08
  45.  
    }
  46.  
    SubShader
  47.  
    {
  48.  
    Tags { "Queue"="Transparent" "RenderType"="Transparent" }
  49.  
    Blend [_SrcBlend] [_DstBlend]
  50.  
    ZWrite [_ZWrite]
  51.  
    ZTest [_ZTest]
  52.  
    Cull [_Cull]
  53.  
     
  54.  
    Pass
  55.  
    {
  56.  
    CGPROGRAM
  57.  
    // Need to define _INVERT_ON shader keyword
  58.  
    #pragma shader_feature _INVERT_ON
  59.  
    // Need to define _INVERT_ON shader keyword
  60.  
    #pragma shader_feature ENABLE_FANCY
  61.  
    // No comma between features
  62.  
    #pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
  63.  
     
  64.  
    #pragma vertex vert
  65.  
    #pragma fragment frag
  66.  
     
  67.  
    #include "UnityCG.cginc"
  68.  
     
  69.  
    sampler2D _MainTex;
  70.  
    float4 _MainTex_ST;
  71.  
    sampler2D _SecondTex;
  72.  
    float4 _SecondTex_ST;
  73.  
    float _Shininess;
  74.  
     
  75.  
    struct appdata
  76.  
    {
  77.  
    float4 vertex : POSITION;
  78.  
    float2 uv : TEXCOORD0;
  79.  
    };
  80.  
     
  81.  
    struct v2f
  82.  
    {
  83.  
    float4 uv : TEXCOORD0;
  84.  
    float4 vertex : SV_POSITION;
  85.  
    };
  86.  
     
  87.  
    v2f vert (appdata v)
  88.  
    {
  89.  
    v2f o;
  90.  
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
  91.  
    o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
  92.  
    o.uv.zw = TRANSFORM_TEX(v.uv, _SecondTex);
  93.  
    return o;
  94.  
    }
  95.  
     
  96.  
    fixed4 frag (v2f i) : SV_Target
  97.  
    {
  98.  
    // sample the texture
  99.  
    fixed4 col = tex2D(_MainTex, i.uv.xy);
  100.  
     
  101.  
    // Use #if, #ifdef or #if defined
  102.  
    #if _INVERT_ON
  103.  
    col = 1 - col;
  104.  
    #endif
  105.  
     
  106.  
    // Use #if, #ifdef or #if defined
  107.  
    #if ENABLE_FANCY
  108.  
    col.r = 0.5;
  109.  
    #endif
  110.  
     
  111.  
    fixed4 secCol = tex2D(_SecondTex, i.uv.zw);
  112.  
     
  113.  
    #if _OVERLAY_ADD
  114.  
    col += secCol;
  115.  
    #elif _OVERLAY_MULTIPLY
  116.  
    col *= secCol;
  117.  
    #endif
  118.  
     
  119.  
    col *= _Shininess;
  120.  
     
  121.  
    return col;
  122.  
    }
  123.  
    ENDCG
  124.  
    }
  125.  
    }
  126.  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

得到的面板是这样的:

 

这里写图片描述

 

上面的代码基本显示了所有可能的例子。有一点需要注意的是使用#pragma multi_compile声明多个keyword时,每个keyword之间是没有逗号的,而官网里的有逗号,这是不对的。

除了上面的四种内置属性面板外,还有一些装饰性的内置drawer,例如Space和Header。在上面的例子里,我们在它们的后面直接写属性,像下面这样:

[Space] _Prop1 ("Prop1", Float) = 0
  • 1

装饰性drawer的后面有没有紧跟属性都是可以的。

shader feature和shader keyword

从上面的内容,我们知道可以使用#pragma shader_feature和#pragma multi_compile来实现“只写一份代码但实现不同功能”的目的。但世界上没有免费的午餐,Unity实际上只是在背后生成了很多份有少许不同的shader变种(shader variants),每个变种包含了不同的keyword而已,没什么非常神奇的地方。

当我们在代码里写到:

#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
  • 1

Unity就生成了3个shader变种,每个对应开启其中的一个keyword。

如果我们再添加新的定义:

#pragma shader_feature ENABLE_FANCY
  • 1

Unity会再分别为是否开启ENABLE_FANCY来生成两种变种,也就是一共生成了3*2=6个shader变种。因此,当我们定义了大量的feature或者keyword的时候,就会出现有大量的变种!(而且Unity允许的keyword数目也是有限制的。)

#pragma shader_feature和#pragma multi_compile的区别

那么#pragma shader_feature和#pragma multi_compile有什么区别呢?实际上,#pragma shader_feature是#pragma multi_compile的子集,#pragma shader_feature生成的变种一个是不包含任何keyword的,一个是包含某个keyword的。我们可以使用#pragma multi_compile来实现同样的目的,只需要使用一个全是下划线的名字来表示不使用任何keyword即可。下面的两句话是在大多数情况下等价的:

 
  1.  
    #pragma shader_feature ENABLE_FANCY
  2.  
    #pragma multi_compile __ ENABLE_FANCY
  • 1
  • 2

但区别在于,使用multi_compile来定义keyword的话Unity是一定会生成所有变种的,。而如果使用的是shader_feature的话,如果有些keyword我们实际上并没有使用到,Unity也不会为这些生成shader变种,我们可以在shader的代入面板里看到到底实际生成了多少个变种:

 

这里写图片描述

 

因此(非常重要!!!),shader_feature适用于那些会在材质面板中直接设置的情况,而如果需要在脚本里通过DisableKeyword和EnableKeyword来开启或关闭keyword的话就应该使用multi_compile。(栽过坑啊!!!)并且不要在#pragma后面写注释!!!如果要在脚本里通过Shader.DisableKeyword来控制keyword的开关的话,不要在Properties里写KeywordEnum,这样可能会导致脚本里的设置失效(可以重新建一个材质解决)。但如果是使用material.DisableKeyword来设置的话,就不会有这个问题,原因暂时不明。

实际上,Unity的surface shader能够有那样强大的功能也是依靠了这样的机制。也包括我们在通过使用#pragma multi_compile_fwdbase或multi_compile_fwdadd这样的代码时,我们之所以需要使用这样的语句就是因为Unity为forward pass等内置了一些shader keyword,而我们通过这些语句来让unity为不同的光照pass生成不同的shader变种,否则的话我们的shader就一种类型,只能包含特定的任务,无法为多类型的光源等进行不同的服务。

当然,我们可以独立使用#pragma shader_feature和#pragma multi_compile,而不必一定要和MaterialPropertyDrawer配合使用。我们可以直接在代码里通过Material.EnableKeyword和Material.DisableKeyword来局部开启某些keyword,也可以通过Shader.EnableKeyword和Shader.DisableKeyword来全局开启。

参考链接:http://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html

转载于:https://www.cnblogs.com/xiaowangba/p/6314638.html

关注
打赏
1664521772
查看更多评论
立即登录/注册

微信扫码登录

0.0405s