Skip to content

光照与材质

Blinn-Phong 和 Phong 区别是什么?

一句话理解

Phong 和 Blinn-Phong 都是经典光照模型,主要区别在镜面高光怎么算:Phong 比较“反射方向 R 和视线方向 V”;Blinn-Phong 比较“半程向量 H 和法线 N”。

cpp-blinn-phong-vs-phong

Phong 怎么算

Phong 的镜面高光思路是:

先根据光照方向 L 和法线 N 算出反射方向 R

再看观察方向 V 和反射方向 R 是否接近。

公式可以记成:

Specular = max(dot(R, V), 0)^shininess

如果 RV 越接近,说明你眼睛刚好在反射光方向上,高光越亮。

Blinn-Phong 怎么算

Blinn-Phong 不直接算反射方向,而是算半程向量 H

H = normalize(L + V)

然后比较 H 和法线 N 是否接近:

Specular = max(dot(N, H), 0)^shininess

半程向量可以理解为:光照方向和视线方向中间的那个方向。 如果这个中间方向刚好接近表面法线,就说明这个点应该有高光。

核心区别

Phong:用 R dot V。 Blinn-Phong:用 N dot H

Phong 更符合“镜面反射方向”的直觉。 Blinn-Phong 计算更方便,早期硬件上通常更快,而且在很多角度下高光更稳定。

高光效果一样吗?

不完全一样。

同样的 shininess 指数下,Phong 和 Blinn-Phong 的高光宽度通常不一样。 一般 Blinn-Phong 如果想得到接近 Phong 的高光,需要把指数调大一些。

所以不能简单认为:

Phong shininess = Blinn shininess

它们参数不是严格等价的。

它们和 PBR 的关系

Phong 和 Blinn-Phong 都是经验模型。 它们能做出“看起来像高光”的效果,但不是严格基于物理的光照模型。

现代游戏里,如果追求真实感,更多会用 PBR,比如基于:

粗糙度;

金属度;

法线分布函数;

菲涅尔;

几何遮蔽。

但 Phong / Blinn-Phong 在教学、低成本风格化渲染、简单材质里仍然很好用。

面试高分说法

NOTE

Phong 和 Blinn-Phong 的漫反射部分通常都可以用 Lambert,主要区别在镜面高光。Phong 先算反射向量 R,再用 dot(R, V) 判断观察方向是否接近反射方向;Blinn-Phong 算的是光照方向和观察方向的半程向量 H,再用 dot(N, H) 判断半程向量是否接近法线。Blinn-Phong 通常计算更方便,也更稳定,但同样的高光指数下两者高光范围不同,参数不能直接等价。两者都属于传统经验光照模型,不是严格 PBR。

漫反射和高光分别是什么?

一句话理解

漫反射决定“物体本身被照亮后的颜色和明暗”;高光决定“表面反光形成的亮斑”。简单说:漫反射看光照到表面多少,高光看反射光有没有进入眼睛

rendering-diffuse-vs-specular

漫反射是什么?

漫反射 Diffuse 可以理解为:光打到粗糙表面后,被往很多方向散开。

比如墙、纸、布、粉笔,它们没有特别明显的亮斑,但是会根据光照角度变亮或变暗。

常见 Lambert 漫反射公式:

Diffuse = Albedo * max(dot(N, L), 0)

这里:

Albedo 是物体本色;

N 是表面法线;

L 是指向光源的方向;

dot(N, L) 表示表面有多“正对”光源。

如果表面正对光源,dot(N, L) 大,物体更亮。 如果表面背对光源,dot(N, L) 小甚至小于 0,就很暗。

高光是什么?

高光 Specular 可以理解为:光照到比较光滑的表面后,沿着接近镜面反射的方向反出去,刚好被眼睛或相机看到,就形成亮斑。

比如金属、塑料、玻璃、湿地面,通常会有明显高光。

Phong 高光常见公式:

Specular = pow(max(dot(R, V), 0), shininess)

这里:

R 是反射方向;

V 是观察方向;

shininess 控制高光集中程度。

shininess 越大,高光越小越锐利; shininess 越小,高光越大越柔和。

Blinn-Phong 里常用半程向量:

H = normalize(L + V)

然后:

Specular = pow(max(dot(N, H), 0), shininess)

它们最大的区别

漫反射主要和光照方向、表面法线、物体本色有关。 高光除了和光照方向、法线有关,还强烈依赖观察方向。

所以相机移动时:

漫反射通常不会明显跟着跑;

高光会在物体表面移动。

这就是你在游戏里转动相机时,金属、塑料、水面上的亮斑会动的原因。

美术上怎么理解

漫反射像是在回答:这个物体是什么颜色?被照得有多亮?

高光像是在回答:这个物体表面有多光滑?反光强不强?有没有油亮、金属、湿润的感觉?

面试高分说法

NOTE

漫反射是光照射到粗糙表面后向各个方向散射形成的亮度,常用 Lambert 模型,用 dot(N, L) 表示表面朝向光源的程度,它主要决定物体的基础明暗和颜色。高光是光在较光滑表面上发生方向性反射形成的亮斑,Phong 模型用 dot(R, V),Blinn-Phong 用 dot(N, H) 来计算,它和观察方向强相关,能表现材质的光滑程度、金属感和反光感。

环境光有什么作用?

一句话理解

环境光 Ambient Light 的作用是:给场景一个基础亮度和基础颜色,模拟现实中天空、墙面、地面反弹来的间接光,避免没有被直接光照到的地方变成一片死黑。

rendering-ambient-light-purpose

为什么需要环境光?

如果只算直接光,比如方向光、点光源、聚光灯,那么背光面可能非常黑。

现实里不会这样极端,因为光会在环境中反弹:

阳光照到地面,地面会反射一部分光;

天空本身会提供蓝色的散射光;

墙面会把自己的颜色轻微反射到附近物体上。

环境光就是用一个低成本方式近似这种“到处都有一点点的间接光”。

传统光照里怎么理解

一个简单光照模型可以写成:

最终颜色 = 环境光 + 漫反射 + 高光

其中:

漫反射负责物体被主光照亮的明暗;

高光负责表面反光亮斑;

环境光负责暗部基础亮度。

如果没有环境光,背光面可能接近黑色。 如果有环境光,暗部还能保留一些颜色和形体。

环境光主要作用

第一,防止暗部死黑。 角色背面、墙角、阴影区域不会完全丢细节。

第二,模拟间接光。 它不是真正计算光线反弹,而是用便宜的方式近似。

第三,统一画面氛围。 偏蓝的环境光会有夜晚、天空感;偏暖的环境光会有夕阳、室内灯光感。

第四,辅助材质表现。 没有环境光时,很多材质在暗部看不出颜色;有环境光后,物体本色更容易被看到。

环境光不能替代什么?

环境光不能替代主光源。 因为它通常没有明确方向,所以不能产生清晰的投影,也不能塑造强烈的立体感。

如果环境光太强,会出现一个问题:画面变平。 阴影被冲淡,角色和场景的体积感下降,画面像“糊了一层亮灰”。

现代渲染里环境光更高级

在传统模型里,环境光可能只是一个固定颜色:

Ambient = AmbientColor * Albedo

但在现代 PBR 里,环境光经常会变成更真实的间接光方案,比如:

IBL,基于环境贴图的光照;

GI,全局光照;

Light Probe,给动态物体采样周围间接光;

Reflection Probe,提供环境反射;

Ambient Occlusion,压暗缝隙和接触区域,避免环境光把所有角落照得一样亮。

面试高分说法

TIP

环境光的作用是近似间接光照,给没有被直接光源照到的区域提供基础亮度,避免暗部完全死黑,同时还能统一场景色调。它通常没有明确方向,所以不能产生清晰阴影,也不能替代方向光、点光源等直接光。环境光过弱会丢暗部细节,过强会让画面变平、缺少体积感。现代渲染中,环境光往往不只是一个常量颜色,而会结合 IBL、GI、Light Probe、Reflection Probe 和 AO 来获得更自然的间接照明效果。

Gamma 空间和 Linear 空间区别是什么?

一句话理解

Gamma 空间更像“给人眼和显示器看的颜色编码”;Linear 空间更像“真实光强数值”。 渲染里更推荐:贴图按 Gamma/sRGB 存储,光照计算转到 Linear,最后输出到屏幕前再转回 Gamma/sRGB

rendering-gamma-vs-linear-space

Gamma 空间是什么?

人眼对暗部变化更敏感,对亮部变化相对没那么敏感。 所以很多图片格式和显示系统会用非线性方式存颜色,也就是 Gamma/sRGB 编码。

大概可以理解成:

屏幕颜色 = 线性光强 ^ (1 / 2.2)

反过来:

线性光强 = 屏幕颜色 ^ 2.2

所以在 Gamma/sRGB 里,数值 0.5 并不代表真实光强的一半。 它大概只对应 0.214 左右的线性光强。

Linear 空间是什么?

Linear 空间里的颜色值和真实光强成线性关系。

也就是说:

0.2 + 0.2 = 0.4

这在物理意义上是合理的。 光照、阴影、混合、插值、PBR 这些计算,本质都应该基于真实光强,所以应该在线性空间里算。

为什么 Gamma 空间直接算光会有问题?

因为 Gamma 值不是线性光强。

比如两个颜色做混合,如果你直接在 Gamma 空间平均,得到的亮度不是物理正确的平均光强。 所以会出现画面偏灰、偏亮、过渡不自然、光照不真实的问题。

尤其这些操作很容易受影响:

灯光叠加;

透明混合;

颜色插值;

阴影衰减;

PBR 光照;

后处理中的亮度计算。

Unity 里怎么理解

Unity 里项目可以选择 Gamma 或 Linear Color Space。

Gamma 模式: 兼容性好,旧项目常见,低端设备历史上更友好。

Linear 模式: 光照更真实,PBR 材质更准确,现代项目更推荐。

在 Linear 工作流里,Unity 通常会做这件事:

读取 sRGB 颜色贴图时,把它解码到 Linear;

在线性空间里做光照计算;

最后输出到屏幕时,再编码回 sRGB/Gamma。

贴图设置很重要

颜色贴图,比如 Albedo、BaseMap,通常应该勾选 sRGB。 因为它们是给人看的颜色数据。

但数据贴图通常不应该勾选 sRGB,比如:

法线贴图;

金属度贴图;

粗糙度贴图;

AO 贴图;

Mask 贴图;

高度图。

因为这些不是颜色,而是数值数据。 如果错误地做 Gamma 转换,数值就变了,效果会不对。

面试高分说法

IMPORTANT

Gamma 空间是为了适配人眼感知和显示设备的非线性颜色编码,sRGB 贴图通常就是 Gamma 编码;Linear 空间表示真实线性光强,更适合做光照、混合、插值和 PBR 计算。正确流程一般是:颜色贴图从 sRGB 解码到 Linear,在线性空间里进行光照计算,最后输出到屏幕前再编码回 sRGB。Unity 中颜色贴图通常开启 sRGB,而法线、金属度、粗糙度、AO、Mask 等数据贴图不应该当颜色做 sRGB 转换。Gamma 模式兼容性好,但 Linear 模式的光照更符合物理规律。

HDR 是什么?

一句话理解

HDR 是 High Dynamic Range,高动态范围。渲染里它表示:颜色亮度可以超过普通 0 到 1 的范围,先保留强光信息,再通过曝光、Bloom、Tone Mapping 映射到屏幕上。

rendering-hdr-explained

LDR 是什么

LDR 是低动态范围,颜色通常限制在 0 到 1

比如:

0 表示黑色;

1 表示最亮白色。

如果一个光照结果算出来是 3.0,LDR 会直接把它截断成 1.0。 这样 1.53.010.0 最后都可能变成一样的白色,亮部细节就没了。

HDR 是什么

HDR 允许渲染中间结果超过 1.0

比如:

太阳附近亮度可以是 10.0

火焰可以是 5.0

普通墙面可能是 0.6

这样渲染管线能知道:“这里不是普通白,而是真的很亮。”

这对后处理非常重要。

为什么 HDR 经常和 Bloom 一起出现

Bloom 泛光通常会提取特别亮的区域,让它向周围扩散,形成发光效果。

如果是 LDR:

亮度超过 1.0 的信息已经被截断;

后处理不知道哪里是 1.2,哪里是 10.0

Bloom 效果容易不自然。

如果是 HDR:

强光信息还在;

Bloom 可以只提取真正超过阈值的亮部;

火焰、太阳、魔法特效、霓虹灯会更有发光感。

Tone Mapping 是什么

屏幕最终还是有显示范围的。 哪怕渲染中有 10.0 的亮度,也不能直接原样显示。

所以需要 Tone Mapping,把 HDR 的大亮度范围压缩回屏幕能显示的范围。

你可以把它理解成相机曝光:

太亮的地方压下来;

暗部尽量保留;

让画面看起来既有层次,又不会整片过曝。

HDR 的代价

HDR 不是免费的。

它通常需要更高精度的 RenderTexture,比如浮点格式。 这会增加:

显存占用;

内存带宽;

后处理成本;

移动端发热和耗电风险。

所以移动端项目不能无脑开 HDR,要看画面收益和性能预算。

Unity 里怎么理解

Unity 中开启 Camera HDR 或管线 HDR 后,场景会先渲染到 HDR 缓冲里。 然后经过 Bloom、Color Grading、Tone Mapping 等后处理,最后输出到屏幕。

注意: HDR 渲染不等于玩家一定使用 HDR 显示器。 即使是普通 SDR 屏幕,HDR 渲染也可以通过 Tone Mapping 和 Bloom 让画面更自然。

面试高分说法

TIP

HDR 是高动态范围渲染,它允许渲染过程中的颜色亮度超过 0 到 1,从而保留强光区域的真实亮度差异。LDR 会把超过 1 的值截断,导致亮部细节丢失;HDR 会先把这些超亮信息保留下来,用于 Bloom、曝光、Color Grading 等后处理,最后通过 Tone Mapping 压缩到屏幕可显示范围。HDR 能提升亮部层次和发光效果,但会增加 RenderTexture 精度、显存和带宽成本,移动端要谨慎开启。

Tone Mapping 是什么?

一句话理解

Tone Mapping 是把 HDR 渲染出来的高亮度范围,压缩映射到屏幕能显示的范围。它不是简单把超过 1 的颜色砍掉,而是用一条曲线把暗部、中间调、亮部重新分配,让画面既不过曝,又保留层次。

rendering-tone-mapping-explained

为什么需要 Tone Mapping

HDR 渲染里,颜色亮度可能超过 1

比如:

太阳:10.0

火焰:5.0

金属高光:2.0

普通墙面:0.6

但普通屏幕最终显示范围有限。 如果直接 clamp0 到 1,那么 2.05.010.0 都会变成 1.0,结果就是亮部一片白,细节全没了。

Tone Mapping 的作用就是: 把这些超亮值压下来,但尽量保留谁更亮、谁更暗的关系。

它大概做了什么

渲染流程可以理解成:

HDR 场景颜色 -> 曝光调整 -> Tone Mapping 曲线 -> Gamma/sRGB 输出 -> 屏幕

其中 Tone Mapping 负责从“大范围亮度”变成“屏幕可显示亮度”。

它像相机或胶片的响应曲线:

暗部不要太死黑;

中间调保持自然;

高光不要直接炸白;

整体画面要有可看的对比度。

常见 Tone Mapping 类型

Reinhard: 简单、柔和,经典公式类似:

color = color / (color + 1)

它会自然把很大的值压到 1 以下。

Filmic: 模拟胶片响应,亮部压缩比较自然,常用于电影感画面。

ACES: 现在游戏引擎里很常见,亮部和色彩表现更有电影感。Unity、Unreal 里都经常看到 ACES 风格的 Tone Mapping。

Neutral: 更偏中性,不想让画面有太强电影调色感时可以用。

Tone Mapping 和 Bloom 区别

Bloom 是泛光。 它负责让特别亮的区域向周围扩散,形成发光效果。

Tone Mapping 是亮度映射。 它负责把 HDR 的大亮度范围压到屏幕可显示范围。

两者经常一起出现,但不是一回事。

通常流程是:

先在 HDR 中找到高亮区域做 Bloom;

再把原画面加上 Bloom;

最后做 Tone Mapping 输出。

Unity 里怎么理解

在 Unity 的 URP/HDRP 或 Post Processing 中,Tonemapping 常见选项有 NoneNeutralACES

None:不做明显映射,亮部可能更容易硬。

Neutral:比较中性,适合不想大幅改变颜色风格的项目。

ACES:电影感更强,亮部压缩和色彩观感更好,但可能改变整体色彩倾向。

面试高分说法

NOTE

Tone Mapping 是 HDR 渲染到显示设备之间的一步映射过程。因为 HDR 场景颜色可以超过 0 到 1,而屏幕显示范围有限,如果直接截断会导致高光细节丢失,所以 Tone Mapping 会用一条非线性曲线把大范围亮度压缩到可显示范围,同时尽量保留暗部、中间调和亮部层次。它常和曝光、Bloom、Color Grading 一起使用。Bloom 是泛光效果,Tone Mapping 是亮度范围映射,两者不要混为一谈。

IBL 是什么?

一句话理解

IBL 是 Image Based Lighting,基于图像的光照。它的核心是:用一张环境贴图记录周围世界的光照信息,然后用这张图来给物体提供间接光和环境反射

rendering-ibl-explained

IBL 不是单纯的天空盒

天空盒只是“看起来像背景”。 IBL 是把环境贴图真的拿来参与光照计算。

比如一个金属球放在蓝天黄土地的环境里,它表面会反射蓝天和地面的颜色。 这不是普通方向光能做出来的,需要环境信息。

IBL 主要做两件事

第一,提供间接漫反射。

粗糙表面会接收来自周围很多方向的光。 比如墙面、角色衣服、地面,它们不会像镜子一样清晰反射环境,但会被环境颜色影响。

常见做法是用 Irradiance Map,也就是把环境贴图卷积成低频的漫反射环境光。

第二,提供间接高光反射。

光滑表面会反射周围环境。 越光滑,反射越清晰;越粗糙,反射越模糊。

常见做法是用 Prefiltered Environment Map。 不同粗糙度对应不同 mip 级别,粗糙度越高,采样越模糊的 mip。

PBR 里 IBL 为什么重要

PBR 材质不只需要直接光,还需要环境间接光。

最终大概可以理解成:

最终光照 = 直接光 + IBL 漫反射 + IBL 高光反射

如果没有 IBL:

金属会很黑;

暗部缺少环境颜色;

光滑物体没有真实反射;

材质很难融入场景。

有了 IBL 后,物体会更像“真的放在这个环境里”。

为什么 IBL 要预计算

理论上,一个点受到环境光影响时,要从半球很多方向采样环境贴图。 如果每个像素实时采样很多方向,太贵。

所以通常提前准备几张东西:

Irradiance Map:给漫反射用,模糊、低频。

Prefiltered Environment Map:给高光反射用,不同 mip 表示不同粗糙度。

BRDF LUT:把视角、粗糙度等部分计算预存成查找表。

这样运行时就能用少量贴图采样得到比较真实的环境光。

Unity 里怎么对应

Unity 里常见相关概念:

Skybox:环境来源之一。

Reflection Probe:采样环境反射,给金属、光滑表面使用。

Light Probe:给动态物体提供间接光信息。

Ambient Lighting:基础环境光设置。

URP/HDRP 的 PBR Shader:会结合这些数据计算环境漫反射和环境高光。

IBL 的代价

IBL 效果好,但不是免费。

环境贴图会占显存;

预过滤 mip 会增加贴图数据;

Reflection Probe 如果实时刷新,会有渲染成本;

多个探针混合也会增加复杂度。

移动端项目里,通常会控制反射探针数量、分辨率和刷新频率。

面试高分说法

IMPORTANT

IBL 是基于图像的光照,它使用环境贴图或探针记录周围环境的亮度和颜色,用来模拟间接光照和环境反射。PBR 中 IBL 通常分为两部分:漫反射部分使用卷积后的 Irradiance Map,高光反射部分使用预过滤的环境贴图和 BRDF LUT。它能让物体受到环境色影响,也能让金属、玻璃、水面等光滑材质反射周围世界。相比普通环境光,IBL 包含方向、颜色和亮度变化,效果更真实,但会增加贴图内存、探针更新和采样成本。

BRDF 是什么?

一句话理解

BRDF 是 Bidirectional Reflectance Distribution Function,双向反射分布函数。它描述的是:一束光从某个方向照到材质表面后,有多少光会被反射到观察方向

rendering-brdf-explained

BRDF 解决什么问题

同样一束光照到不同材质上,表现完全不同。

粉笔、布料:光会向很多方向散开,主要表现为漫反射。 金属、玻璃、湿地面:光会更集中地反射,产生明显高光。 粗糙塑料:既有漫反射,也有比较宽的高光。

BRDF 就是用数学函数描述这些“材质如何反射光”的规则。

可以简单记成:

BRDF = f(N, L, V, Material)

其中:

N 是表面法线;

L 是光照方向;

V 是观察方向;

Material 是材质参数,比如粗糙度、金属度、基础颜色。

它和最终颜色的关系

BRDF 本身不是最终颜色。 它是渲染方程中的一部分。

最终光照大概可以理解成:

出射光 = 入射光 * BRDF * NdotL

也就是说:

光有多强;

光从哪个方向来;

表面朝不朝向光;

材质怎么反射光;

这些一起决定最后你看到的颜色。

漫反射 BRDF

最经典的是 Lambert 漫反射。

它认为光打到粗糙表面后,会比较均匀地向各个方向散开。

常见形式:

DiffuseBRDF = Albedo / PI

这里 Albedo 是物体基础颜色。 除以 PI 是为了能量守恒,让反射出去的总能量不会凭空变多。

镜面反射 BRDF

镜面反射描述高光和反射。

传统模型里可能用 Phong 或 Blinn-Phong。 现代 PBR 里更常用 Cook-Torrance 微表面模型。

它的大概形式是:

SpecularBRDF = D * F * G / (4 * NdotL * NdotV)

这三个字母很重要:

D:Normal Distribution Function,法线分布函数。 描述微小表面朝向是否集中。粗糙度越低,微表面越整齐,高光越小越亮。

F:Fresnel,菲涅尔项。 描述不同观察角度下反射强度变化。视线越接近掠射角,反射越强。

G:Geometry,几何遮蔽项。 描述微表面之间互相遮挡、互相阴影的情况。

粗糙度怎么影响 BRDF

粗糙度低: 微表面排列更整齐,反射方向更集中,高光小而亮。

粗糙度高: 微表面方向更乱,反射被分散,高光大而暗。

所以粗糙度不是简单让物体“变暗”,而是改变反射能量的分布方式。

金属度怎么影响 BRDF

非金属: 通常有明显漫反射,镜面反射颜色偏白。

金属: 漫反射很弱甚至没有,反射颜色会被金属本身颜色染色。

这就是为什么金、铜这类金属的反光会带颜色,而普通塑料的高光更接近白色。

面试高分说法

NOTE

BRDF 是双向反射分布函数,用来描述材质表面如何把某个入射方向的光反射到某个观察方向。它是 PBR 光照计算的核心,输入通常包括法线、光照方向、观察方向和材质参数。传统模型里有 Lambert、Phong、Blinn-Phong;现代 PBR 常用 Cook-Torrance 微表面 BRDF,把镜面反射拆成 DFG 三项:法线分布、菲涅尔和几何遮蔽。一个合理的 BRDF 应该满足能量守恒,不能让反射光能量超过入射光。

Metallic/Roughness 工作流是什么?

一句话理解

Metallic/Roughness 是 PBR 常用材质工作流:用 Metallic 决定材质像不像金属,用 Roughness 决定表面反射是清晰还是模糊。

rendering-metallic-roughness-workflow

Metallic 是什么

Metallic 金属度一般是 0 到 1

0 表示非金属,比如木头、布料、石头、塑料。 1 表示金属,比如铁、金、铜、铝。

非金属通常有明显漫反射,物体本色主要来自 BaseColor。 金属几乎没有漫反射,主要靠镜面反射表现,而且反射颜色会被 BaseColor 染色。

所以同样一张 BaseColor,在非金属和金属里含义不完全一样:

非金属:BaseColor 更像表面本色。 金属:BaseColor 更像反射颜色。

Roughness 是什么

Roughness 粗糙度一般也是 0 到 1

0 表示非常光滑,反射清晰,高光小而亮。 1 表示非常粗糙,反射模糊,高光大而暗。

它的底层含义是:微表面法线分布有多乱。

表面越光滑,微表面越整齐,反射方向越集中。 表面越粗糙,微表面越杂乱,反射方向越分散。

所以粗糙度不是简单地让材质“变暗”,而是改变反射能量的分布。

工作流里常见贴图

BaseColor:基础颜色。 Metallic:金属度。 Roughness:粗糙度。 Normal:法线贴图,改变表面细节方向。 AO:环境遮蔽,压暗缝隙和接触区域。 Emission:自发光。

这些输入进入 PBR BRDF 后,再结合直接光和 IBL,得到最终材质表现。

Unity 里的重点坑

Unity 很多管线里更常说 Smoothness,而不是 Roughness

它们是反过来的:

Smoothness = 1 - Roughness

所以:

粗糙度高,Smoothness 低。 粗糙度低,Smoothness 高。

如果美术给的是 Roughness 贴图,但 Shader 需要 Smoothness,就要反相处理。 这个在 Unity 项目里很常见。

贴图通道也要注意

很多项目会把贴图合并到不同通道里,比如:

Metallic 放 R 通道;

AO 放 G 通道;

Roughness 或 Smoothness 放 A 通道。

具体怎么放没有绝对统一标准,要看项目和 Shader 约定。

还有一点很重要: MetallicRoughnessAOMask 这类是数据贴图,不是颜色贴图,通常不要按 sRGB 做颜色转换。

面试高分说法

IMPORTANT

Metallic/Roughness 是 PBR 中常见的材质描述方式。Metallic 决定材质是非金属还是金属:非金属主要有漫反射,镜面高光通常偏白;金属几乎没有漫反射,主要靠镜面反射表现,并且反射颜色会被 BaseColor 染色。Roughness 决定微表面法线分布的分散程度,粗糙度越低,高光越锐利、反射越清晰;粗糙度越高,高光越宽、反射越模糊。Unity 里常用 Smoothness,它和 Roughness 是反向关系,也就是 Smoothness = 1 - Roughness

法线贴图为什么通常偏蓝?

一句话理解

法线贴图通常偏蓝,是因为最常见的切线空间法线贴图里,平坦表面的默认法线大约是 (0, 0, 1),编码成 RGB 后就是 (0.5, 0.5, 1.0),也就是接近 (128, 128, 255),蓝色通道最大。

rendering-normal-map-why-blue

法线贴图不是普通颜色图

法线贴图里的 RGB 不是在表达“这个地方是什么颜色”,而是在表达“这个像素的表面朝向”。

也就是说:

R 通道通常表示 X 方向;

G 通道通常表示 Y 方向;

B 通道通常表示 Z 方向。

它是一张数据贴图,不是颜色贴图。

为什么会偏蓝

法线方向本来是一个三维向量,范围通常是 -1 到 1

但贴图颜色通道一般是 0 到 1,所以需要编码:

RGB = Normal * 0.5 + 0.5

对于一个平坦表面,它在切线空间里的默认法线通常是:

Normal = (0, 0, 1)

代入公式:

R = 0 * 0.5 + 0.5 = 0.5
G = 0 * 0.5 + 0.5 = 0.5
B = 1 * 0.5 + 0.5 = 1.0

所以得到:

RGB = (0.5, 0.5, 1.0)

换成 8 位颜色就是:

(128, 128, 255)

这就是一种偏蓝紫色的颜色。

为什么蓝色通道通常最大

因为大多数表面的小凹凸不会让法线完全倒向侧面。 它们只是稍微往左、右、上、下偏一点,但整体仍然朝着表面外侧,也就是切线空间的 +Z

所以:

红色 R 表示左右偏移,通常在 0.5 附近波动;

绿色 G 表示上下偏移,通常在 0.5 附近波动;

蓝色 B 表示朝外程度,通常接近 1。

于是整张法线贴图看起来就偏蓝。

是不是所有法线贴图都偏蓝?

不是。

偏蓝的一般是切线空间法线贴图,也就是游戏里最常见的那种。 如果是对象空间法线贴图或世界空间法线贴图,颜色可能会更花,不一定整体偏蓝。

Unity 里的注意点

Unity 里法线贴图要标记为 Normal Map。 引擎会按法线数据去解码,而不是按普通颜色图处理。

还有一个常见坑: DirectX 和 OpenGL 对绿色通道方向的约定可能不同。 所以有些法线贴图导入后,凹凸方向反了,就需要翻转 G 通道。

面试高分说法

IMPORTANT

法线贴图通常偏蓝,是因为游戏里最常用的是切线空间法线贴图。平坦表面的默认法线方向是 (0,0,1),而贴图 RGB 只能存 0 到 1,所以会把法线从 [-1,1] 编码到 [0,1],公式是 RGB = Normal * 0.5 + 0.5。这样 (0,0,1) 会变成 (0.5,0.5,1),也就是约等于 (128,128,255),蓝色通道最大。R 和 G 表示切线平面内的偏移,B 表示朝外方向,所以大多数切线空间法线贴图看起来都是蓝紫色。

文章评价

读完这篇,留下你的看法

暂无审核通过的评价。

登录账号后才能评价。

本站访客数0总站访问量0本页访问量0