Shader编程学习笔记(七)—— Surface Shader 1

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”。

相关文章

相关标签/搜索