资讯动态

环境光揭秘:让阴影不再死黑的视觉魔法

发布时间:2026/10/16 1:40:58 来源:尧图企业网站定制
一句话环境光是不直接来自光源、而是在场景里反弹了一圈之后才到达表面的光。它让阴影不是一片死黑。背光面的亮度、室内角落的昏暗、角色站进山洞后整体变暗、白天屋檐下的淡蓝色调都由它决定。一、为什么需要环境光做个实验场景里只放一盏太阳光只计算直接光照漫反射 镜面反射☀ ↘ ● ← 朝向太阳的一面亮 ███ ← 背光面纯黑0, 0, 0 ═══════════ ▓▓▓ ← 影子里纯黑背光面和影子都是纯黑看起来像在外太空。但现实中你站在树荫下照样看得清地面。因为阳光照到地面、墙壁、天空中的空气分子再反弹到阴影里☀ ──▶ 地面 ──反弹──▶ 背光面 地面反射的光 ☀ ──▶ 大气 ──散射──▶ 背光面 天空本身就是一个巨大的蓝色光源直接光间接光环境光来源从光源直接射来经过其他表面或大气反弹方向明确的一个方向四面八方产生清晰的明暗、硬阴影柔和的整体亮度、阴影里的细节计算难度简单非常难要知道整个场景互相反弹的结果完整计算光的所有反弹叫全局光照Global IlluminationGI。环境光就是游戏为了近似全局光照发展出来的一系列方法。二、最简单的版本一个常数最早期以及很多入门教程的做法给所有像素加一个固定的亮度。vec3 ambient ambientColor * albedo; // 例如 ambientColor (0.2, 0.2, 0.2) vec3 color ambient diffuse specular;阴影不黑了但问题很明显阴影里的球 ⬤ ← 均匀的一片灰看不出是个球常数环境光和方向无关所以阴影里的物体完全失去了立体感像纸片一样扁平。而且它不分室内室外、不分角落还是空地整个场景都一样亮。后面所有的改进本质上都在解决两个问题环境光从哪个方向来让暗部有立体感这个位置能接收到多少环境光让角落、缝隙变暗。三、让环境光有方向1. 半球光Hemisphere Light户外的环境光大致是上面是天空亮、偏蓝下面是地面暗、偏地面颜色。按法线朝上还是朝下来混合float t N.y * 0.5 0.5; // 朝上 → 1朝下 → 0 vec3 ambient mix(groundColor, skyColor, t) * albedo;用数字看天空(0.4, 0.5, 0.7)、草地(0.1, 0.15, 0.05)法线方向t环境光朝上头顶1.0(0.40, 0.50, 0.70) 偏蓝、亮水平侧面0.5(0.25, 0.33, 0.38)朝下下巴0.0(0.10, 0.15, 0.05) 偏绿、暗只多了一行代码阴影里的球就有了上亮下暗的体积感角色下巴还带一点草地反上来的绿色。Unity 的Gradient环境光天空 / 地平线 / 地面三色就是它的扩展版。2. 球谐光照Spherical HarmonicsSH半球光只能表示上下两个方向。如果左边是一堵被阳光照亮的橙色墙右边是阴影就需要更精细的表示。做法从某个位置拍一张全景图Cubemap计算如果法线朝向 N从整个半球收到多少光这个结果叫辐照度IrradianceE(N) 把所有方向的入射光按 max(N·光线方向, 0) 加权后积分漫反射本身就是很模糊的法线差几度收到的光几乎不变。所以这个结果非常平滑可以用很少的数据近似。球谐函数就是一种压缩方法表示方式数据量辐照度 Cubemap32×32×66144 个颜色L2 球谐9 个系数 × RGB27 个数27 个数就能相当准确地描述四面八方的环境光查询只需要一些乘加// L1 球谐的简化示意4 个系数SH[0..3] 每个都是 RGB vec3 EvalSH_L1(vec3 N, vec3 SH[4]) { return SH[0] SH[1] * N.y SH[2] * N.z SH[3] * N.x; }可以看出半球光其实就是一个只有上下分量的极简球谐。3. 基于图像的光照IBL上一篇讲镜面反射时提到过 Cubemap。同一张环境图可以同时提供两种环境光环境漫反射环境镜面反射查询方向法线 N反射方向 R数据辐照度常用球谐存储预过滤 Cubemap按粗糙度取 Mip作用暗部的颜色和体积感暗部的反光、金属的外观vec3 ambientDiffuse EvalSH(N) * albedo; vec3 ambientSpecular textureLod(prefilteredEnv, R, roughness * maxMip).rgb * specTerm; vec3 ambient (ambientDiffuse * (1 - F) ambientSpecular) * ao;PBR 里环境光就是这两部分的和。所以上一篇说金属在没有环境的场景里会发黑金属没有漫反射它的环境光全部来自环境镜面反射。四、让环境光随位置变化一个场景只有一组环境光是不够的室外阳光充足山洞里应该很暗红色房间里的物体应该被染红。1. 光照贴图Lightmap——静态物体对于不会动的物体地形、建筑、墙壁离线把全局光照完整计算一遍光线追踪、多次反弹结果烘焙进一张纹理烘焙阶段编辑器可能要几分钟到几小时 模拟光线在场景中多次反弹 → 每个表面点存一个亮度 → 写进 Lightmap 运行时 color albedo × 采样(Lightmap, uv2) 实时直接光效果最真实、几乎零运行时开销角落天然变暗、颜色互相渗透红墙把白地板染红代价物体不能动光源不能变或只能在几套预烘焙结果之间切换占用内存需要单独的第二套 UVuv2不能重叠。2. 光照探针Light Probe——动态物体角色、敌人、可推动的箱子不能用光照贴图。解决办法是在场景里撒很多个采样点每个点烘焙一组球谐系数● ● ● ● ● 光照探针每个存 27 个数 ● ● ● ● 角色取周围几个探针按位置插值角色走进山洞时周围的探针变暗角色也随之变暗靠近红墙时角色侧面被染红。这样动态物体就能和烘焙的静态场景融为一体。Unity 的 Light Probe Group / Adaptive Probe Volume、Unreal 的 Volumetric Lightmap 都是这个思路。3. 实时全局光照光照贴图和探针都是预先烘焙的开关灯、昼夜循环、可破坏场景时就不对了。现代引擎开始实时计算间接光方案思路代表动态探针DDGI探针每帧用光线追踪更新多款光追游戏SSGI在屏幕空间里找附近表面的颜色当作反弹光很多引擎的可选项混合方案距离场 屏幕空间 光追 缓存Unreal 5 Lumen路径追踪完整模拟所有反弹高端画质模式五、环境光遮蔽Ambient OcclusionAO1. 问题缝隙和角落应该更暗即使环境光有了方向、有了位置变化探针和球谐也太粗了表示不了几厘米尺度的遮挡。比如墙角、桌子底下衣服褶皱、盔甲接缝角色站在地上时脚下的那一圈。这些地方被周围几何挡住了大部分方向的环境光现实中会明显更暗。2. AO 是什么AO 回答的是从这个点往外看半球内有多少比例的方向没有被挡住。空地上的点 墙角的点 缝隙深处的点 ╲ │ ╱ │ ╲ ╱ ║ ║ ── ● ── │ ● ║ ● ║ 全部方向都能看到天空 挡住约一半 大部分被挡住 AO 1.0 AO ≈ 0.5 AO ≈ 0.1然后只乘在环境光上vec3 color ambient * ao direct; // 注意不乘直接光3. AO 的几种来源来源说明尺度AO 贴图美术在建模软件中烘焙随材质一起提供模型内部的细节褶皱、铆钉光照贴图烘焙 GI 时天然包含场景级别SSAO / HBAO / GTAO每帧根据深度缓冲检查周围像素是否挡住了当前点物体之间角色脚下、物品与桌面光追 AO真正发射光线检测遮挡最准确AO 对画面的提升非常明显没有它物体像是飘在地面上加上之后物体有了接触感和重量感。六、案例案例 1阴影里一片死黑现象太阳光一挡影子里什么都看不见。原因完全没有环境光或环境光强度为 0。修复至少加上天空盒或半球环境光有条件就烘焙光照贴图和探针。案例 2阴影里的物体像纸片现象影子里的角色一片均匀的灰五官、肌肉轮廓全没了。原因使用了常数环境光与法线方向无关。修复换成半球光或球谐暗部立刻有了上亮下暗的体积感再叠加法线贴图细节就回来了。案例 3角色在昏暗走廊里像在发光现象场景是烘焙的非常暗但角色走进去依然明亮像贴上去的一样。原因场景用光照贴图角色却在用全局默认的天空环境光两者不一致。修复在走廊里放光照探针让动态物体从探针获取环境光。案例 4光线从墙后漏过来现象室内角色的一侧被染成了室外的天空蓝或者墙根处有一条亮缝。原因探针间距太大某个探针放在墙外室内角色插值时用到了它光照贴图分辨率太低一个纹素跨过了墙内外。修复探针沿墙壁两侧都要放或使用带可见性检测的探针系统如 DDGI 的距离信息、Unity APV 的漏光修正墙体加厚提高关键区域的光照贴图分辨率。案例 5大物体的光照一刀切现象一艘大船整体只用一个探针船头在阳光下、船尾在阴影里却一样亮。原因每个物体默认只取一次探针按包围盒中心。修复对大型动态物体使用逐像素采样的探针体积Light Probe Proxy Volume / Probe Volume。案例 6AO 把高光也压暗了出现黑晕现象开了 SSAO 之后被阳光直射的墙角也出现了一圈脏兮兮的黑边。原因AO 乘在了最终颜色上连直接光也被压暗。可直接光是否被挡住已经由阴影负责了。修复AO 只乘环境光以及环境镜面反射时使用专门的镜面遮蔽。案例 7SSAO 在物体边缘出现光晕现象角色站在远处的墙前轮廓周围出现一圈暗影像描了边。原因SSAO 只看深度缓冲把屏幕上相邻、但实际相距很远的墙误判成了遮挡物。修复加入距离衰减只统计在一定半径内的遮挡或者改用 GTAO 等更准确的算法。案例 8昼夜循环时影子颜色不对现象天已经黑了阴影里的建筑依然带着白天的暖色反弹光。原因光照贴图是在白天烘焙的间接光写死了。修复烘焙多套光照早、中、晚、夜并插值或者只烘焙可见性与天空遮蔽颜色在运行时根据当前天空计算或者使用实时 GI。案例 9环境光太强画面发灰现象整个画面对比度很低像蒙了一层雾。原因为了让暗部看得清把环境光强度调得很高直接光与环境光的比例失衡阴影已经不像阴影了。修复降低环境光、提高直接光用 AO 恢复层次需要看清暗部时用曝光和色调映射而不是硬加环境光。七、发展脉络常数环境光 ── 阴影不黑了但没有立体感 ↓ 半球光 / 三色梯度 ── 有了上下方向 ↓ 球谐 天空盒IBL── 有了任意方向漫反射与镜面反射都考虑 ↓ 光照贴图 探针 ── 有了位置变化室内暗、角落暗、颜色渗透 ↓ 环境光遮蔽AO ── 有了细小尺度的遮挡与接触感 ↓ 实时全局光照 ── 光源、场景可以动态变化八、总结问题答案环境光是什么经过反弹、从四面八方到达表面的间接光是对全局光照的近似为什么需要没有它背光面和阴影是纯黑的常数环境光的问题与方向无关暗部失去立体感如何加入方向半球光、球谐、IBL漫反射查法线镜面反射查反射方向静态物体光照贴图离线烘焙多次反弹运行时几乎零成本动态物体光照探针在空间中撒点存球谐插值获取小尺度遮挡环境光遮蔽AO 贴图、SSAO/GTAO只乘环境光动态场景实时 GIDDGI、SSGI、Lumen、路径追踪常见坑死黑、纸片感、动态物体与场景不融合、漏光、AO 压暗直接光、SSAO 光晕、昼夜不匹配、画面发灰一句话直接光决定物体哪里亮环境光决定物体暗的地方是什么样。半球光和球谐告诉暗部光从哪来光照贴图和探针告诉它这里有多少光AO 告诉它有多少光被挡住了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑