
簡介
在3D計算機圖形學中,Phong着色是計算機圖形學先驅Bui Tuong Phong發明的一種用於表面着色的插值技術。也稱為Phong插值或法向矢量插值陰影。它會在柵格化的多邊形上內插表面法線,並根據內插法線和反射模型計算像素顏色。Phong陰影也可以指Phong插值和Phong反射模型的特定組合。
主要過程:
- 計算多邊形頂點的法向量
- 雙線性插值計算每個像素點的法向量
- 通過每個像素的法向量計算光強
- 根據光強繪制像素
歷史:
在1975年,由Phong提出,以他的名字冠名,是一種局部光照的模型。
Phong着色法與Gouraud着色法 比較
Phong着色法與Gouraud着色法類似,區別在於進行雙線性插值的不是光照強度本身,而是頂點的法線。因此使用這種着色法計算出的高光比Gouraud着色更精確。Phong着色法與Gouraud著色法比較,Phong著色法的效果更逼真,能夠提供更好的光滑曲面的近似值。Phong著色法假設一個平滑變化的曲面為一矢量。
在對於有較小的高光曲線區的反射模型,例如Phong模型時,Phong著色法比Gouraud著色法更優。但運算程序也比前者為復雜。Gouraud著色法在遇到在較大的多邊形模型中央有高光曲線區時會產生嚴重的問題。因為這些高光曲線區在多邊形的頂點處會產生缺失而Gouraud著色法是基於頂點的顏色的,這些高光曲線區會從多邊形的內部缺失。這個問題在Phong著色法中得到了解決。不同於通過多邊形差值的Gouraud著色法,Phong著色法中一個矢量是從多邊形頂點的法線到多邊形表面進行差值的。為了或得到最后的像素顏色,面的法線被差值,應用於一個反射模型。由於Phong著色法需要逐像素點進行計算,因此運算量遠大於Gouraud著色法。
Phong光照模型是真實圖形學中提出的第一個有影響的光照明模型,該模型只考慮物體對直接光照的反射作用,認為環境光是常量,沒有考慮物體之間相互的反射光,物體間的反射光只用環境光表示。Phong光照模型屬於簡單光照模型。
基本概念
Phong模型認為物體表面反射光線由三部分組成:
- 環境光(Ambient):場景中的其他間接光照
- 漫反射(Diffuse):散射部分(大但不光亮)
- 高光反射(Specular):鏡面反射部分(小而亮)
數學基礎
平面法線計算
給定一個多邊形上任意三個非共線的點,P0 P1 P2,計算該平面的法線:
反射向量計算
半角向量計算
平均分割了一個角的向量
公式推導
光照最終結果=環境光照+漫反射光照+鏡面反射光照
- 法向量(Normal vector):\(n\)
- 視口向量(View vector):\(v\)
- 光源向量(Light vector):\(l\)
- 反射向量(Reflection vector):\(r\)
環境光 Ambient
環境光照是考慮到即使在不可見光源的地方,經過各種反射,總會帶有微弱的光照。光線追蹤的算法會更加准確,但是在這里采用一種簡化的方法。就是將光源的顏色,乘以一個很小的系數,再乘以物體的顏色。
由於環境光計算過於復制,並難以計算。在Phong模型中環境光被簡化為一個環境光反射系數和環境光光照強度的乘積:
- 環境光ambient=環境光反射系數*環境光光照強度
漫反射 Diffuse
漫反射(簡稱漫射,英語:diffuse reflection)是指當一束平行的入射光線射到粗糙的表面時,粗糙的表面會把光線向着各個方向反射的現象。雖然入射線互相平行,由於粗糙的表面上的各點的法線方向不一致,造成反射光線向不同的方向無規則地反射。這種反射的光稱為漫射光。很多物體,如植物、牆壁、衣服等,其表面粗看起來似乎是平滑,但用放大鏡仔細觀察,就會看到其表面是凹凸不平的,所以本來是平行的太陽光被這些表面反射后,彌漫地射向不同方向。
- 漫反射亮度受光線入射方向\(l\)與單位表面的法線\(n\)夾角影響
- 受\(l\)和\(n\)的夾角的影響,夾角為0時光照最亮,夾角為90度時無光照
- 如果$ \vec{n} \cdot \vec{l} <0$ ,代表光照方向和法線方向相反,此時不做光照處理:$m a x ( 0 , \vec{n} \cdot l ) $
高光反射 Specular
鏡面反射是指若反射面比較光滑,當平行入射的光線射到這個反射面時,仍會平行地向一個方向反射出來,這種反射就屬於鏡面反射
當一束平行光觸及光滑物體表面時,光線則發生規律性反射,反射后的光線也相互平行,這種規律性反射稱為光的單向反射或鏡面反射。但物體的光滑程度是相對的,而一般物體的表面多粗糙不平,入射線雖然為平行光線,但反射后的光線則向各個方向分散,此種現象為光的漫反射。
- 取反射方向和視口方向的夾角作為高光區域的判定
- 指數系數\(s\): 如下圖所示,控制高光區域的大小
Phong 模型
Blinn-Phong光照模型
Blinn-Phong光照模型在物理上的正確性不必Phong高,但計算效率提高了很多。當觀察向量與反射向量越接近,那么半角向量與法向量N越接近,觀察者看到的鏡面光成分越強。
Blinn-Phong 計算方程
Blinn-Phong和Phong對比
//TODO:
GLSL實現
Phong
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
Blinn-Phong
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
// declare an interface block; see 'Advanced GLSL' for what these are.
out VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
void main()
{
vs_out.FragPos = aPos;
vs_out.Normal = aNormal;
vs_out.TexCoords = aTexCoords;
gl_Position = projection * view * vec4(aPos, 1.0);
}
#version 330 core
out vec4 FragColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
} fs_in;
uniform sampler2D floorTexture;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform bool blinn;
void main()
{
vec3 color = texture(floorTexture, fs_in.TexCoords).rgb;
// ambient
vec3 ambient = 0.05 * color;
// diffuse
vec3 lightDir = normalize(lightPos - fs_in.FragPos);
vec3 normal = normalize(fs_in.Normal);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color;
// specular
vec3 viewDir = normalize(viewPos - fs_in.FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = 0.0;
if(blinn)
{
vec3 halfwayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
}
else
{
vec3 reflectDir = reflect(-lightDir, normal);
spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);
}
vec3 specular = vec3(0.3) * spec; // assuming bright white light color
FragColor = vec4(ambient + diffuse + specular, 1.0);
}
相關鏈接
Illumination I: The Phong Illumination Model pdf
Blinn–Phong reflection model Blinn–Phong GLSL多光源實現
CS315: Illumination 2 使用WebGL實現了一些效果
代碼實現
OpenGL: Blinn-Phong model implemented in shaders give wrong result
DirectX10 Tutorial 8: Lighting Theory and HLSL
HLSL per-pixel point light using phong-blinn lighting model
Lambert、Phong、BlinnPhong光照模型(逐頂點+逐像素計算)
論文
Bui Tuong Phong published his illumination model in 1973: "Illumination for Computer-Generated Images".
