背景
这里的高光反射模型,并不是现实意义上的高光反射,而是只是用来计算物理上那些沿着完全镜面反射方向被反射的光线,让物体看起来有光泽,例如金属材质
高光反射计算公式如下
逐顶点光照
Shader "Test Shader/SpecularVertexLevel{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
//控制高光反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1)
//控制高光区域大小
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 color:COLOR;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//利用Cg内置反射光线方向计算函数计算反射光线
fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
//mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
//视角方向=摄像头位置-顶点位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
//根据公式计算高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
o.color=ambient+diffuse+specular;
return o;
}
fixed4 frag(v2f i):SV_Target
{
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "Specular"}
这里高光部分明显不平滑,这是因为高光反射的计算是非线性的,而在顶点着色器中计算光照再进行差值的过程是线性的,破坏了原有的视觉问题
逐像素光照
Shader "Test Shader/SpecularPixelLevel"{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
//控制高光反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1)
//控制高光区域大小
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//利用Cg内置反射光线方向计算函数计算反射光线
fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
//mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
//视角方向=摄像头位置-顶点位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//根据公式计算高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
FallBack "Specular"}
这样的光照反射模型更平滑,而这一光照模型就是著名的phong光照模型
Blinn-Phong光照模型
blinn模型没有使用反射方向。而是引入了矢量h
Shader "Test Shader/BlinnPhone"{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
//控制高光反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1)
//控制高光区域大小
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
//视角方向=摄像头位置-顶点位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//计算矢量h
fixed3 h=normalize(worldLight+viewDir);
//根据公式计算高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, h)), _Gloss);
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
FallBack "Specular"}
这样的高光反射模型反射部分更大更亮些,大多数情况下,我们都会选择这一高光反射模型
最后结果如下
从右到左分别是逐顶点光照,逐像素光照phong模型,blinn模型