Phong 1975 — 把光照拆成环境+漫反射+高光三项
是什么
Phong 1975 提出一个让屏幕上的物体看起来像被光照过的最小公式。日常类比:你画一个苹果,光打在上面。苹果整体不死黑(背面也能看清),亮面有渐变(侧面比正面暗一点),最亮处有一个白点(高光)——这三件事,Phong 拆成三项可计算的数学量。
公式只有 8 行:
I = ka·ia + Σ over lights [ kd·(L·N)·id + ks·(R·V)^n·is ]逐项读:
ka·ia—— 环境光(ambient):常数项,让背面别死黑kd·(L·N)·id—— 漫反射(diffuse):光源方向 L 和法线 N 的夹角越正,越亮ks·(R·V)^n·is—— 高光(specular):反射方向 R 和视线 V 越对齐,越闪
L、N、R、V 都是单位向量;· 是点乘(cos 夹角);n 是光泽度(shininess),数值越大高光越尖。
为什么重要
不理解 Phong,下面这些事都无法解释:
- 为什么 Three.js 的标准材质叫
MeshPhongMaterial、shininess、specular——名字直接来自 1975 这篇 - 为什么所有图形入门课第一个能跑的着色器都是这 8 行
- 为什么 50 年后的 PBR(Cook-Torrance、GGX)还在沿用
diffuse + specular这种拆法 - 为什么 OpenGL 固定管线默认就是 Phong 模型的简化版(实际是 Blinn-Phong,1977 年的优化)
这是计算机图形学第一个进入工业的真实感光照模型。在它之前,1971 年 Gouraud 已经能在多边形之间平滑过渡颜色,但没有高光——苹果看起来像橡皮泥。Phong 加上高光那一刻,CG 才开始像照片。
核心要点
三项每一项对应一种物理现象的粗暴近似:
-
环境光 ka·ia:现实里光会在房间里反弹无数次,每个角落都有一点散射光。Phong 说:“太复杂,加个常数糊弄过去。“——这就是 ambient。物理上不存在,纯 fudge factor,但没它背面就是死黑。
-
漫反射 kd·(L·N)·id:粗糙表面(纸、墙、橡胶)把光均匀往四面八方散。Lambert 1760 年就推过:朝向光源越正(L·N 越大),单位面积接到的光越多。这一项和视角无关——你绕到侧面看,漫反射部分亮度不变。
-
高光 ks·(R·V)^n·is:光滑表面(金属、塑料、湿物)把光集中往反射方向弹。理想镜面只在 R 方向有亮,但真实表面有微小起伏,所以亮度按 cos 衰减。Phong 的关键创新就是这一项——指数 n 控制衰减速度,n=1 像橡皮,n=200 像镜子。这一项强烈依赖视角——你动一下头,高光就漂移。
三项加起来就是这个像素的颜色。每个光源都跑一遍 diffuse + specular,再加一次 ambient。
实践案例
案例 1:Three.js 里的 Phong 材质
const material = new THREE.MeshPhongMaterial({ color: 0xff0000, // kd 漫反射颜色(红色苹果) specular: 0xffffff, // ks 高光颜色(白色高光) shininess: 30, // n 光泽度(30 = 塑料感)});shininess 这个字段名就是 Phong 1975 公式里的 n。改成 200 就像抛光金属,改成 1 就像哑光橡胶。
案例 2:手动拆解一个像素的颜色
红苹果某个像素,光从左上来,相机在正前:
ka = 0.1, ia = (1,1,1) # 环境kd = (0.8, 0.1, 0.1), id = (1,1,1) # 红色漫反射ks = (1,1,1), is = (1,1,1), n = 50 # 白色高光
L·N = 0.7 # 光与法线夹角约 45°R·V = 0.95 # 反射方向几乎对着相机
ambient = 0.1 · (1,1,1) = (0.10, 0.10, 0.10)diffuse = (0.8,0.1,0.1) · 0.7 · 1 = (0.56, 0.07, 0.07)specular = (1,1,1) · 0.95^50 · 1 = (0.08, 0.08, 0.08)
I = (0.74, 0.25, 0.25) # 偏红+一点白这个像素是亮红色加一点白闪。换个角度让 R·V=0.5,0.5^50 ≈ 0.0000009,高光直接消失——这就是高光”会跟着视角跑”的感觉。
案例 3:Phong shading 与 Phong reflection 不是一回事
同一篇论文里两个东西容易混:
- Phong reflection model(本文主角):上面那个 8 行公式
- Phong shading:在多边形顶点之间插值法线,每个像素重新算一次公式
对比 1971 年 Gouraud:Gouraud 在顶点算颜色,多边形内部插颜色——结果高光在多边形中心会丢失。Phong 改成插法线,每像素算一次完整公式,高光保住。代价是 GPU 要做 N 倍计算,1975 年根本跑不动,1990 年代硬件追上后才普及。
踩过的坑
-
能量不守恒:ka + kd + ks 可以随便填超过 1,物体看起来比光源还亮。现代 PBR 强制 kd + ks ≤ 1。
-
金属不准:Phong 假设高光是白色(光源同色)。但金属的高光带颜色——金子的高光是黄的。要画金属必须改成 ks 跟漫反射颜色挂钩,或者干脆换 Cook-Torrance。
-
n 大了会闪:n=500 时高光只占几个像素,相机一动高光就在像素之间跳——叫做 specular aliasing。解法是加 mipmap 或用 prefiltered environment map。
-
R 算错方向:R = 2(N·L)N - L。新人常写成 L 和 N 反向算,结果高光在背面出现。
-
光强累加超出 1:多个光源各自跑一遍后加起来颜色 > 1,需要 tone mapping 压回 [0,1]。
适用 vs 不适用场景
适用:
- 教学:图形学第一个能跑的真实感模型
- 移动端 forward 渲染(每像素几十次乘法即可)
- 风格化渲染(卡通渲染常基于 Phong 改)
- 不追求物理正确的实时场景
不适用:
- 物理正确渲染(PBR 流程必须用 Cook-Torrance / GGX)
- 金属、各向异性材质(拉丝金属)
- 间接光照(ambient 只是常数糊弄,需要 IBL / 球谐 / 路径追踪)
- 半透明、次表面散射(皮肤、蜡、玉)
历史小故事(可跳过)
- 1971:Henri Gouraud 在 Utah 大学发表平滑着色,能在多边形间渐变颜色但没高光
- 1973:Bui Tuong Phong 在 Utah 大学完成博士论文,导师是 Ivan Sutherland(图形学之父)
- 1975 年 6 月:CACM 发表 6 页论文,把博士论文核心成果浓缩
- 1975 年:Phong 因白血病去世,年仅 33 岁
- 1977:Jim Blinn 用半角向量 H 替代 R,得到 Blinn-Phong,省一次反射计算,OpenGL 选它作默认
- 1982 年后:Cook-Torrance 等物理模型出现,但 Phong 因为快、好教,一直活到今天
学到什么
- 拆解就是减少:把”画出真实光照”这个无法直接计算的目标,拆成三个各自只有一行公式的可计算项。这是工程学的基本功。
- 经验模型也能赢 50 年:Phong 不守恒、不物理,但 cos^n 那一项实在太聪明——一个参数从橡胶滑到镜子。
- 命名很重要:specular / diffuse / shininess / ambient 这四个词从 1975 用到现在,所有图形 API 都遵循。
- 简单 + 早 = 教科书:6 页论文 + 8 行公式 + 一个 cos^n,半个世纪没换。
延伸阅读
- 论文 PDF:Phong 1975 — Illumination for Computer Generated Pictures(CACM 6 页,密度极高)
- Scratchapixel 教程:The Phong Model(每一步推导都有图)
- 视频:LearnOpenGL — Basic Lighting(边写代码边讲,最适合零基础)
- blinn-phong —— 1977 年用半角向量优化的版本(如有)
- cook-torrance-1982 —— 第一个物理基础的 BRDF(如有)
关联
- gouraud-1971 —— Gouraud 着色:Phong 的前辈,按颜色插值丢高光
- lambert-cosine —— Lambert 余弦定律:漫反射那一项的物理基础
- 3d-gaussian-splatting —— 现代实时渲染的另一支,但着色思想仍是 diffuse+specular
- ssa —— 编译器术语 specular 与 SSA 无关,只是同时代图形学命名学示例
反向链接
- 3d-gaussian-splatting —— 3D Gaussian Splatting — 用一堆 3D 模糊光斑重建场景
- blinn-1977 —— Blinn 1977 — 用半角向量 H 把高光算量减半
- cook-torrance-1982 —— Cook-Torrance 1982 — 把镜面反射拆成微面元 × 几何遮挡 × Fresnel
- disney-brdf-2012 —— Disney Principled BRDF 2012 — 11 个滑块封装 Cook-Torrance 全家桶
- heckbert-1986-texture-survey —— Heckbert 1986 — 把”贴图”这件事讲清楚的第一篇综述
- perlin-1985-noise —— Perlin Noise — 让计算机生成的图像不再有”机器味”
- ssa —— SSA — 静态单赋值形式
- ward-1992 —— Ward 1992 — 第一个能落地的各向异性反射模型
- whitted-1980 —— Whitted 1980 — 让光线在场景里递归跑三种次级射线