高光(Specular):模拟有光泽物体表面上的亮点,其颜色更倾向于光的颜色而不是物体表面的颜色。
Blinn-Phong specular 模型
vec3 viewDir;
vec3 halfDir = normalize(lightDir + viewDir);
//specPower 镜面高光系数
//I为入射光颜色向量,k为镜面反射系数
float spec = I * k * pow(max(dot(halfDir, normal), gloss), specPower);
半角向量,用H表示。半角向量计算简单,通过将光源方向L和视线方向V相加后归一化即可得到半角向量.
Phong specular模型
vec3 viewDir;
//需要光线方向为由光源指出
vec3 reflectDir = normalize(reflect(-lightDir, normal));
//I为入射光颜色向量,k为镜面反射系数
float spec = I * k * pow(max(dot(reflectDir, viewDir), gloss), specPower);
镜面反射强度跟反射向量与观察向量的余弦值呈指数关系,指数为gloss,该系数反映了物体表面的光滑程度。
1.该值越大,表示物体越光滑,反射光越集中。当偏离反射方向时,光线衰减程度越大,只有当视线方向与反射光方向非常接近时才能看到高光现象,镜面反射光形成的光斑较亮并且较小。
2.该值越小,表示物体越粗糙,反射光越分散,可以观察到光斑的区域越广,光斑大并且强度较弱。
计算由反射向量R和观察向量V决定,当两者的夹角a超过90时,截断为0.0,则没有了镜面光成分.
Phong specular模型能处理的是下面的左图中(a≤90)的情况,而对于右图中(a>90)的情况则镜面光成分计算为0
RimLight边缘发光:
在当前视角方向,物体上位于边缘的地方额外加一个光的效果。 通过法线方向和视线方向的夹角来判断,一个点是否在物体的边缘。
1.当视线方向V与法线方向N垂直时,这个法线对应的面就与视线方向平行,说明当前这个点对于当前视角来说,就处在边缘。dot(N,V)越接近0,此处有边缘光。
2.当视线方向V与法线方向N一致时,这个法线对应的面就垂直于视线方向,说明当前是直视这个面。dot(N,V)越接近1,边缘光越弱。
shader基本参数如下:
Shader "S_Game_Hero/Hero_Show_Rim_Dissolve"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_RimColor("边缘光Rim Color",Color) = (1,1,1,1)
_RimPower ("Rim Power",range(1,10)) = 2
_MaskTex ("高光Mask (R,G,B)", 2D) = "white" {}
_SpecColor ("Spec Color", Color) = (0,0,0,0)
_SpecPower ("Spec Power", Range(1, 128)) = 15
_SpecMultiplier ("Spec Multiplier", Float) = 1
_LightTex ("轮廓光Light (RGB)", 2D) = "white" {}
_LightColor ("Spec Color", Color) = (0,0,0,0)
_HurtColor ("HurtColor", Color) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uv1:TEXCOORD1;
float2 uv2:TEXCOORD2;
float2 uv3:TEXCOORD3;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 angle : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _MaskTex;
float4 _MaskTex_ST;
sampler2D _LightTex;
float4 _LightTex_ST;
float _RimPower;
float4 _RimColor;
float4 _SpecColor;
float _SpecPower;
float _SpecMultiplier;
float4 _LightColor;
float4 _HurtColor;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 viewDir = UnityWorldSpaceViewDir(v.vertex);
//视角方向从世界坐标到模型坐标的转换
viewDir = mul((float3x3)unity_WorldToObject,viewDir);
float3 vieNormal = normalize(viewDir);
//用视角方向和法线方向做点乘,越边缘的地方,法线和视角方向越接近90度,点乘越接近0.
o.angle.x = saturate(dot(v.normal,vieNormal));
//平行光源的入射向量L 给个假的
float3 lightDirection = normalize(float3(0.0, 0.0, 2.0));
//镜面反射光的计算
o.angle.y = max(0.0,dot(reflect(-lightDirection, v.normal),vieNormal));
//UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv) + _HurtColor;
//轮廓光
fixed4 albedo = tex2D(_LightTex, i.uv) * _LightColor;
col.rgb += albedo;
//光泽度 值越高光泽度越高,1为极值,物极必反
float gloss = tex2D(_MaskTex, i.uv.xy).x;
//镜面反射
fixed3 reflectAngle = i.angle.y*0.5 + 0.5;
//SpecColor高光
col.rgb += _SpecColor * pow (reflectAngle, _SpecPower ) * gloss * _SpecMultiplier * 2.0;
//用(1- 上面点乘的结果)*颜色,来反映边缘颜色情况
float rim = 1 - i.angle.x;
col.rgb += saturate(pow(rim ,_RimPower)*(1+_RimPower))* _RimColor.rgb;
// apply fog
//UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
左图无光照,右图为处理过光照的模型渲染效果
参考资料: