Unity Shader基础-ShaderLab语法基础

Unity Shader基础-ShaderLab语法基础


引用官方文档中对ShaderLab的解释:

Unity中所有Shader文件都通过一种陈述性语言进行描述,称为“ShaderLab”, Shader文件中用一种嵌套式语法来描述着色器行为,如材质面板中显示的属性、使用的混合模式等等,实际的着色器代码(如HLSL/Cg)定义在CGPROGRAM与ENDCG之间。

着色器文件的根命令为Shader,每个文件都必须调用该命令进行定义,语法如下:

Shader "name" 
{ 
	[Properties] 
	Subshaders 
	[Fallback] 
	[CustomEditor] 
}

下面对ShaderLab语言进行简单介绍。


1 属性关键字Properties


Unity着色器可通过该关键字定义一个参数列表,该参数列表可在Unity材质面板中进行配置,定义方法如下。

Properties { Property [Property ...] }

数字与滑动条

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

通过Range进行定义会在面板中显示为滑动条,基本类型则为可填数字。

着色与向量

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

定义颜色值为RGBA,向量为四维向量。

纹理

name ("display name", 2D) = "defaulttexture" {}
name ("display name", Cube) = "defaulttexture" {}
name ("display name", 3D) = "defaulttexture" {}

定义2D或3D纹理,及cubemap

示例

// properties for a water shader
Properties
{
    _WaveScale ("Wave scale", Range (0.02,0.15)) = 0.07 // sliders
    _ReflDistort ("Reflection distort", Range (0,1.5)) = 0.5
    _RefrDistort ("Refraction distort", Range (0,1.5)) = 0.4
    _RefrColor ("Refraction color", Color) = (.34, .85, .92, 1) // color
    _ReflectionTex ("Environment Reflection", 2D) = "" {} // textures
    _RefractionTex ("Environment Refraction", 2D) = "" {}
    _Fresnel ("Fresnel (A) ", 2D) = "" {}
    _BumpMap ("Bumpmap (RGB) ", 2D) = "" {}
}

Shader中调用

Unity着色器代码中调用属性值,需要定义一个与属性名称与类型都匹配的变量,如:

Shader "Unlit/TestShader"
{
	//属性声明
	Properties{
		_Color("Tint", Color) = (1,1,1,1)
	}
	//着色器
	SubShader{
		Pass{
			CGPROGRAM
			...
			//在CG代码中,需要定义一个与属性名称与类型都匹配的变量
			fixed4 _Color;
			...
			ENDCG
		}
	}
}

2 子着色器关键字SubShader


Unity的着色器由一个子着色器列表组成,当Unity需要显示的个网格时,会从子着色器列表选择第一个子着色器来渲染。

Subshader { [Tags] [CommonState] Passdef [Passdef ...] }

Subshader语句由可选的Tags与通用状态,及一个渲染通道列表。当Unity选择某个子着色器进行渲染时,它会将每个通道都渲染一次(由于光照作用可能还会更多),由于每次渲染都是开销巨大的操作,因此应该尽可能降低通道数量。

示例:

// ...
SubShader {
    Pass {
        Lighting Off
        SetTexture [_MainTex] {}
    }
}
// ...

2.1 Pass


语法:

Pass { [Name and Tags] [RenderSetup] }

Pass命令包含一个渲染状态设置命令列表,一个通道可以定义其名称(Name)及任意数量的标签(Tags)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EM0LiYxw-1572692889763)(Image/SubShaderStruct.png)]


2.1.1 裁剪与深度测试相关渲染状态设置

Cull

  • 用法:

      Cull Back/Front/Off
    
  • 作用: 遮挡剔除模式
  • 说明:Back表示不渲染背面多边形(默认);Front表示不渲染正面多边形,用于显示物体内部;Off表示关闭剔除。

ZTest

  • 用法:

      ZTest Less/Greater/LEqual/GEqual/Equal/NotEqual/Always
    
  • 作用:深度缓存测试模式

  • 说明:控制深度测试如何执行,默认为LEqual,代表通过比较深度来更改颜色缓存的值。例如当取默认值的情况下,如果将要绘制的新像素的z值小于等于深度缓存中的值,则将用新像素的颜色值更新深度缓存中对应像素的颜色值,其实就是较为流行的Z-Buffer算法

  • 备注:需要注意的是,当ZTest取值为Off时,表示的是关闭深度测试,等价于取值为Always,而不是Never!Always指的是直接将当前像素颜色(不是深度)写进颜色缓冲区中;而Never指的是不要将当前像素颜色写进颜色缓冲区中,相当于消失。

ZWrite

  • 用法:

      ZWrite On/Off
    
  • 作用:设置深度缓存写模式

  • 说明:控制是否将当前对象的像素点写入深度缓存,或画实心体,可打开该项;当绘制半透明特效,则可关闭。

  • 备注:在On的情况,还要看ZTest是否通过,通过才会写入深度缓存。

Offset

  • 用法:

      Offset Factor, Units
    
  • 作用: 设置深度偏移量,Factor为Z相对XY所在平面最大斜率的缩放参数,Units为最小可分辨深度缓存值的缩放值参数,该命令可以强制将一个多边形绘制到另一个多边行之上(即便它们的位置是一样的),如Offset 0, -1可忽略多边形斜率情况下,将多边形拉近至摄像机;可以这么理解:

    每一个Fragment的深度值都会增加如下所示的偏移量:

      offset = (m * factor) + (r * units)
    

    m是多边形的深度的斜率(在光栅化阶段计算得出)中的最大值,一个多边形越是与近裁剪面(near clipping plan)平行,m就越接近0。

    r是能产生在窗口坐标系的深度值中可分辨的差异的最小值,r是由具体实现OpenGL的平台指定的一个常量。

    一个大于0的offset会把模型推到离你(摄像机)更远一点的位置,相应地,一个小于0的offset会把模型拉近。

  • 备注:不理解该参数的作用的话,可参考unity shader Offset Factor, Units详解

ColorMask

  • 用法:

      ColorMask RGB / A / 0 / any combination of R, G, B, A
    
  • 作用: 设置颜色通道,ColorMask 0表示关闭所有颜色通道的渲染,默认模式为所有通道(RGBA)渲染。


2.1.2 混合(Blend)相关渲染状态设置


Blending用于制作透明物体,语法如下:

  • Blend Off: 关闭blending (默认)

  • Blend SrcFactor DstFactor: 配置并开启混合,生成的颜色乘以系数SrcFactor,绘制到屏幕上的颜色将乘以系数DstFactor,两数相加,公式如下:

      float4 result = SrcFactor * fragment_output + DstFactor * pixel_color
    
  • Blend SrcFactor DstFactor, SrcFactorA DstFactorA: 功能同上,只是使用不同系数混合Alpha通道。

  • BlendOp Op: 混合操作,采用不同操作混合颜色(上面是加法)。

  • BlendOp OpColor, OpAlpha: 同上,只是RGB通道与Alpha通道采用不同操作。

当使用多个Render Target时,以上命令稍做改动,如下:

  • Blend N SrcFactor DstFactor
  • Blend N SrcFactor DstFactor, SrcFactorA DstFactorA
  • BlendOp N Op
  • BlendOp N OpColor, OpAlpha

其中N是Render Targer的下标(0-7),该特性可用于大部分APIs/GPUs(DX11/12, GLCore, Metal, PS4)

Blend运算(Op)和系数因子(Factor)详见:Unity ShaderLab:Blending


2.2 UsePass


UsePase命令用于设置命名其它着色器中的通道。用法如下:

UsePass "Shader/Name"

通过该命令可实现着色器通道复用,减少代码重复。对于需要复用的通道,需要定义通道名称以供调用,通过Name关键字定义名称。

Name "MyPassName"

2.3 GrabPass


GrabPass命令用于捕获当前屏幕至一张纹理中(当前对象绘制前的屏幕内容),在GrabPass命令的后序通道定义中可以用该纹理。

示例:

Shader "GrabPassInvert"
{
    SubShader
    {
        // Draw ourselves after all opaque geometry
        Tags { "Queue" = "Transparent" }

        // Grab the screen behind the object into _BackgroundTexture
        GrabPass
        {
            "_BackgroundTexture"
        }

        // Render the object with the texture generated above, and invert the colors
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 grabPos : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert(appdata_base v) {
                v2f o;
                // use UnityObjectToClipPos from UnityCG.cginc to calculate 
                // the clip-space of the vertex
                o.pos = UnityObjectToClipPos(v.vertex);
                // use ComputeGrabScreenPos function from UnityCG.cginc
                // to get the correct texture coordinate
                o.grabPos = ComputeGrabScreenPos(o.pos);
                return o;
            }

            sampler2D _BackgroundTexture; //通过该变量进行访问

            half4 frag(v2f i) : SV_Target
            {
                half4 bgcolor = tex2Dproj(_BackgroundTexture, i.grabPos);
                return 1 - bgcolor;
            }
            ENDCG
        }

    }
}

以上示例中有两个通道,第一个通道取得当前对象后面的屏幕纹理,第二个通过访问该纹理内容。访问该纹理同样要在CGPROGRAM块中定义一个相同类型的变量,如以上代码中的sampler2D _BackgroundTexture。


2.4 Tags


子着色器通过Tags来告诉渲染引擎何时、如何渲染。定义方式如下:

Tags { "TagName1" = "Value1" "TagName2" = "Value2" }

Tags定义一个名称-取值对的列表,在子着色器的标签内部用于确定渲染顺序和其它参数,以下标签在Unity必须定义于子着色器中,不能在通道当中

Queue

可以通过Queue标签来定义对象的渲染顺序,一个着色器决定了从属于它的所有对象的渲染队列(如所有透明着色器应该在不透明对象之后绘制等)。有五种预定义渲染队列:

  • Background: 该渲染队列最早渲染,典型应用就是背景。

  • Geometry (默认):大部分对象都使用该方式,不透明几何体也使用该渲染队列。

  • AlphaTest: Alpha通道检测的几何体使用该渲染队列,由于绘制完所有实心体后再渲染alpha-tested对象会更加高效,因此这是一个独立的渲染队列。

  • Transparent:该渲染队列在Geometry和AlphaTest队列后渲染,按从后往前的顺序,任意alpha-blended对象(无法写入深度缓存)应该放入此队列(琉璃、粒子特效等)

  • Overlay:该渲染队列用于重叠效果,最后渲染的对象应放入该队列。

示例:

Shader "Transparent Queue Example"
{
     SubShader
     {
        Tags { "Queue" = "Transparent" }
        Pass
        {
            // rest of the shader body...
        }
    }
}

如果有特殊渲染要求,可队列之间进行嵌入,GPU进行着色时会按照渲染队列数值进行渲染。Background,Geometry,AlphaTest,Transparent,Overlay的整型数值为1000,2000,2450,3000,4000。如果使用下列命令进行嵌入:

Tags { "Queue" = "Geometry+1" }

此时该物体(2001)的渲染时机会先于transparent但后于opaque/ Geometry,这可将某种渲染嵌入队列之间,如水纹渲染就位于Geometry和transparent之间。自定义一个位于2500的序列,此时渲染不透明的游戏物体可获得最好性能。对透明物体的距离(次序)进行排序,然后按照次序高低进行渲染,推荐透明物体渲染队列在2500之上。而天空盒的渲染一般位于2000和3000之间。

RenderType

RenderType标签将着色器分类至几种预定义分组,如不透明着色器,alpha-tested着色器等

DisableBatching

当Drawcall合批时,某些着色器会失败,那是由于合批将所有几何体转换至世界空间,因此“模型空间”会丢失(没有参考物)。该标签可用于处理该情况,有三种取值:True表示关闭合批操作,False表示不关闭合批操作,LODFading表示当使用LOD时关闭合批,常被用于树木。

ForceNoShadowCasting

ForceNoShadowCasting标签为True时,用该子着色器进行渲染的对象将没有阴影,当引用其它着色器用于透明物体时,但又不想继承其它子着色器的shadow通道,该标签便十分有用了。

IgnoreProjector

IgnoreProjector标签为True时,则使用该着色器的对象将不受任何投影类型材质或者贴图影响,对于半透明物体十分有用。

CanUseSpriteAtlas

如果该着色器片段仅用于处理图片时,并且设置该标签的值为False,当想把图片打包成图集时,这个命令不会起作用

PreviewType

一般情况下,点击某个材质可在材质属性面板上预览到材质。默认材质都是球型的,使用该标签可将预览模型改为“2D平面”或者“3D天空盒”。

除了内置的标签,可以自定义标签,并通过Material.GetTag进行访问。


3 备用着色器关键字Fallback


当所有子着色器都定义完后,便可以定义备用着色器,即当所有的子着色器都没法在当前硬件运行时,试图使用其它着色器。定义方式如下:

Fallback "name"

没有备用着色器时,可声明以下语句,这样即使所有子着色器都无法在当前硬件平台运行时,也不会报警告。

Fallback Off

示例:

Shader "example" {
    // properties and subshaders here...
    Fallback "otherexample"
}

4 分类关键字Category


Category可对任意命令进行逻辑分组,常用于“继承”渲染状态,如着色器可能有多个子着色器,且它们都要求将Fog设置为Off,Blending设置为additive,可以使用如下分类:

Shader "example" {
	Category {
	    Fog { Mode Off }
	    Blend One One
	    SubShader {
	        // ...
	    }
	    SubShader {
	        // ...
	    }
	    // ...
	}
}

Category块仅影响着色器解析,等同于将分类内部任意状态设置至所有子模块中,并不影响着色器执行速度。


参考


Tags: Unity Shader Vistied:
Share: Twitter Facebook LinkedIn