unity中表面着色器实际是对顶点/片元着色器的抽象
Shader "Unity Shaders Book/Chapter 17/Bumped Diffuse" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300
CGPROGRAM
#pragma surface surf Lambert
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
struct Input {
float2 uv_MainTex;//必须以uv开头
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = tex.rgb * _Color.rgb;
o.Alpha = tex.a * _Color.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
FallBack "Legacy Shaders/Diffuse"
}
效果:
表面着色器重要的是两个结构体和编译指令。
编译指令
指明表面着色器使用的表面函数和光照函数,并设置一些可选参数:
#pragma surface surfaceFunction lightmode [optionalparams]
表面函数:
void surf (Input IN , inout SurfaceOutput o); void surf (Input IN , inout SurfaceOutputStandard o); void surf (Input IN , inout SurfaceOutputStandardSpecular o);
光照函数:
光照函数会使用表面函数中设置的各种表面属性,来应用某些光照模型,进而模拟物体表面的光照模型,使用内置的也可以自己定义。
还有一些其他可选参数:自定义的的修改函数、阴影、透明度混合和透明度测试、光照、控制代码的生成。
两个结构体
表面着色器最多自定义4种关键的函数:表面函数(用于设置各种表面性质,如反射率、法线等)、光照函数(定义表面使用的光照模型)、顶点修改函数(修改顶点或传递顶点属性)、最后的颜色修改函数(对最后的颜色进行修改)。
函数之间的信息传递通过结构体:表面函数的输入结构体Input,存储了表面属性的结构体SurfaceOutput,还有SurfaceOutputStandard 、SurfaceOutputStandardSpecular 。
input结构体:
SurfaceOutput结构体:Input结构体提供的数据用来计算表面属性然后存储到SurfaceOutput结构体中。作为表面函数的输出,随后作为光照函数的输入来计算各种光照。该类结构体变量不可增加也不会减少,没有赋值 的就用默认值:
表面着色器实例分析
实现效果:对模型进行膨胀,在顶点修改函数中沿着法线方向扩张顶点位置。
shader代码:
Shader "Unity Shaders Book/Chapter 17/Normal Extrusion" {
Properties {
_ColorTint ("Color Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
_Amount ("Extrusion Amount", Range(-0.5, 0.5)) = 0.1
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300
CGPROGRAM
// surf - which surface function.
// CustomLambert - which lighting model to use.
// vertex:myvert - use custom vertex modification function.
// finalcolor:mycolor - use custom final color modification function.
// addshadow - generate a shadow caster pass. Because we modify the vertex position, the shder needs special shadows handling.
// exclude_path:deferred/exclude_path:prepas - do not generate passes for deferred/legacy deferred rendering path.
// nometa - do not generate a “meta” pass (that’s used by lightmapping & dynamic global illumination to extract surface information).
#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
#pragma target 3.0
fixed4 _ColorTint;
sampler2D _MainTex;
sampler2D _BumpMap;
half _Amount;
//表面函数输入结构体
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
//自定义顶点修改函数
void myvert (inout appdata_full v) {
v.vertex.xyz += v.normal * _Amount;//对顶点位置按照法线方向进行膨胀
}
//自定义表面函数
void surf (Input IN, inout SurfaceOutput o) {
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = tex.rgb;//使用主纹理设置反射率
o.Alpha = tex.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));//使用法线纹理设置表面法线方向
}
//自定义光照函数
half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot(s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);//兰伯特漫反射光照模型
c.a = s.Alpha;
return c;
}
//自定义最后的颜色修改函数
void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {
color *= _ColorTint;
}
ENDCG
}
FallBack "Legacy Shaders/Diffuse"
}
效果:
然后可以点击show generated code来查看表面着色器实际生成的shader代码:
表面着色器的缺点
性能较差且自定义程度低,优化程度不够。