V为从顶点指向摄像机的向量,L为从光源指向顶点的向量,R为反射向量
求反射向量R的两种方式 :
1、CG中Reflect函数,可以直接计算出反射向量R
R = Reflect(I , N);
// 参数 :入射光向量I :顶点指向光源的向量,即L的反向量 、 顶点的法向量 注 :都是在世界坐标系中的位置
2、L + R = 2 * cos(N , L) * N ,当N、L都为单位向量时,cos(N,L) = dot(N,L) 是一个标量值,所以要乘上N,得到向量
R = 2 * dot(N,L) * N - L
如何证明高亮:
当V与R重合的时候,最为高亮。所以,求得R与V的夹角,即可判断这个顶点是否有高光效果(光滑)
Phong模型 :
环境光照(Ambient) + 漫反射光照(Diffuse) + 镜面高光(Specular)
L :
辅助函数WorldSpaceLightDir :参数为顶点在自身坐标系中的位置,函数返回值为 :在世界坐标系中,顶点指向光源位置的向量,所以求入射光线的向量L需要取反
N :
辅助函数UnityObjectToWorldNormal(v.normal),将法向量从自身坐标系变换到世界坐标系中,内部做了向量的规范化。
V :
辅助函数WorldSpaceViewDir(v.vertex) :求从顶点指向摄像机的向量,传入一个自身坐标系中的顶点的位置,返回一个世界坐标系中的从顶点指向摄像机的向量
Blinnphong与半角向量 :因为计算反射向量R时,也使用了一次dot操作,运行速度慢,所以可以使用Blinnphong模型和半角向量
H :
半角向量,L + V 得出的向量
float3 H = L + V;
H = normalize(H);
Shader "Custom/MySpecular" {
Properties
{
_SpecularColor("_Specular" , Color) = (1,1,1,1) // 高亮光颜色
_Shininess("Shininess" , range(1,64)) = 8 // 高亮系数
}
SubShader
{
Pass
{
tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "unitycg.cginc"
#include "lighting.cginc"
float4 _SpecularColor;
float _Shininess;
struct v2f {
float4 pos : POSITION;
fixed4 color : COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 L = normalize(WorldSpaceLightDir(v.vertex)); //世界坐标系中,从顶点指向光源的向量
float3 N = UnityObjectToWorldNormal(v.normal); // 把顶点的法向量,从自身坐标系中变换到世界坐标系中
float3 V = normalize(WorldSpaceViewDir(v.vertex)); //世界坐标系中,从顶点指向摄像机的向量
// 1、Ambient Color
o.color = UNITY_LIGHTMODEL_AMBIENT;
// 2、Diffuse Color
float ndotl = saturate(dot(N,L));
o.color += _LightColor0 * ndotl ; //平行光的颜色
// 3、Specular Color :高光系数的两种求法 :1、dot(R , V) 2、半角向量
// 1、
求R的两种方式 :(1)、Reflect函数 ; (2)、R = 2 * dot(N,L) * N - L
(1)、
//float3 I = -WorldSpaceLightDir(v.vertex); // 入射光向量I,从顶点指向光源
//float3 R = reflect(I , N);
(2)、
float3 R = 2 * dot(N , L) * N - L;
R = normalize(R);
float specularScale = pow(saturate(dot(R,V)) , _Shininess);
// 2、多用半角向量,运算速度快
float3 H = L + V ;
H = normalize(H);
float specularScale = pow(saturate(dot(H , N)) , _Shininess) ;
o.color.rgb += _SpecularColor * specularScale;
return o ;
}
fixed4 frag(v2f IN) : COLOR
{
return IN.color ;
}
ENDCG
}
}
}
Phong模型 :要计算反射向量
Blinnphong模型 :要计算半角向量
计算这两个向量,都是为了计算高光
L :
辅助函数WorldSpaceLightDir :参数为顶点在自身坐标系中的位置,函数返回值为 :在世界坐标系中,顶点指向光源位置的向量,所以求入射光线的向量L需要取反
N :
辅助函数UnityObjectToWorldNormal(v.normal),将法向量从自身坐标系变换到世界坐标系中,内部做了向量的规范化。
V :
辅助函数WorldSpaceViewDir(v.vertex) :求从顶点指向摄像机的向量,传入一个自身坐标系中的顶点的位置,返回一个世界坐标系中的从顶点指向摄像机的向量