在入门系列最后一节中可以看到输出结果,尽管模型是一个立方体,但我们看到的就是一坨,为了丰富细节,会模拟光照使模型表面更加明显,就如同在现实生活中我们看到物体时是借助光线,在Shader也是同样。
1.1 自发光:
在上一篇文章中已经指明,向外暴露的_Color属性即是物体的自发光。
1.2 环境光:
绝大多数物体都是没有自发光特性的,因此光照模型计算一般不会让自发光参与,想要计算自发光也可以,只需在最后将自发光颜色与环境光颜色相加后输出。
Shader "Custom/Test0"
{
Properties
{
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Color;
float4 vert(float4 vertex:POSITION):SV_POSITION
{
//坐标转换
return UnityObjectToClipPos(vertex);
}
fixed4 frag():SV_Target
{
//使用内置宏来得到Unity的环境光颜色并返回
return fixed4(UNITY_LIGHTMODEL_AMBIENT.rgb,1);
}
ENDCG
}
}
}
1.3 环境光+漫反射:
注意环境光不应为白色附近,否则无法得到正确光照结果,建议为环境光为黑色,场景中的平行光为白色。
漫反射光照公式:
是光源颜色,
是材质的漫反射颜色,
为表面法线的方向,
为指向光源的方向,注意本系列除特外说明,所用到的方向全是单位矢量。
1.3.1 逐顶点光照:
Shader "Custom/Test1"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 color :COLOR;
};
v2f vert(a2v i)
{
v2f o;
o.vertex=UnityObjectToClipPos(i.vertex);
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal = UnityObjectToWorldNormal(i.normal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//向量在同一空间下,计算才有意义
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*
saturate(dot(worldLight,worldNormal));
o.color=ambient+diffuse;
return o;
}
fixed4 frag(v2f i):SV_Target
{
//将已经插值过的颜色输出
return fixed4(i.color,1);
}
ENDCG
}
}
}
1.3.2 逐像素光照:
Shader "Custom/Test2"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 vertex:SV_POSITION;
float3 worldNormal :COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*
saturate(dot(worldLight,worldNormal));
return fixed4(ambient+diffuse,1);
}
ENDCG
}
}
}
1.3.3 半兰伯特模型:
Shader "Custom/Test2"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
float3 worldNormal :COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*
(dot(worldLight,worldNormal)*0.5+0.5);
return fixed4(ambient+diffuse,1);
}
ENDCG
}
}
}
左一侧为逐顶点,左二为逐像素,左三为半兰伯特。
区别:逐像素的光照因为计算频率更高,得到的效果也普遍更好。
1.4 环境光+漫反射+高光反射:
1.4.1 逐顶点:
Shader "Custom/Test1"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
_Specular("高光反射系数",Color)=(1,1,1,1)
_Gloss("光泽度",Range(0,256))=20
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
fixed _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 color :COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
//漫反射计算
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*
saturate(dot(worldLight,worldNormal));
//高光反射计算
fixed3 reflectDir=normalize(reflect(-worldLight,v.normal));
fixed3 viewDir=normalize(WorldSpaceViewDir(v.vertex));
fixed3 specular=_LightColor0*_Specular*
pow(saturate(dot(viewDir,reflectDir)),_Gloss);
o.color=ambient+diffuse+specular;
return o;
}
fixed4 frag(v2f i):SV_Target
{
return fixed4(i.color,1);
}
ENDCG
}
}
}
1.4.2 常规逐像素:
Shader "Custom/Test2"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
_Specular("高光反射系数",Color)=(1,1,1,1)
_Gloss("光泽度",Range(0,256))=20
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
fixed _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 vertex:SV_POSITION;
float3 worldNormal :TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//unity有一个内置API,只能负责向量变化,顶点不能使用
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb
*saturate(dot(worldLight,worldNormal));
//高光反射计算
fixed3 reflectDir=normalize(reflect(-worldLight,i.worldNormal));
fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
fixed3 specular=_LightColor0.rgb*_Specular
*pow(saturate(dot(viewDir,reflectDir)),_Gloss);
return fixed4(ambient+diffuse+specular,1);
}
ENDCG
}
}
}
1.4.3 Blinn-Phong逐像素:
Shader "Custom/Test2"
{
Properties
{
_Diffuse("漫反射系数",Color)=(1,1,1,1)
_Specular("高光反射系数",Color)=(1,1,1,1)
_Gloss("光泽度",Range(0,256))=20
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
fixed _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 vertex:SV_POSITION;
float3 worldNormal :TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//unity有一个内置API,只能负责向量变化,顶点不能使用
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb
*saturate(dot(worldLight,worldNormal));
//高光反射计算
fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
fixed3 halfDir=normalize(viewDir+worldLight);
fixed3 specular=_LightColor0.rgb*_Specular
*pow(saturate(dot(worldNormal,halfDir)),_Gloss);
return fixed4(ambient+diffuse+specular,1);
}
ENDCG
}
}
}
左一侧为逐顶点,左二为常规逐像素,左三为Blinn-Phong。
一般使用Blinn-Phong光照模型的更多。逐顶点的高光部分表现并不好,主要是高光计算是非线性的,而插值过程是线性的,非线性的计算会破坏线性的插值,使得线性的结果变的非线性,也就导致高光不平滑。