0x00 Surface Shader 常用的三种输出数据格式
//标准输出,若自定义输出必须带上以下所列参数 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 }; //基于物理光照模型标准输出 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 };
0x01 Surface Shader compile directives
Surface shader编码在CGPROGRAM...ENDCG块内
- 必须在Subshader块内而不能在pass内部,Surface shader将自己编译成多个通道.
- 必须使用#pragma surface surfaceFunction lightModel [optionalparams] 指令来表明这是surface shader.
#pragma surface surfaceFunction lightModel [optionalparams]
蓝色必要要参数:require params
格式void surf (Input IN, inout SurfaceOutput o),Input自定义结构常必须包含纹理坐标和其他自需变量 |
|||||||
|
|
surfaceFunction 中Input参数
//surfaceFunction 中Input参数纹理坐标必须带uv或uv2
struct Input { //纹理坐标用uv_或uv2_前缀指示,必须! float2 uv_myMainTex;//uv_XXX; //---以下为可选--- float3 viewDir; //视图方向为了计算时差、边缘光照等效果,Input需要包含视图方向 float4 color; //每个顶点颜色值 float4 screenPos; //屏幕空间位置,为了获得反射效果 float3 worldPos; //世界坐标空间 float3 worldRefl; //世界空间反射向量,surface shader不能写入o.Normal参数 float3 worldNormal;//世界空间法向量,surface shader不能写入o.Normal参数 //世界坐标反射向量,surface shader必须写入o.Normal参数 //基于逐像素法线贴图获得反射向量,请使用WorldReflectionVector(IN,o.Normal) float3 worldRefl;INTERNAL_DATA; //世界坐标法线向量,surface shader必须写入o.Normal参数 //基于逐像素法线贴图获得反射向量,请使用WorldReflectionVector(IN,o.Normal) float3 worldNormal;INTERNAL_DATA; }
橙色可选参数:optional params
Transparency and alpha testing 使用alpha and alphatest指令 |
|
|||||||||||||||||||||||||||
Custom modifier functions用于计算更改输入的顶点数据,或更改最终计算的片元色 |
|
|||||||||||||||||||||||||||
Shadows and Tessellation 阴影和网格细分 |
|
|||||||||||||||||||||||||||
Code generation options 调整生成着色器代码选项,使得shader更小加载更快 |
|
|||||||||||||||||||||||||||
Miscellaneous options 各种其他选项 |
|
表面着色器的工作原理
0x02 Surface Shader Light Model
直接照明和间接照明,间接照明可以理解为光照烘焙。下面是Unity自带的直接照明模型:
Lighting.cginc表
UnityLambertLight |
基于非物理的漫反射 |
LightingLambert |
|
//.. |
|
//.. |
|
UnityBlinnPhongLight |
基于非物理的镜面高光 |
LightingBlinnPhong |
//.. |
LightingBlinnPhong_Deferred |
//.. |
LightingBlinnPhong_PrePass |
//.. |
LightingLambert_GI |
//.. |
LightingBlinnPhong_GI |
//.. |
自定义光照函数表及说明:
格式:Lighting+ [自定义部分] + [_path],//要去掉中括号, _path是可选的,例如
LightingMyPhong在forward path路径;LightingMyPhong_Deffered在deffered自动识别后缀,不加默认是forward path.
//orward path,不依赖视线方向向量: half4 Lighting<Name> (SurfaceOutput s, UnityGI gi) //在forward path,依赖视线方向向量 //half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, UnityGI gi) //在deferred lighting paths half4 Lighting<Name>_Deferred ( SurfaceOutput s, //标准输出结构 UnityGI gi, //全局光照 out half4 outDiffuseOcclusion, //漫反射rgb+剔除alpha组合 out half4 outSpecSmoothness, //高光rgb+平滑alpha组合 out half4 outNormal //法线 )//后面三个参数在UnityStandardDataToGbuffer计算编码传递给GBuffer //在Use this in light prepass (legacy deferred) lighting paths. //in light prepass (legacy deferred) lighting paths half4 Lighting<Name>_PrePass (SurfaceOutput s, half4 light) //自定义GI解析光照贴图和探针数据 half4 Lighting<Name>_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi)
自带常用Lambert and BlinnPhong lighting models
漫反射和Lambert模型
inline fixed4 UnityLambertLight (SurfaceOutput s, UnityLight light){ fixed diff = max (0, dot (s.Normal, light.dir));//法线和光的入射方向的夹角得到漫反射值 fixed4 c; c.rgb = s.Albedo * light.color * diff;//表面色*光色*反射(强度值) c.a = s.Alpha; return c; }
镜面高光BlinnPhong模型
inline fixed4 UnityBlinnPhongLight (SurfaceOutput s, half3 viewDir, UnityLight light){ half3 h = normalize (light.dir + viewDir);//视线方向+光线方向=半角方向 fixed diff = max (0, dot (s.Normal, light.dir));//得到与光照相关的漫反射 float nh = max (0, dot (s.Normal, h));//得到与视线相关的漫反射 float spec = pow (nh, s.Specular*128.0) * s.Gloss;//与光滑程度有关 fixed4 c; c.rgb = s.Albedo * light.color * diff + light.color * _SpecColor.rgb * spec; c.a = s.Alpha; return c; }
0x03 Example
自带的两个光照模型对比:
把SurfaceShader中光照函数翻译为VFShader
Lambert -逐像素
struct v2f { float4 pos :SV_POSITION; float2 uv :TEXCOORD0; float3 lightDir :TEXCOORD1; float3 normal :TEXCOORD2; }; v2f m_vert(appdata_full v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); //Transforms 2D UV by scale/bias property //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw) o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // Computes object space light direction o.lightDir = ObjSpaceLightDir(v.vertex); o.normal = v.normal; return o;
} fixed4 m_frag(v2f i) : COLOR{ float4 c = tex2D(_MainTex,i.uv); i.lightDir = normalize(i.lightDir); i.normal = normalize(i.normal); float diff = max(0,dot(i.normal,i.lightDir)); return c * _LightColor0 * diff; }
BlinnPhong -逐像素
struct v2f { float4 pos :SV_POSITION; float2 uv :TEXCOORD0; float3 lightDir :TEXCOORD1; float3 viewDir :TEXCOORD2; float3 normal :TEXCOORD3; }; v2f m_vert(appdata_full v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); //Transforms 2D UV by scale/bias property //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw) o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // Computes object space light direction o.lightDir = ObjSpaceLightDir(v.vertex); o.viewDir = ObjSpaceViewDir(v.vertex); o.normal = v.normal; return o; } fixed4 m_frag(v2f i) : COLOR{ float4 c = tex2D(_MainTex,i.uv); i.lightDir = normalize(i.lightDir); i.viewDir = normalize(i.viewDir); i.normal = normalize(i.normal); half3 h = normalize(i.lightDir + i.viewDir); float nh = max(0, dot(h, i.normal)); float diff = max(0, dot(i.normal, i.lightDir)); float spec = pow(nh, _Specular * 128) * _Gloss; c.rgb = c.rgb * _LightColor0.rgb * diff + _SpecColor.rgb * spec; return c; }