Ramp Texture 是一种控制漫反射的方法。 一般Ramp Texture是一种类似下图的渐变图:

一般的漫反射光照模型

这是一个半兰伯特光照模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
    // 计算光线与法线的夹角余弦,求漫反射颜色。夹角越小,值越大。
    half NdotL = dot (s.Normal, lightDir);

    // 将结果都转换到[0,1]范围,可以保证光线和平面夹角大于90度(即余弦值小于0)时也可以有颜色。
    half diff = NdotL * 0.5 + 0.5;

    half4 c;
    // atten是光照的衰减值
    c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
    c.a = s.Alpha;
    return c;
}

加入Ramp Texture:

1
2
3
4
5
6
7
8
9
10
11
12
half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
    …
    …
    // 根据一张ramp贴图,根据余弦值对其颜色采样
    half3 ramp = tex2D (_RampTex, float2(diff, diff)).rgb;
    
    half4 c;
    // atten是光照的衰减值
    c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten;
    c.a = s.Alpha;
    return c;
}

注意 half3 ramp = tex2D (_RampTex, float2(diff, diff)).rgb;
我们使用了diff值对RampTex 进行采样。
因为现在使用的Ramp贴图可以视为1D的颜色索引值,所以float2(diff, diff)也可以是float2(diff, 0),仅对x方向采样。
我们知道diff表示了光线和法线的夹角,夹角越小,diff值越大,颜色越亮,光线越直射表面。
所以效果上看,就是光线直接照射的面,采样到越靠右的颜色。

使用一个spotlight照射方块,Ramp贴图如上,可以看到光直接照射的面,diff很大,采样最右边的颜色,侧面的diff很小,采样倒了左侧的颜色。

Unity中的Surface Shaders可以说是一种代码生成手段,比起顶点/像素着色器,可以让我们更轻松的编写光照shader。它并没有特殊的地方,只是生成一些重复性的代码,你仍需要使用Cg / HLSL 编写代码。

工作原理

定义一个“surface 函数”,它会接收UVs或你需要的数据作为输入,并填充输出结构体SurfaceOutput。SurfaceOutput基本描述了表面的属性(albedo color, normal, emission, specularity等)。 然后表面着色器的编译器会算出,需要哪些输入,填充哪些输出等等,并生成实际的顶点/像素着色器代码,并渲染通道来控制正向或延迟渲染。

标准的SurfaceOutput结构体如下:

1
2
3
4
5
6
7
8
9
struct SurfaceOutput
{
    fixed3 Albedo;  // diffuse color
    fixed3 Normal;  // tangent space normal, if written
    fixed3 Emission;
    half Specular;  // specular power in 0..1 range
    fixed Gloss;    // specular intensity
    fixed Alpha;    // alpha for transparencies
};

Unity5中, 也可以使用基于物理的光照模型。内建的 Standard 和 StandardSpecular 光照模型使用如下输出结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct SurfaceOutputStandard
{
    fixed3 Albedo;      // base (diffuse or specular) color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Metallic;      // 0=non-metal, 1=metal
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};
struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      // diffuse color
    fixed3 Specular;    // specular color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};

Surface Shader编译指令

和其他shader一样,Surface shaders置于 CGPROGRAM .. ENDCG 区域中。区别在于:

  1. 它必须被置于SubShader中,而不是Pass中。表面着色器会自动编译为多个pass。
  2. 它使用#pragma surface … 指令来表明它是Surface Shader。

#pragma surface指令如下:

1
#pragma surface surfaceFunction lightModel [optionalparams]

必要参数:

surfaceFunction - 包含表面着色器代码的cg函数。

函数声明如:void surf (Input IN, inout SurfaceOutput o) Input是你定义的结构体,应该包含纹理坐标和其他需要的自动变量。

lightModel - 使用的光照模型。

内建的光照模型是基于物理的 Standard 和 StandardSpecular,也有简单的非物理的 Lambert (diffuse) 和 BlinnPhong (specular)。也可以编写自己的光照模型。

  • Standard 使用 SurfaceOutputStandard 输出结构体, 并匹配 Standard (metallic workflow) shader.
  • StandardSpecular 使用 SurfaceOutputStandardSpecular 输出结构体, 并匹配 Standard (specular setup) shader.
  • Lambert 和 BlinnPhong 是不基于物理的(通常用于Unity 4.x), 但在低端硬件上使用它们会更高效。

可选参数

Transparency and alpha testing

这是由 alpha 和 alphatest 指令控制的。透明(Transparency )通常有两种方式:传统的alpha blending 或者 物理模拟的 premultiplied blending (允许半透明表面拥有合适的高光反射)。激活semitransparency 会使生成的代码包含blending命令,而激活 alpha cutout 在生成的片段着色器中会根据给定变量进行片段抛弃。

  • alpha or alpha:auto - Will pick fade-transparency (same as alpha:fade) for simple lighting functions, and premultiplied transparency (same as alpha:premul) for physically based lighting functions.
  • alpha:fade - Enable traditional fade-transparency.
  • alpha:premul - Enable premultiplied alpha transparency.
  • alphatest:VariableName - Enable alpha cutout transparency. Cutoff value is in a float variable with VariableName. You’ll likely also want to use addshadow directive to generate proper shadow caster pass.
  • keepalpha - By default opaque surface shaders write 1.0 (white) into alpha channel, no matter what’s output in the Alpha of output struct or what’s returned by the lighting function. Using this option allows keeping lighting function’s alpha value even for opaque surface shaders.
  • decal:add - Additive decal shader (e.g. terrain AddPass). This is meant for objects that lie atop of other surfaces, and use additive blending. See Surface Shader Examples
  • decal:blend - Semitransparent decal shader. This is meant for objects that lie atop of other surfaces, and use alpha blending. See Surface Shader Examples。

Custom modifier functions

可以改变或计算输入的顶点数据,或改变最终计算出的片段颜色(fragment color)。

  • vertex:VertexFunction - Custom vertex modification function. This function is invoked at start of generated vertex shader, and can modify or compute per-vertex data. See Surface Shader Examples.
  • finalcolor:ColorFunction - Custom final color modification function. See Surface Shader Examples.
  • finalgbuffer:ColorFunction - Custom deferred path for altering gbuffer content.
  • finalprepass:ColorFunction - Custom prepass base path.

Shadows and Tessellation

额外的指令,可用于控制shadows 和 tessellation(曲面细分)。

  • addshadow - Generate a shadow caster pass. Commonly used with custom vertex modification, so that shadow casting also gets any procedural vertex animation. Often shaders don’t need any special shadows handling, as they can just use shadow caster pass from their fallback.
  • fullforwardshadows - Support all light shadow types in Forward rendering path. By default shaders only support shadows from one directional light in forward rendering (to save on internal shader variant count). If you need point or spot light shadows in forward rendering, use this directive.
  • tessellate:TessFunction - use DX11 GPU tessellation; the function computes tessellation factors. See Surface Shader Tessellationfor details.

Code generation options

默认生成的表面着色器代码会尝试处理所有可能的lighting / shadowing /lightmap 情景。然而有时有些并不需要,这时可以调整生成代码跳过它们。

  • exclude_path:deferred, exclude_path:forward, exclude_path:prepass - Do not generate passes for given rendering path (Deferred Shading, Forward and Legacy Deferred respectively).
  • noshadow - Disables all shadow receiving support in this shader.
  • noambient - Do not apply any ambient lighting or light probes.
  • novertexlights - Do not apply any light probes or per-vertex lights in Forward rendering.
  • nolightmap - Disables all lightmapping support in this shader.
  • nodynlightmap - Disables runtime dynamic global illumination support in this shader.
  • nodirlightmap - Disables directional lightmaps support in this shader.
  • nofog - Disables all built-in Fog support.
  • nometa - Does not generate a “meta” pass (that’s used by lightmapping & dynamic global illumination to extract surface information).
  • noforwardadd - Disables Forward rendering additive pass. This makes the shader support one full directional light, with all other lights computed per-vertex/SH. Makes shaders smaller as well.

Miscellaneous options

杂项设置

  • softvegetation - Makes the surface shader only be rendered when Soft Vegetation is on.
  • interpolateview - Compute view direction in the vertex shader and interpolate it; instead of computing it in the pixel shader. This can make the pixel shader faster, but uses up one more texture interpolator.
  • halfasview - Pass half-direction vector into the lighting function instead of view-direction. Half-direction will be computed and normalized per vertex. This is faster, but not entirely correct.
  • approxview - Removed in Unity 5.0. Use interpolateview instead.
  • dualforward - Use dual lightmaps in forward rendering path.

表面着色器的输入结构体:

输入的结构体 Input 通常包含所需的纹理坐标。纹理坐标通常用uv_加贴图名字构成。(或者uv2_ 表示第二张贴图)。 还有其他值可以放入Input结构体:

  • float3 viewDir - will contain view direction, for computing Parallax effects, rim lighting etc.
    观察向量。
  • float4 with COLOR semantic - will contain interpolated per-vertex color.
    每个顶点的插值颜色。
  • float4 screenPos - will contain screen space position for reflection or screenspace effects.
    屏幕空间坐标。
  • float3 worldPos - will contain world space position.
    世界空间坐标。
  • float3 worldRefl - will contain world reflection vector if surface shader does not write to o.Normal. See Reflect-Diffuse shader for example.
    世界中的反射向量。
  • float3 worldNormal - will contain world normal vector if surface shader does not write to o.Normal.
    世界中的法线向量。
  • float3 worldRefl; INTERNAL_DATA - will contain world reflection vector if surface shader writes to o.Normal. To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN, o.Normal). See Reflect-Bumped shader for example.
    内部数据。如果表面着色写入到o.Normal,将包含世界反射向量 。
  • float3 worldNormal; INTERNAL_DATA - will contain world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal).
    内部数据。如果表面着色写入到o.Normal, 将包含世界法线向量。

CG中的一些函数:

1
half4 c = tex2D (_MainTex, IN.uv_MainTex); 

tex2D: 对一张贴图采样,返回float4

1
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

UnpackNormal是定义在UnityCG.cginc文件中的方法,这个文件中包含了一系列常用的CG变量以及方法。UnpackNormal接受一个fixed4的输入,并将其转换为所对应的法线值(fixed3)。在解包得到这个值之后,将其赋给输出的Normal,就可以参与到光线运算中完成接下来的渲染工作了。

1
saturate(x)

saturate :将x限制在0-1之间

Surface Shaders中的自定义光照模型

当写Surface Shaders时,你会描述一个表面的属性和由光照模型计算的灯光互动。内建的光照模型是Lambert (diffuse lighting) 和 BlinnPhong (specular lighting).

但也许有时你想使用自定义的光照模型。光照模型无非就是一组符合一些规则的Cg/HLSL函数。Lambert 和 BlinnPhong 模型是定义在Lighting.cginc文件。
(Windows:{unity install path}/Data/CGIncludes/Lighting.cginc, Mac:/Applications/Unity/Unity.app/Contents/CGIncludes/Lighting.cginc).

光照模型声明

光照模型是一组以Lighting开头的有规则的函数,它们可以声明在shader的任何位置或任一包含文件内。这些函数有:

1
2
3
4
5
6
7
8
//用于光照模型的forward rendering path,不依赖视线方向(如diffuse)。     
half4 Lighting<Name> (SurfaceOutput s, half3 lightDir, half atten);
      
//用于光照模型的forward rendering path,但是赖视线方向。
half4 Lighting<Name> (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
       
//用于deferred lighting path。    
half4 Lighting<Name>_PrePass (SurfaceOutput s, half4 light);

注意,你并不需要声明所有函数。一个光照模型既可以使用视角方向也可以不使用。同样,如果光照模型不用于deferred lighting,你并不需要 _PrePass 函数。

解码光照贴图

与灯光函数类似,解码光照贴图,可以根据是否依赖视角方向选择使用下面一种函数。要解码标准的unity光照贴图数据(传入color, totalColor, indirectOnlyColor和scale参数),使用DecodeLightmap函数。

自定义的光照贴图解码函数会自己处理forward 和 deferred lighting rendering paths。但你必须了解,对于deferred,Lighting_PrePass 函数必须在光照贴图解码之后调用,然后light参数会包含实时灯光和光照贴图的和。如果必要的话,可以使用内建宏UNITY_PASS_PREPASSFINAL来区分foward和deferrd。

解码 Single lightmaps 的函数:

1
2
3
4
5
//用于不依赖观察方向的光照模型。
half4 Lighting<Name>_SingleLightmap (SurfaceOutput s, fixed4 color);         
    
//依赖观察方向的光照模型。
half4 Lighting<Name>_SingleLightmap (SurfaceOutput s, fixed4 color, half3 viewDir); 

解码 Dual lightmaps 的函数:

1
2
3
4
5
//用于不依赖观察方向的光照模型。
half4 Lighting<Name>_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade);     

//依赖观察方向的光照模型。
half4 Lighting<Name>_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade, half3 viewDir);     

解码 Directional lightmaps 的函数:

1
2
3
4
5
//用于不依赖观察方向的光照模型。
half4 Lighting<Name>_DirLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal);    
   
//依赖观察方向的光照模型。
half4 Lighting<Name>_DirLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, half3 viewDir, bool surfFuncWritesNormal, out half3 specColor); 

参考:
Writing Surface Shaders
Custom Lighting models in Surface Shaders

每个shader都由一列SubShader构成。真正用于呈现渲染物体的内容是在SubShader中实现的。Unity在实际运行时,会根据硬件情况从上到下选择最优的一个SubShader来执行。

语法

1
2
3
4
5
Subshader { 
    [Tags] 
    [CommonState] 
    Passdef [Passdef ...] 
}

细节

每个SubShader都定义了一系列passes,并可对所有passes可选的设置任一状态。另外,还可以指定[Tags]。 当Unity选定了一个SubShader,它会对定义的每一个pass都渲染一遍。有时在某些硬件某些复杂的渲染效果必须在多个pass中完成。

每个pass都可以被定义成 regular Pass,  Use Pass or Grab Pass.

Pass简介

一个pass块引起一次物体的几何学渲染。

  • 语法
    Pass { [Name and Tags] [RenderSetup] }

  • Name and tags
    一个pass块可以定义它的名字和任意数量的Tags。 内置的Tags都是针对渲染路径的,告诉渲染引擎这个pass块应该在什么路径下被渲染。 Name一般被用来引用Pass块。pass名字都是大写。

UsePass

UsePass命令可以使用另一个shader中的pass块,从而减少重复劳动。

格式:UsePass “Shader/Name”

若要UsePass能工作,所引用的pass块必须定义了名字。

GrabPass

GrabPass是一种特殊的pass类型,它可以捕获物体所在位置的屏幕内容并写入到一个纹理中。从而用于后续通道完成高级图像效果。

语法:

  • GrabPass{},能捕获当前屏幕的内容到一个纹理中,纹理能在后续通道中通过 _GrabTexture 进行访问。注意:这种形式的捕获通道对于每个使用它的物体将进行昂贵的屏幕捕捉操作。
  • GrabPass { “TextureName” },能捕获屏幕内容到一个纹理中,但对于第一个给定纹理名的物体,只会在每帧中处理一次。在后面的通道中可以通过纹理名获取纹理。当你在一个场景中有多个物体使用grab pass,这是更高效的方法。

另外, GrabPass 能使用 Name 和 Tags 命令。

Tags

SubShader使用Tags告诉渲染引擎期望何时和如何渲染对象。

语法

字符串的键值对:

1
2
3
4
Tags { 
    "TagName1" = "Value1" 
    "TagName2" = "Value2" 
}

细节

在一个SubShader中标签被用于决定渲染次序和其他SubShader的参数。 注意接下来的标签只能置于SubShader块而不是Pass块中。 除了可被Unity认可的内置标签,你还可以使用自己的标签并通过Material.GetTag函数查询。

  • Rendering Order - Queue tag
    使用 Queue 标签决定对象被渲染的次序。比如确保透明(Transparent)物体在不透明(opaque)物体之后绘制。 有四种预定义队列,但可以在其之间定义更多队列:

    1. Background: 在所有队列之前被渲染,被用于渲染天空盒之类。
    2. Geometry (default): 默认的,被用于大多数对象。不透明的几何体使用这个队列。
    3. AlphaTest: alpha测试,这是分离自Geometry的队列,因为对于alpha-tested物体,在所有固体渲染后再渲染更有效率。
    4. Transparent: 透明,在Geometry队列之后被渲染,采用由后到前的次序。
    5. Overlay: 覆盖,用于实现叠加效果。任何需要最后渲染的对象应该放置在此处。(如镜头光晕)。

    每一个队列都代表一个整数索引值。Background-1000,Geometry-2000,AlphaTest-2450,Transparent-3000,Overlay-4000。可以自定义一个队列:

    Tags { “Queue” = “Geometry+1” }

    该队列的索引值为2001,这会使物体在所有不透明的对象之后但却在所有透明物体前被渲染。

  • RenderType
    将shader分类成预定义的组。

  • DisableBatching
    一些shader在Draw Call Batching 模式下不能工作,这个指令来决定是否禁用它。 “True”:禁用。“False”:不禁用(默认)。“LODFading”:当LOD Fading开启时禁用,一般用于树木。

  • ForceNoShadowCasting  “True”:不使用cast shadows。

  • IgnoreProjector 
    “True”:物体不会受 Projectors影响。

  • CanUseSpriteAtlas 
    “False”:如果这个shader是指sprites,将无法打包成图集。 (Sprite Packer)

  • PreviewType 
    指示material inspector预览怎样显示材质。默认显示是spheres,也可设置为“Plane”(2D显示),或者”Skybox”(天空球显示)。


pass中也可以使用Tags


参考: Unity Manual - ShaderLab: SubShader

如前所述,Unity中的所有Shaders都是用“ShaderLab”这种声明式语言(Declarative programming)编写的。真正的“shader code”写在同一shader文件的CGPROGRAM snippets内。CGPROGRAM snippets 是用通用的 HLSL/Cg 语言编写的。

Properties

Properties { Property [Property …] }

格式

_Name(“Display Name”,type) = defaultValue{options} 属性名(“显示名”,类型) = 默认值 {可选参数}

数字和滑动条

FloatIntRange (min, max)
这些都使用默认值定义了数字属性。 Range格式会显示一个min到max的滑动条。

1
2
name ("display name", Range (min, max)) = number
name ("display name", Float) = number

Colors and Vectors

定义一个RGBA颜色,或者一个4D向量属性。

1
2
name ("display name", Color) = (number,number,number,number)
name ("display name", Vector) = (number,number,number,number)

Textures

定义一个2D texturecubemap or 3D (volume) 属性。

1
2
3
name ("display name", 2D) = "defaulttexture" {}
name ("display name", Cube) = "defaulttexture" {}
name ("display name", 3D) = "defaulttexture" {}
  • 2D:一张2的阶数大小的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终显示出来。
  • Cube: 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样。
  • 3D: Unity支持3D纹理,从着色器和脚本使用并创建。目前,3D纹理只能通过脚本创建。

细节

每个属性都是根据name引用的(在Unity中一般以下划线_开始),而在编辑器界面显示的是display name,等号后面指定了默认值:

  • 对于Range和Float其只是一个浮点数,color范围0~1
  • Color和Vector是括号内的四个数
  • 贴图(2D、Cube)默认值是一个空字符串,或者是内建的默认贴图:“white”, “black”, “gray” or “bump”.

之后在shader的fixed function部分,属性值可以通过方括号加属性名获取:[name]。 Properties块中的属性会被序列化成material数据,这对于在脚本控制值非常有用 (使用 Material.SetFloat 或类似函数).

Property attributes and drawers

对之前的属性,都可以使用方括号指定可选的修饰(attributes),下面是一些Unity可以认出的修饰,或是可以自己指定(MaterialPropertyDrawer classes) 以控制它们在material inspector中如何被渲染。

  • [HideInInspector] 不在inspector显示
  • [NoScaleOffset] 对于texture属性,不会显示贴图的 tiling/offset字段。
  • [Normal] 指示一个贴图是normal-map。
  • [HDR] 指示一个贴图是高动态范围(high-dynamic range (HDR))贴图。
  • [PerRendererData] 指示一个贴图是来自MaterialPropertyBlock格式的 per-renderer data 。

参考: Unity Manual - ShaderLab: Properties

一个顶点光照着色器的例子

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
Shader "Custom/FF1" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0)
        _Ambient ("Ambient", Color) = (0.5,0.5,0.5,0)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Pass {
            Material {
                Diffuse [_Color]
                Ambient [_Ambient]
                Shininess [_Shininess]
                Specular [_SpecColor]
                Emission [_Emission]
            }
            Lighting On
            SeparateSpecular On
            SetTexture [_MainTex] {
                Combine texture * primary DOUBLE
            }
        }
    }
}

首先要注意,shader不区分大小写。

首先第一行是shader的名称,采用分级方式。定义好后可通过Shader.Find来找到对应名称的shader。

接下来是属性(Properties)。其中包含许多属性。前一部分是属性名,括号内字符串是显示的名字,后面跟着属性的类型。最后等号后面是默认值。 属性的类型有许多种包括数字(Float,Int),滑动条(Range), 颜色(Color),Vector, 贴图(2D、Rect、3D、Cube)等。

下面是一个SubShader。每个Subshader都由一个或多个Passes组成,一个Pass块引起物体的一次渲染。所以至少要有一个Pass。 我们的Pass中有一个Material块,绑定了属性值到材质设置。

再下面是一些操作命令。

属性解释

  • Color 
    物体的纯色
  • Diffuse Color
    漫反射颜色。这是物体的基本颜色。
  • Ambient Color
    环境色颜色。物体在被RenderSettings里面设置的环境光照射时的颜色。
  • Specular Color
    物体反射高光的颜色。
  • Shininess Number
    高光的锐利度,在0到1之间,0时高光巨大,1时高光尖锐。
  • Emission Color
    自发光,当物体没有被任何光线照射时的颜色。

  • Lighting On | Off
    顶点光照的开关
  • SeparateSpecular On | Off
    控制顶点光照的高光开关

Combine texture

SetTexture 读入材质内容,Combine将primary(前面光照计算的颜色)与材质纹理合并,DOUBLE表示乘以2以让颜色更亮。

SetTexture 可以不只有一个(支持多少个由硬件决定,一般两个就够了)。将上面例子改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.......

Properties {
......
_MainTex ("Base (RGB)", 2D) = "white" {}
    _SecondTex ("Second (RGB)", 2D) = "white" {}
}

......

SetTexture [_MainTex] {
    Combine texture * primary DOUBLE
}
SetTexture [_SecondTex] {
    Combine texture * previous DOUBLE
}

第二个SetTexture 中的previous表示上一次SetTexture 计算的结果。从而可以混合两张纹理。

其他更为详细的命令,将在后面总结。


参考: Unity Manual - Shaders: ShaderLab & Fixed Function shaders