Surface Shader
本小结对Unity的Surface Shader做一个大概的了解。主要了解在Surface Shader当中比较重要的几个部分,分别是:
- SurfaceOutput
- Input
- lighing
- shadow
首先查看一下Unity的官方手册中的Writing Surface Shaders,其中描述道:如果要编写一个shader去和光进行交互是比较复杂的,因为光照会有不同的光照类型,不同的阴影选项和不同的渲染路径(包括foward和deferred rendering等),因此shader需要找到处理这些复杂事物的方法。
在使用Surface Shader时,它可以自动生成一些代码,比直接去使用低阶的顶点和像素着色器来说要容易许多。但是需要注意的是Surface Shader并不是一种定制的语言,也不是一种神奇的东西,它只不过自动生成了以前必须去手写的代码。Surface Shader还是使用Cg或HLSL语言编写。在前面课程当中,已经初步解到,Surface Shader实际上就是对顶点和像素着色器的一种包装,它让我们不用去关注更多的顶点和片段程序的细节,能够快速地得到想要的着色器。接下来试着编写一些Surface Shader程序。
创建一个默认的Surface Shader,如下:
1 Shader "Lesson/SurfaceShader1" { 2 Properties { 3 _Color ("Color", Color) = (1,1,1,1) 4 _MainTex ("Albedo (RGB)", 2D) = "white" {} 5 _Glossiness ("Smoothness", Range(0,1)) = 0.5 6 _Metallic ("Metallic", Range(0,1)) = 0.0 7 } 8 SubShader { 9 Tags { "RenderType"="Opaque" } 10 LOD 200 11 12 CGPROGRAM 13 // Physically based Standard lighting model, and enable shadows on all light types 14 #pragma surface surf Standard fullforwardshadows 15 16 // Use shader model 3.0 target, to get nicer looking lighting 17 #pragma target 3.0 18 19 sampler2D _MainTex; 20 21 struct Input { 22 float2 uv_MainTex; 23 }; 24 25 half _Glossiness; 26 half _Metallic; 27 fixed4 _Color; 28 29 void surf (Input IN, inout SurfaceOutputStandard o) { 30 // Albedo comes from a texture tinted by color 31 fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; 32 o.Albedo = c.rgb; 33 // Metallic and smoothness come from slider variables 34 o.Metallic = _Metallic; 35 o.Smoothness = _Glossiness; 36 o.Alpha = c.a; 37 } 38 ENDCG 39 } 40 FallBack "Diffuse" 41 }
首先来了解一下这个Shader结构。这个Shader在Properties中有四个属性:
- _Color为颜色值。
- _MainTex是主纹理。
- _Glossiness是一个浮点值,用于计算高光的光泽度。
- _Metallic也是一个浮点值,用于计算表现金属的光泽度。
该shader有一个SubShader,但是,在这个SubShader中并没有Pass通道。这里需要注意的是,在Surface Shader当中不需要去编写Pass通道,原因是Surface Shader就是对Vertex & Fragment Shader的一种包装,它能够在自动生成着色器代码,生成的过程不需要我们干预,Pass通道也能自动生成,因此,如果添加了Pass通道,就会出现编译错误。最后,作为ShaderLab的基本结构,该默认的Surface Shader拥有一个FallBack,如果shader中的某一种特性不能够被使用,那么会回滚到Diffuse。
接下来重点了解一下SubShader拥有哪些内容。其中“Tags { "RenderType"="Opaque" }”描述的是渲染类型,“Opaque”表示不透明的物体;“LOD 200”是指层级细节;“CGPROGRAM”到“ENDCG”是一个代码块,表示其中使用Cg语法,要真正地学会Surface Shader编程,有必要先学会Cg语言,不过现在可以简单地从Surface Shader的结构出发,基本了解一下Surface Shader的内容。
首先“#pragma surface surf Standard fullforwardshadows”是Surface Shader当中比较重要的一部分。“pragma”是一个编译指令,这个编译指令有具体的格式,可以从Unity官方手册中了解一下有关的内容,其格式为:“#pragma surface surfaceFunction lightModel [optionalparams]”。
- 以“#”开头,“surface”关键词表示该shader以Surface Shader格式来编写,Unity引擎在处理这个shader结构的时候,就会自动地进行编译低阶的代码。
- “surfaceFunction”是指surface的函数,在一定条件下可以随便取名,在默认创建的Surface Shader中的"surf"就是函数名,下面也有对应的函数体。
- “lightModel”描述的是光照模型,在默认创建的Surface Shader中使用的是“Standard”,那么这个光照模型实际上也是一个函数,我们可以在Unity(5.0以上)的安装目录上找到一个光照的代码文件"UnityPBSLighting.cginc",其中的函数“LightingStandard”,其中“Lighting”后的名称是我们真正使用的函数名。
- “[optionalparams]”表示其他的一些选项,默认Surface Shader使用的是“fullforwardshadows”,从字面上理解是关于阴影的一些功能,查看Unity手册可以了解到默认使用的“fullforwardshadows”表示它能够在Forward渲染路径下支持所有的阴影类型,默认的shader仅仅能支持一个方向光的阴影,如要在Forward渲染路径下使用点光源或聚光灯产生的阴影就需要使用该指令。这里需要注意的是,在其上还有一个“addshadow”指令,它的主要功能就是生成一个阴影投射器,在现目前的代码当中也可以直接使用“addshadow”。
接下来在工程中实际使用一下这个shader。
创建一个材质并使用该shader,然后在场景中创建一个Cube当做地面,在其上创建一个Sphere,并使用我们创建的材质,可以看到球体的检视面板中已经拥有了shder当中编写的四个属性:“Color”是颜色,默认为白色;“Albedo (RGB)”是主纹理,这里先拖放一张贴图;“Smoothness”是平滑度,描述的是高光的强度;“Metallic”表示的是金属质感,调整这个系数可以修改金属质感的强度,金属质感原理就是如果光照射非常光滑的金属物体,有镜面反射的部分光会很集中,形成高亮,没有镜面反射的地方就会很暗淡。效果如图:
以上就是对该shader的基本使用。
接下来在该shader中,“#pragma target 3.0”表示我们将要对这个着色器使用硬件的“shader model 3.0”的能力,硬件的“shader model”是硬件一个用于着色处理的基础的指令范围和能力,值越高表示能使用越高级的功能,如果没有使用“#pragma target 3.0”这句指令,默认使用的是“shader model 2.0”。
“sampler2D”是一个二维纹理,这里表示该shader的主纹理参数。
struct Input { float2 uv_MainTex; };
之后的Input结构体,用于描述纹理的uv坐标,查看Unity文档,在"Surface Shader input structure"目录中,关于这个纹理坐标,必须以“uv”或“uv2”开头,使用“uv”表示用第一套uv坐标集合,用“uv2”表示第二套uv坐标集合。因此,如果在Properties中使用类似"_MainTex"纹理属性,就需要在SubShader中的一个输入结构体当中必须以“uv”开头的变量,否则就得不到该纹理采样值。
除了Input结构体之外,在CGPROGRAM要对Properties中的属性做对应的声明。两者的类型不一样,“2D”对应“sampler2D”,“Range”对应“half”,“Color”对应“fixed4”。