1、概述
该模型是一种模拟物体在光线下看起来什么样的方法,它把光分成3部分:环境光、漫反射光、镜面高光。
2、举例说明
拿着一个苹果站在灯下:
- 整个房间是亮的,所以苹果不会完全黑掉 -> 环境光(Ambient)
- 苹果被灯光照到的一面比较亮 -> 漫反射(Diffuse)
- 苹果表面有个闪闪发亮的小点(像镜子一样反光) -> 镜面高光(Specular)
(1)环境光
想象你在室内,虽然灯不是正对着苹果,但苹果也不是全黑的。因为墙、地板、天花板会反射一些光,所以苹果还是有点亮。Phong用环境光来模拟“整体亮度”。
(2)漫反射
比如你用手电筒照苹果,正对着手电筒的那一面最亮。偏一点的角度,就暗一些。这个光是物体表面均匀反射的光,不光滑、不闪亮,但能看清物体的形状。
(3)镜面高光
苹果表面光滑的地方会出现一个“亮点”,这个亮点会随着你眼睛的位置变化而变化。越光滑的物体(比如玻璃、金属),这个高光越亮越集中。
用Phong模型可以:
- 让角色在灯光下有明暗变化(漫反射)
- 表面光滑的皮肤、金属武器的反光(镜面高光)
- 让整个场景看起来不会太黑(环境光)
3、公式解读
(1)环境光部分
I_a = K_a * I_light
- K_a:材质对环境光的反射能力(比如塑料、金属、纸张不一样)
- I_light:环境光的强度(比如房间灯光)
float k_a = 0.6; // a value of our choice, typically between zero and one
vec3 i_a = vec3(0.7, 0.7, 0); // a color of our choice
vec3 ambient = k_a * i_a;
(2)漫反射部分
I_d = K_d * I_light * cosθ
- K_d:材质的漫反射系数(越粗糙越容易漫反射)
- I_light:光源强度
- θ:光线和物体表面法线的夹角(cosθ就是点积)
- cosθ = dot(N, L),N是法线的单位向量,L是光源方向的单位向量
vec3 p = ro + rd * d; // point on surface found by ray marching
vec3 N = calcNormal(p); // surface normal
vec3 lightPosition = vec3(1, 1, 1);
vec3 L = normalize(lightPosition - p);
float k_d = 0.5; // a value of our choice, typically between zero and one
vec3 dotLN = dot(L, N);
vec3 i_d = vec3(0.7, 0.5, 0); // a color of our choice
vec3 diffuse = k_d * dotLN * i_d;
(3)镜面反射部分
I_s = K_s * I_light * (cosα)^n
- K_s:材质的镜面反射系数(越光滑越容易反光)
- α:视线方向和反射方向的夹角
- n:光泽度(越大,高光越集中)
vec3 p = ro + rd * d; // point on surface found by ray marching
vec3 N = calcNormal(p); // surface normal
vec3 lightPosition = vec3(1, 1, 1);
vec3 L = normalize(lightPosition - p);
float k_s = 0.6; // a value of our choice, typically between zero and one
vec3 R = reflect(L, N);
vec3 V = -rd; // direction pointing toward viewer (V) is just the negative of the ray direction
vec3 dotRV = dot(R, V);
vec3 i_s = vec3(1, 1, 1); // a color of our choice
float alpha = 10.;
vec3 specular = k_s * pow(dotRV, alpha) * i_s;
vec3 phong(vec3 lightDir, vec3 normal, vec3 rd) {
// ambient
float k_a = 0.6;
vec3 i_a = vec3(0.7, 0.7, 0);
vec3 ambient = k_a * i_a;
// diffuse
float k_d = 0.5;
float dotLN = clamp(dot(lightDir, normal), 0., 1.);
vec3 i_d = vec3(0.7, 0.5, 0);
vec3 diffuse = k_d * dotLN * i_d;
// specular
float k_s = 0.6;
float dotRV = clamp(dot(reflect(lightDir, normal), -rd), 0., 1.);
vec3 i_s = vec3(1, 1, 1);
float alpha = 10.;
vec3 specular = k_s * pow(dotRV, alpha) * i_s;
return ambient + diffuse + specular;
}