第 6 章 光照:为什么同一张脸有明有暗

上一章我们给积木贴上了”皮肤”——纹理与材质,管的是”表面长什么样”。这一章我们再进一步——不聊”长什么样”了,聊”亮不亮”。

你先回想一个每天都能看见、却从没细想过的现象:

你大白天站在窗边,半边脸被太阳照得发亮,半边脸藏在阴影里发暗。同一个鼻子,鼻尖亮、鼻侧暗。一个苹果,朝着灯的那一面白里透红,背着灯的那一面沉沉的发青黑。为什么同一个物体表面,亮度会不一样?

如果游戏里的角色全是一张”均匀亮度”的死人脸,你会立刻觉得”假”——像塑料,像贴纸。可”明暗”这件事,现实里到底是怎么发生的?工程师又是怎么把它塞进引擎的?这一章,我们就把”光”从一句”加上 Light 组件就行”拆成一套你能讲清楚来龙去脉的知识。

6.1 自然现象:光从哪来、影子去哪了

咱们先回到没有计算机、没有游戏的世界,纯粹观察光。

你屋子里有一盏台灯。光从灯泡出发,向四面八方射出去。它碰到你的手,一部分被手挡住,手后面的墙上就少了一块光——那块少光的地方,就是影子(Shadow)。

你把另一只杯子往灯和墙之间一放,墙上又多一个影子。你把手移近灯,墙上的影子变大、变虚;手离灯越远,影子越小、越实。这些你从小玩过无数次,熟得不能再熟。

现在我把这三件事从现象里拎出来,因为后面全靠它们:

  1. 光有方向。太阳、台灯,光都是从某个源头出发、沿某个方向走的。哪怕是”四面八方散开”的灯泡,对墙上某一个点来说,光也是”从灯泡那个位置射过来”的,有个确定的来向。
  2. 光有衰减。你离台灯越近越刺眼,离得远就柔和。光不是”走到哪都一个亮度”,它会随着距离变远而变弱。这叫衰减(Attenuation),现实里大致遵循”距离翻倍、亮度降到约四分之一”的规律(平方反比,下面会细说)。
  3. 光被遮挡会产生影子。不透明的东西挡在光路上,它背后就缺光。影子不是”某种黑色的物质”,而是”该有光却没光”的空缺。

还有更微妙的一层,也是本章的核心:

同一张桌子,桌面朝天那一面亮,桌腿朝里那一面暗;同一个球,正对窗的那一半亮,背窗的那一半暗。同样是”被光照到”,为什么朝向不同,亮度不同?

这最后一点,是第 4 章我们埋下的”法线(Normal)“概念的真正用武之地。你当时学了:每个顶点带一根”小刺”,指向表面朝外。现在揭晓——那根小刺,就是决定”这块表面有多亮”的关键。

所以你看,“明暗”在现实里其实由两件事共同决定:这块表面朝着光的程度(朝向),以及它离光源有多远(距离)。记住这两点,整章的骨架就有了。

6.2 法线与漫反射:朝向决定亮度

先说法线怎么管亮度。这是本章最该讲透的一节,慢一点。

6.2.1 朗伯余弦律的思想:越正对光,越亮

你拿一张白纸,正对着太阳举着,纸很亮。你把纸慢慢放平、斜过来,你会发现它变暗了。同样一张纸,同样强度的阳光,为什么斜着就暗?

因为光是”一束一束”射来的。纸正对太阳时,单位面积上”接住”的光最多;纸一斜,同样一束光,摊到了更大的面积上——单位面积分到的光就少了,所以看起来暗。

这背后是一条叫**朗伯余弦律(Lambert’s Cosine Law)**的物理规律,名字吓人,思想极朴素:表面接收到的光强,正比于”表面朝向”和”光方向”夹角的余弦。

翻译成人话:

  • 表面正对光(夹角 0 度)→ 余弦是 1 → 最亮。
  • 表面斜对光(夹角 60 度)→ 余弦是 0.5 → 亮度减半。
  • 表面侧对光(夹角 90 度)→ 余弦是 0 → 完全没接到直射光,最暗。

而”表面朝向”是什么?就是第 4 章说的法线。“光方向”是什么?就是从表面指向光源的那个向量。两个向量夹角的余弦,在编程里你会算——点积(dot product)。所以这一节的数学本质就一句话:

亮度 ∝ 法线 · 光方向(两个单位向量的点积)

你写过程序、懂向量,这点积对你不是新东西。新的是:游戏里的”明暗”,底层就是每个像素、每个顶点在算这么一个余弦。

6.2.2 为什么球面有柔和的明暗过渡

现在回答前面那个问题:为什么一个球,亮的一面和暗的一面之间,不是”一半白一半黑”一刀切,而是有一条柔和过渡的灰带?

你想象球面上密密麻麻的法线:正对光的顶点,法线直指光源,余弦≈1,最亮;转到侧面的顶点,法线渐渐和光方向垂直,余弦从 1 滑到 0,亮度跟着滑;背光的顶点,法线背着光,余弦变负,但亮度不能是负数,于是被”掐”到 0(纯暗)。

因为球面上每个点的法线方向是连续渐变的(球嘛,表面平滑弯曲),所以余弦也是连续渐变的,于是亮度从亮到暗是一条平滑的灰梯——这就是你看到的”球面有立体感”。

反过来想:如果一个物体是平面(比如一块板子),它所有点的法线都一样,那要么整块都亮、要么整块都暗,没有过渡。这就是为什么平面看起来”平”、球面看起来”圆”——根子在法线变不变。

这里给你一个能直接用的直觉:一个物体有没有”圆润的明暗过渡”,取决于它的法线是不是随位置平滑变化。建模时法线算得细,明暗就自然;法线错了(比如第 4 章说的平滑/硬边没设对),明暗就会出锯齿、出怪斑。

6.2.3 动手算一个:球面上一个点的亮度

光说思想不过瘾,咱们真算一次,你就彻底不怕它了。假设你写过程序、懂向量点积。

设球面上某点的法线 N = (0, 1, 0)(正朝上),太阳光从正上方照下来,光方向(从表面指向光源)L = (0, 1, 0)(也正朝上)。两者都是单位向量。

亮度系数 = N · L = 0*0 + 1*1 + 0*0 = 1。最亮。合理——这块表面正对太阳。

再取球侧面一点,法线 N = (1, 0, 0)(朝右),光还是 L = (0, 1, 0)。N · L = 1*0 + 0*1 + 0*0 = 0。亮度系数 0,最暗。也合理——侧面和阳光垂直,接不住直射光。

再取斜 45 度一点,N = (0.707, 0.707, 0),L = (0, 1, 0)。点积 = 0.707。亮度系数 0.707,中等亮。看,余弦 45 度 ≈ 0.707,完全对上。

你就发现:所谓”光照计算”,运行时每个点都在重复这道初中级的点积。 它快得离谱(一次乘法加法),所以哪怕几百万个点也能实时算。这也是为什么漫反射能”实时”——它便宜。贵的是阴影和光会弹,那才需要烘焙(见 6.4)。

顺带纠正一个常见误会:点积出来可能是负数(表面背对光,比如球的背光面 N = (0,-1,0),点积 = -1)。这时不能让亮度变负(负亮度没意义,会出怪色),所以引擎把负数”掐”成 0——背光面就是纯靠环境光垫的那层底色(见 6.4.4)。这条 max(0, N·L) 的”掐零”,是你以后看任何光照 shader 都会遇到的第一道 clamp(截断)。

6.2.4 漫反射:光被打散、四面八方逃

上面说的”表面接住光、按余弦分配亮度”,在图形学里有个正式名字:漫反射(Diffuse Reflection)。

“漫”的意思是”散漫、不讲究方向”。当光打到一个粗糙表面(墙、纸、皮肤、石头),它不会原路反弹,而是被表面微小的不平整打散到各个方向。你站在左边看、站在右边看,看到的漫反射亮度差不多——这正是现实里大多数物体的样子:无论你从哪个角度瞅一个红苹果,它都是均匀发红,不会只有某个角度才亮。

漫反射的亮度,就由上面那个余弦决定:正对光亮,斜着暗。它不依赖你看的角度。这点很关键,和第 6.3 节的高光形成对照。

所以”漫反射”这个名词,你以后在材质面板里会反复撞见(连回第 5 章的材质:材质里有一项就是漫反射颜色 / 漫反射贴图)。现在你明白它是什么了:漫反射 = 光被表面打散后、按”朝向光的程度”决定的那部分亮度。 它是物体”本来的颜色”能被看见的基础——没有漫反射,红苹果在你眼里就不红。

6.3 高光与材质:金属为什么”反光点”强

上一节说漫反射”不挑你看的角度”。但现实里有另一类光,特别挑角度——你肯定见过:

大中午的湖面,你站在某个位置,水面有一道刺眼的白光直扎你眼;你往旁边挪两步,那道白光跟着挪,甚至消失了。干净的玻璃、抛亮的金属勺子、打蜡的皮鞋,都有一个”只有在特定角度看才出现的亮斑”。这个亮斑,图形学叫高光(Specular Highlight),或称镜面反射(Specular Reflection)。

6.3.1 镜面反射 vs 漫反射

对比一下两种反射,这是理解材质的一半:

  • 漫反射:光打散到各方向,亮度只看法线对光的程度(余弦),和你(摄像机)在哪看无关。粗糙表面才有。
  • 镜面反射(高光):光像撞到镜子一样,按”入射角=反射角”原路反弹到一个特定方向。只有在”反弹方向正好射进你眼睛”的那个角度,你才看得到那道亮。表面越光滑、越像镜子,这道亮越集中、越锐利。

打个比方。漫反射像把一盆水泼在粗糙的水泥地上,水四溅,哪儿都有点湿;镜面反射像把一束激光打在镜子上,只有镜子正前方那个点被照得雪亮,偏一点就啥也没有。

关键洞察:高光强不强,取决于表面有多”像镜子”。 这就是金属和塑料看起来不同的根源——

  • 金属(金、银、钢):表面致密、光滑,镜面反射强 → 又亮又硬的高光,而且高光常常带着金属本身的颜色(金勺子的高光偏黄)。
  • 塑料、墙壁、皮肤:表面粗糙,镜面反射被打散,高光弱甚至没有 → 看起来柔和、哑光。
  • 水滴、玻璃:几乎全镜面 → 高光极锐、极亮,甚至能映出窗的形状。

6.3.2 落进材质:高光是材质的一项属性

连回第 5 章我们讲的材质(Material)。你当时知道材质决定”物体表面长什么样”。现在补全:一个标准材质(比如 Unity 的 Standard Shader / PBR 材质),至少有这么几块和光相关的属性:

  • Albedo(反照率 / 漫反射颜色):物体”本来的颜色”,喂给漫反射。红苹果的 Albedo 是红的。
  • Metallic(金属度):0~1,控制”这表面多像金属”。高了,漫反射变弱、镜面反射(带色高光)变强。金属勺子拉到 1。
  • Smoothness / Roughness(光滑度 / 粗糙度):控制高光”多大一块、多锐利”。光滑度低(粗糙)→ 高光摊成一大片柔光;光滑度高 → 高光缩成一个针尖亮点。
    • 注意有的引擎用 Roughness(粗糙度),和 Smoothness 是反着的数,本质一回事。
  • Normal Map(法线贴图):第 4 章提过,它不改形状、只改”算光时用的法线”,让平面也能有凹凸的明暗假象。

你看,材质面板里那堆滑块,骨子里就是在回答两个问题:“这表面漫反射啥颜色”和”这表面多像镜子、多光滑”。你把 Metallic 和 Smoothness 一拧,金属勺子和哑光塑料就分出来了——而这一切,全是为了模拟 6.3.1 那两种反射。

一个常踩的坑先预警:高光不是”额外贴上去的亮点”,它是光照计算的一部分。你关掉灯,高光立刻没;你把物体转个身,高光跟着光走。新手常以为高光是材质自带的贴图,结果调半天不对——记住,高光是”光 + 表面光滑度”算出来的,不是画上去的。

6.3.3 高光怎么算:Phong 与 Blinn-Phong 两派

你写过程序,肯定想知道”高光那道亮斑,数学上到底怎么求”。核心还是向量,但比漫反射多考虑”你看的角度”。

回忆镜面反射:光按”入射角=反射角”反弹到特定方向。只有当”反弹方向”正好射进摄像机(你眼睛),你才看得到高光。所以高光强度 = “光反射方向”和”你视线方向”有多接近——接近到重合,最亮;偏开一点,骤降。

经典公式有两派:

  • Phong 模型:直接算”反射光方向 R”和”视线方向 V”的夹角余弦,再取一个幂次(幂次由光滑度决定,越光滑幂次越大、亮斑越尖)。高光 = pow(max(0, R·V), shininess)。
  • Blinn-Phong 模型:不真算反射方向,而是算”半角向量 H”(光方向 L 和视线 V 的中间方向)和”法线 N”的夹角。谁的”半角”越贴合法线,高光越亮。高光 = pow(max(0, N·H), shininess)。这派计算更便宜、更稳定,是今天绝大多数实时引擎(包括 Unity 默认)用的。

你不用背公式,带走的就一句:高光 = “你视线”和”光该反弹去的方向”的吻合度,再被光滑度收紧成亮斑。 光滑度(Smoothness/Roughness)就是那个幂次——它决定亮斑是”摊成一片柔光”还是”缩成针尖”。这也回答了为什么金属勺子高光又强又小:金属度高 → 漫反射弱、能量全跑到镜面项;光滑度高 → 亮斑收得极紧。

补充一个现实对照,帮你检验直觉:你拿手机拍湖面反光,稍微动一下手机,亮斑就跳走——这就是”高光挑剔你视线方向”。漫反射的湖蓝色却怎么拍都差不多。同一个湖面,两种反射同时发生:蓝色是漫反射(不挑角度),白亮斑是高光(挑角度)。游戏里一个水面材质,正是这两部分叠加出来的。

6.4 实时光照的代价:为什么游戏常用”假光”

讲到这,你已经能解释”一张脸为什么有明有暗”了。但工程师的麻烦才刚开始。我先问你一个扎心的问题:

上面那些计算——每个像素算”法线·光方向”、算高光、算阴影——如果每帧、每个物体、每个光源都实时算,要花多少算力?

答案是:贵到撑不住。咱们拆开看为什么,以及工程师怎么”糊弄”。

6.4.1 实时阴影的贵

阴影怎么算?最直观的办法(阴影贴图 Shadow Mapping):先从光源的视角,把整个场景”拍一张深度照片”,记录”从光源看,哪个点最近”。然后正常渲染时,对每个像素,问一句”从光源看,我前面有没有东西挡着?“——有,就在影子里;没有,就被照亮。

听上去就一句”问一句”,但每个像素每帧都要问一次,而且场景里可能有几十个物体、好几个光源。更致命的是:阴影要”柔和”(边缘虚化)才好看,柔和阴影意味着要采样很多次、做模糊,算力再翻几倍。

再加上你想要”动态”——角色一抬手,影子立刻跟着动。这每一步都是实时算的。一两个光源还行,光源一多、场景一大,16 毫秒的预算(第 1 章的餐厅)立刻爆。

6.4.2 全局光照:光会”弹”

更深层的问题叫全局光照(Global Illumination,简称 GI)。现实里光不是”打一下就完”——光打到红墙,被染成红色,再反弹到旁边的白桌子上,白桌子的阴影里就泛红。这种”光在场景里弹来弹去、互相染色”的效果,才是真实感的大头。

可你要实时算”光弹 N 次、每次打到不同颜色的东西再染色”,那算力是指数级的爆炸。真·实时全局光照,目前也只有顶级硬件勉强做局部。普通游戏根本算不起。

6.4.3 工程师的方案:烘焙(Bake)——把贵的计算提前做掉

回到第 1 章那句贯穿全书的钥匙:把贵的计算提前做掉,运行时只做便宜的拼装。

光照这儿的”提前做掉”,叫烘焙(Bake)。思想极其朴素:

  • 那些不会动的东西(墙、地板、固定的山、建筑),它们和光源的相对位置是固定的。那我在游戏开始前,用离线算力(不赶 16 毫秒,慢慢算几小时都行)把光照结果——包括阴影、包括光弹来弹去的染色——直接算好,烤进一张贴图(Lightmap,光照贴图)。
  • 运行时,墙不用再算光了,直接把”已经烤好的亮暗”贴上去就行。便宜到可以忽略。

这和第 1 章”餐厅备半成品”、第 8 章”雾遮住偷工的地方”是同一个套路。烘焙就是光照届的”预制菜”。

代价是什么?烤死的光不会动。 你烘了一张静态阴影在地板上,角色走过去,那影子不会让开——因为地板贴图是死的。所以烘焙适合”不动的场景 + 不动的灯”,比如室内建筑、固定日月。会动的角色,通常还是用”实时光”打一层,再和烘焙的底子叠加。

6.4.4 光照探针与环境光:让动态物体也”沾光”

还有个细腻的问题:烘焙只烤了静态物体。一个动态角色跑进烘焙好的房间,它身上该有”红墙染的红光”才对,可它没被烘焙,怎么办?

工程师给了个轻量办法:光照探针(Light Probe)。在场景里撒一堆”采样点”,每个点提前记录”这里的环境光是什么颜色/强度”。角色跑到哪,就取附近几个探针的值, interpolated(插值)一下,给自己染上近似的环境色。它不精确(不是真算 GI),但便宜又够看——又是一次”糊弄”。

再补一个词:环境光(Ambient Light)。现实里即便背光的暗面,也不是纯黑——因为天空、四周的墙都在往它反射微弱的光。引擎用”环境光”这个全局底色,给所有物体的暗面垫一层基础亮度,免得影子黑得像窟窿。它是 GI 的”乞丐版”,一个均匀的颜色,人人都有份。

这一节你只要带走一条主线:实时光贵在”阴影 + 光会弹”,所以静态部分烘焙进贴图、动态部分用探针和实时光凑合。 你以后看到 Lightmap、Light Probe、Mixed Lighting 这些词,脑子里都是这一句。

6.4.5 算一笔实时光的”预算账”

光说”贵”太虚,咱们量化一下,你就知道工程师为什么非糊弄不可。

假设一个场景有 1000 个物体,每个物体表面 1 万个点要算亮度(这是很克制的建模精度)。再来 4 盏实时灯。

  • 漫反射(只算朝向):每点每灯一次点积,约 1000 × 10000 × 4 = 4 千万次乘法/帧。现代 GPU 轻松扛,没问题。
  • 加上实时阴影:每盏灯要”从光源视角拍深度图”再让每个点”问一次被挡没”,开销直接翻几倍;要软阴影还得每个点采样多次、做模糊,再翻几倍。轻松破几亿次操作/帧。
  • 再加全局光照(光弹来弹去染色):理论上每点要追踪光在场景里弹 N 次、每次撞不同颜色物体——这是路径追踪,电影级离线渲染一帧要算几小时到几天。实时?根本不可能。

这就是矛盾:你想要”真”(阴影 + 光会弹),算力就要爆炸;你只有 16 毫秒(第 1 章餐厅)。所以工程师的取舍是:

  • 漫反射 + 单盏方向光:实时,便宜,够看清形状(大多数手游的底线)。
  • 多盏实时灯 + 实时阴影:中配,主机/PC 扛得住,但灯一多就掉帧。
  • 静态场景烘焙 GI + Lightmap:预计算几小时,运行时近乎免费,真实感拉满,但光不会动。
  • 动态物体用 Light Probe 蹭环境色:廉价的”伪 GI”,凑合够看。

你调画质时,那几个档(低/中/高画质)背后,本质就是在”实时光数量、阴影分辨率、烘焙与否”之间拨开关。懂了这笔账,你就不会盲目拉满所有灯导致卡死,也不会为了真实硬上实时 GI 把帧率干崩。

一句话收这节:光照的真实感和流畅度,是同一笔预算里的两头;烘焙是把”一次性贵”挪到开机前,实时是把”每帧贵”压到能忍。 全书的”提前做掉贵的”在这里体现得最淋漓尽致。

6.5 关键参数直觉:那几个旋钮各对付什么问题

最后落到你最关心的——Unity 里一个 Light 组件、一套光照设置,那堆参数到底管啥。我按”它解决什么问题”逐个给你。

6.5.1 光源本身的参数(以 Light 组件为例)

  • Type(类型):Directional(平行光,如太阳,光都平行、无衰减、无唯一位置)、Point(点光,如灯泡,四面八方向外、有衰减)、Spot(聚光,如手电筒,锥形)、Area(面光,如灯管,柔光,通常只能烘焙)。选类型 = 选”光从哪来、怎么散”。
  • Color(颜色):光的颜色。夕阳偏橙、正午偏白、荧光灯偏冷。颜色直接乘到物体上——红光打白墙,墙显红。
  • Intensity(强度):光有多”猛”。调亮暗的总开关。现实类比:台灯旋钮。强度低,整个场景发灰;强度高,过曝死白。
  • Range(范围,点光/聚光专用):光能照多远。超过这个范围,光按衰减降到 0。它解决”一盏灯别把整个地图都照亮”的性能和真实感问题——现实里灯泡照不了三里地。
  • Spot Angle(聚光角,Spot 专用):手电筒张多大口。角小像激光笔,角大像泛光灯。

6.5.2 阴影参数

  • Shadow Type(阴影类型):No Shadows(不要影子,最便宜)、Hard Shadows(硬边影子,边缘锐利、便宜)、Soft Shadows(软边影子,边缘虚化、真实但贵)。
    • 硬软对照你生活里见过:正午太阳高照,影子边缘利落(硬);阴天或晨昏,影子发虚(软)。游戏里”硬”是性能妥协,“软”是真实感。
  • Shadow Strength(阴影浓度):影子多黑。0 是没影子,1 是纯黑。调低可做”半透明影子”或柔和环境。
  • Shadow Resolution(阴影分辨率):影子贴图多精细。低了影子边缘锯齿、像马赛克;高了清晰但吃显存。这是”清晰度 vs 算力”的直接权衡。
  • Shadow Bias(阴影偏移):防”阴影自己长到物体脸上”的错位 bug(自阴影痤疮)。调参时你多半只在出问题才碰它。

6.5.3 环境光与全局

  • Environment Lighting / Ambient Intensity(环境光强度):给所有暗面垫的底色亮度。解决”背光面纯黑像窟窿”。调高,整体更亮更”平”;调低,明暗对比更强烈、更戏剧化。
  • Ambient Color / Skybox 反射:环境光取天空盒颜色,让暗面泛天蓝——和第 8 章雾色取天空色是同一思路,都是为了”过渡自然”。

6.5.4 一张”对症下旋钮”的表

你想解决的问题拧哪个怎么拧
场景太暗 / 太亮Intensity加 / 减光源强度,或抬 Ambient
背光面黑得像洞Ambient Intensity调高环境光
影子边缘太硬、显假Shadow Type → Soft切软阴影
影子糊成马赛克Shadow Resolution调高分辨率
一盏灯把全图照亮、不真实Range点光缩小 Range
想要黄昏暖调Color光源色偏橙
远处物体太清楚、想藏边界(接第 8 章)配合 Fog雾色=天色,远处溶掉

你发现没有,这一章和雾、和坐标、和材质,全部是”先有现象和问题,才有参数”的同一条线。光照没有神秘,它只是把”光从哪来、怎么反射、怎么被挡、怎么衰减”翻译成了一组旋钮。

6.5.5 颜色相乘:为什么红光打白墙,墙变红

最后点破一个你调 Color 时必然遇到的”反直觉”:光照里的颜色,是乘上去的,不是加上去的。

现实中你拿红光照白墙,墙显红——因为白墙本来反射所有色,红光只含红色成分,乘上去只剩红。引擎里也一样:最终颜色 = 物体本色(Albedo) × 光颜色。白墙 Albedo 是 (1,1,1),乘红光 (1,0,0),结果是 (1,0,0) 红。你拿绿光照红苹果(Albedo 红 (1,0,0)),乘绿 (0,1,0) = (0,0,0) 黑——红苹果在纯绿光下几乎是黑的,因为红苹果不反射绿。

这解释了好多现象:黄昏的暖光(偏橙)打在一切上,整个世界泛橙;蓝光(月夜)打一切,世界泛冷蓝。你调 Color 不是”给场景加一层颜色滤镜”,而是”改光里含哪些成分、物体乘完剩什么”。记住”乘”不是”加”,你就不会奇怪”为什么绿光下红东西变黑”——加的话红+绿=黄,乘的话红×绿=黑,现实是乘。

顺带:多盏灯时是”各自算完再相加”(红光照的 + 绿光照的)。所以红绿两盏灯同时打红苹果,红处被红灯照亮、绿处贡献为 0,结果还是红但更亮。这也呼应 6.4.5 的”多灯预算”——每盏灯独立算一遍再求和。

6.6 调光实战小剧场:从”全黑”到”像样”

讲了一堆参数,咱们串一次真实工作流,你就知道这些旋钮怎么配合着拧。假设你刚导入一个室内场景:四面墙、一张桌子、一把椅子,全黑,啥也看不见。

第一步:打一盏主光(Key Light)。 加一盏 Directional Light,当”太阳/主灯”。它没位置(平行光),只管方向。你把它转到”从窗户斜射进来”,Intensity 设 1 左右,Color 偏暖白。这一下,墙面有了明暗、桌子亮面暗面分出来——全靠 6.2 的漫反射(法线·光方向)。如果全黑,先检查:光方向是不是背对着场景了?物体法线是不是反了(第 4 章)?

第二步:补环境光,别让暗面成窟窿。 你发现背光面黑得像被挖掉。抬 Ambient Intensity 到 0.3 左右,暗面泛起一层灰底(6.4.4 的环境光)。场景立刻”透气”了。但别抬太高,否则明暗对比消失,变扁平塑料感。

第三步:加阴影。 主光 Shadow Type 切 Soft Shadows。桌子的影投到地上,立体感暴涨。卡了?看 Shadow Resolution 够不够(低了马赛克),看 Bias 是不是让影子”浮”在物体上(痤疮 bug)。性能吃紧就把几盏辅助灯设成 No Shadows,只让主光投影。

第四步:金属 vs 木头。 桌子是木的——材质 Metallic 拉 0、Smoothness 中等,哑光木纹。椅子腿是钢的——Metallic 拉 1、Smoothness 高,立刻出现又硬又带色的高光(6.3)。你挪一下光,高光跟着跑,确认它是算出来的不是贴的。

第五步:烘焙省性能(可选)。 墙和地板都不动、灯也不动,那就把光照 Bake 进 Lightmap(6.4.3),运行时白嫖。如果角色要跑进来沾环境色,撒几个 Light Probe(6.4.4)。

第六步:藏远处(接第 8 章)。 室外的话,加雾,雾色取天色,Far 裁剪面设在雾浓处,世界尽头被温柔遮住。

你看,这六步里每一步拧的旋钮,都是”对付一个具体问题”:看不见→打主光;黑窟窿→环境光;没立体→阴影;材质不分→Metallic/Smoothness;卡→烘焙;边界穿帮→雾。这正是 6.5 那张表的实战版。

留个自检:如果调完发现”整个场景发灰、没精神”,你先猜是哪个旋钮拧过了?多半是 Ambient 太高或主光 Intensity 太低,把明暗对比冲淡了。先压环境光、提主光,对比就回来了。你试过之后,会不会觉得”调光”其实和”调相机曝光”是同一件事?

6.7 收个尾,连回主线

咱们把这一章扣回第 1 章那句话:游戏在”实时”铁律下,用近似和预计算,换流畅又像真。

  • 真实现象:光有方向、有衰减、被挡成影、表面朝向不同亮度不同、金属反光点强。
  • 工程师的麻烦:实时阴影贵、全局光照(光会弹)更贵、算不起。
  • 方案落进引擎:
    • 漫反射 = 法线·光方向(余弦),补第 4 章法线、第 5 章材质。
    • 高光 = 镜面反射,由 Metallic / Smoothness 控制。
    • 烘焙(Bake)= 把静态光提前烤进 Lightmap,运行时贴图;动态物体用 Light Probe 和实时光凑。
    • 参数旋钮:Intensity / Color / Range / Soft-Hard Shadow / Ambient。
  • 你能做的:调几个数,就让一张死人脸有了立体明暗,让塑料变金属,让房间有黄昏。

下一章我们讲摄像机——前面所有”光算出来、物体摆好”的画面,最终都得”通过一台虚拟相机”才进到你眼睛。而相机怎么把三维世界压成你屏幕上的二维,又藏着另一套”近大远小”的学问。

【思考题】

  1. 现实里同一张白纸,正对太阳亮、斜着就暗,物理原因是什么?用”朗伯余弦律的思想”用自己的话讲一遍。
  2. 为什么球面有”柔和明暗过渡”、而一块平板要么全亮要么全暗?关键在表面哪样东西连续不连续?
  3. 漫反射和高光(镜面反射)最核心的区别是什么?为什么金属勺子的高光又强又带金色,而白墙几乎没有高光?
  4. 为什么说游戏里的静态光照常常要”烘焙”?它和第 1 章的”餐厅备半成品”、第 8 章的”雾遮偷工”是同一种思路吗?烘焙的代价是什么?

【参考答案】

  1. 光是一束束射来的。纸正对太阳时,单位面积接住的光最多,最亮;纸一斜,同样一束光摊到更大面积上,单位面积分到的光变少,就暗了。朗伯余弦律的思想就是:表面接收到的光强,正比于”表面朝向”和”光方向”夹角的余弦——正对(0度)最亮,侧对(90度)最暗。本质是”法线对光的程度”决定亮度。
  2. 关键在法线是否随位置连续变化。球面每个点的法线方向平滑渐变,所以”法线·光方向”的余弦也连续渐变,亮度从亮到暗是平滑灰梯,看起来圆润。平板所有点法线相同,余弦处处一样,所以要么整块亮、要么整块暗,没有过渡,看起来”平”。这也说明第 4 章法线算得细不细,直接决定明暗自然不自然。
  3. 核心区别:漫反射不挑你看的角度(光被打散到各方向,亮度只看法线对光的程度),高光挑角度(光按入射角=反射角反弹到特定方向,只有那个方向射进你眼才看得见)。金属表面致密光滑,镜面反射强 → 高光又强又锐,且高光带金属本色(金勺偏黄);白墙粗糙,镜面反射被打散,高光弱到几乎没有,只剩柔和漫反射。所以材质里 Metallic / Smoothness 一拧,金属和哑光就分出来了。
  4. 因为实时算阴影 + 全局光照(光在场景里弹来弹去染色)算力极贵,撑不住每帧 16 毫秒。烘焙 = 游戏开始前用离线算力把静态物体和固定灯的光照结果(含阴影、染色)烤进 Lightmap,运行时直接贴图,便宜到忽略。它和第 1 章”备半成品”、第 8 章”雾遮偷工”是同一思路:把贵的计算提前做掉、运行时只拼装。代价是烘焙的光是死的——静态阴影不会因动态物体让开,所以动态角色通常另用实时光 + 光照探针凑近似。你能否举一个”你希望光能动、却因为烘焙而穿帮”的具体场景?