为了减少 Coder 直接和灯光进行交涉的频次,Unity 提供了新的 Shader 编写方式 - 表面着色器

它对顶点 - 片元着色器进行了封装,还包含了很多的光照模型,比如 Lambert、Blinn-Phong 以及 PBR 等

# 表面着色器的组织结构

1
#pragma surface surfaceFunction lightModel [optionalparams]
  • surface : 声明所使用的 Shader 是表面着色器

  • sufaceFunction : 声明表面着色器的函数名称,称为表面函数

  • lightModel : 声明所使用的光照模型。(Lambert、BlinnPhong。物理光照模型: Standard 和 StandardSpecular)

  • optionalparams : 可选参数

# 可选参数 - 透明指令相关

  • alpha/(alpha : auto)
  • alpha: belnd 透明混合
  • alpha : fade 开启传统的透明模式
  • alpha : premul 开启预乘透明模式
  • alphatest: VariableName 开启透明测试,通常与 addshadow 指令结合,从而产生正确的投影 Pass
  • keepalpha: 默认情况下,不透明的物体会将 alpha 通道设置为 1,添加该指令可以为不透明的表面着色器保留光照函数的 alpha 值
  • decal : add
  • decal : blend

# 阴影和细分相关指令

  • addshadow
  • fullforwardshadows
  • tessellate: TestFunction DX11GPU 细分功能,通过细分系数对顶点进一步细分

# 控制代码生成选项

有些时候生成的代码会把各种东西都包含进去,这部分用于控制代码生成的编译指令 p136

# 表面着色器函数的语法结构

1
2
3
void surf(Input IN,inout SurfaceOutput o){
//表面着色器代码
}

# 输入结构体 Input

变量

说明

float2 uv_texName、float2 uv2_texName

uv 关键词后接纹理的名称,获取贴图的第一套纹理坐标,uv2 表示第二套纹理坐标,依次类推

float3 viewDir

摄像机视角方向,可以用于计算观察效果,边缘光照等,没有被标准化

使用 COLOR 语义定义的 float4 变量

插值后的顶点颜色

float4 screenPos

屏幕空间坐标,可用那个鱼反射或屏幕空间特效,但不适用于 GrabPass, 需要使用 ComputeGrabScreenPos 函数单独计算 uv

float3 worldPos

世界空间坐标

float3 worldRefl

世界空间反射向量,前提是没有修改表面法线 o.Normal

float3 worldNormal

世界空间法线向量,前提没有没有修改表面法线

float3 worldRefl; INTERNAL_DATA

如果表面法线 o.Normal 进行了修改,在表面函数中通过 WorldReflectionVector (IN.o,Normal 得到基于法线贴图的世界空间反射向量)

float3 worldNormal; INTERNAL_DATA

如果表面法线 o.Normal 进行了修改,在表面函数中通过 WorldNormalVector (IN.o,Normal 得到基于法线贴图的世界空间反射向量)

# 输出结构体

1
struct SurfaceOutput