GetIblSpecular间接光高光IBL(basepass静态)
2025-11-10
| 2025-11-12
Words 1890Read Time 5 min
type
status
date
slug
summary
tags
category
icon
password

前言

先复习一下 很久之前学的间接光高光的第一部分算法:
notion image

工业化版本和简单采样cubemap的不同:

half3 IndustrialIBL(...)
{// 1. 平台检测 + 2. 贴图选择 + 3. Mip计算 // + 4. 采样 + 5. 天气系统 + 6. 昼夜混合// + 7. 能量守恒 + 8. 调试支持 }
为什么这么复杂?为什么不是简单的采样一个cubemap(有mip分级的)这么简单?
因为:
  • 实际游戏场景的复杂性。你做个单一的场景可以简单采样一个cubemap,但是实际游戏中,场景太多了。比如角色从室内走到室外,是不是cubemap图要变化?那你就需要这么一个机制,来让物体在合适的地方采样合适的cubemap(nx这里用的是 多反射探针 用 instance_id 映射不同物体对应的探针(比如角色实例绑定室外探针,家具实例绑定室内探针))
  • Mip 分级采样功能。这个不用说了。正常需要的
  • 动态场景变化。这个可以理解,同场景,不可能是静态,从日夜变化,天气变化(闪点阴晴),曝光调整等待。

解析:

找到对应探针

是通过 实例 ID + 映射表 的方式找到对应的探针。这玩意,其实差不多就是个哈希
哈希的本质是 “用一个‘键’(Key),通过某种规则找到对应的‘值’(Value)”—— 比如用 “用户名(键)” 找 “用户信息(值)”。
实例 ID + 映射表完全遵循这个逻辑:
  • 键(Key):物体的实例 ID(比如角色的 ID=10);
  • 映射规则:组号=实例ID/4 + 组内序号=实例ID%4(相当于哈希函数的 “哈希计算”);
  • 值(Value):映射表 u_env_cube_array_idx[组号][组内序号] 存储的 “探针索引(slice)”;
  • 目的:快速通过 “实例 ID” 找到 “探针索引”,和哈希 “快速查找” 的核心目标一致。
但是呢,你要理解这个具体是怎么做的,看这个文档:
int slice = 0;
#if REFLECTION_ENV_USE_CUBE_ARRAY
// 多反射探针场景:用实例ID映射探针索引(slice)
sv_instance_id = int(round(instance_id));
slice = u_env_cube_array_idx[sv_instance_id / 4][sv_instance_id % 4];
// 读取该探针的亮度参数(用于后续亮度平衡)
Brightness = u_env_cube_array_aabb_min[slice].w;
AverageBrightness = u_env_cube_array_aabb_max[slice].w;
#else
// 单反射探针场景:直接用全局亮度参数
Brightness = u_env_cube_aabb_min.w;
AverageBrightness = u_env_cube_aabb_max.w;
#endif
你看了前面的文档就知道了 这纯粹就是拿索引。(或者理解为 哈希值)
每个物体都有个id。这个id对应的二维数组的的确定位置是固定的。但是存储的值是基于cpu那边不断变动的。这个存储的值就是slice。
Brightness = u_env_cube_array_aabb_min[slice].w;
AverageBrightness = u_env_cube_array_aabb_max[slice].w;
这两是这样 属于小优化:
  • u_env_cube_array_aabb_min/max 本来是 “探针的包围盒数据”(xyz 通道存包围盒的最小 / 最大坐标,用来判断物体是否在探针覆盖范围内);
  • 引擎把包围盒的 w 通道(闲置通道)用来存 探针的亮度参数
    • Brightness:当前探针的 “局部亮度”(比如室外探针的 w=5.0,室内探针的 w=1.0);
      AverageBrightness:当前探针的 “平均亮度”(用来做亮度归一化,避免后续计算除以 0);后续用这两个参数平衡不同探针的高光亮度(比如室外探针的高光要亮,室内要暗)。
对了?为什么这里u_env_cube_array_idx 和id的对应关系是 4?
答案:优化,引擎优化成 “4 个物体一组”,每组共用一个探针索引 —— 反正相邻的 4 个物体大概率在同一个场景区域(比如都在室内),用同一个探针完全不影响效果,还能省内存。
Ok 这一步我们拿到索引了

mip分级-基于粗糙度

half level = ComputeReflectionCaptureMipFromRoughness(Roughness, 7.0h);
用的就是这个函数
这函数内是这样的:
half ComputeReflectionCaptureMipFromRoughness(half Roughness, half CubemapMaxMip)
{
half LevelFrom1x1 = 1.0 - 1.2 * log2(Roughness);
return CubemapMaxMip - 1.0h - LevelFrom1x1;
}
以下是LevelFrom1x1 的图像 显示roughness越低 这个越大 是一个清晰程度的量化值
最后通过 最大mip值 -1.0 -LevelFrom1x1 获得最终的(roughness越低 mip也越接近0)
notion image

采样对应 环境贴图

根据slice找到对应的cubemap,采样mip等级是level,使用反射向量R采样,同时用AverageBrightness做基础亮度校准。

场景动态效果适配(昼夜、闪屏)

// 昼夜切换:根据u_lightmap_factor混合白天/夜晚的IBL高光(夜晚暗化)
half3 ibl_night = min(ibl, 1.5h) * 0.3h; //min可以保证夜晚不会过亮 最大不超过0.5h
half3 ibl_temp = lerp(ibl, ibl_night, clamp(half(u_lightmap_factor), 0.0h, 1.0h));
ibl = lerp(ibl_temp, ibl, half(u_env_day2night_exposure.y));
// 闪屏特效:根据u_lightning临时调整高光亮度
ibl = lerp(ibl, ibl * u_lightning.y ,clamp(u_lightning.x, 0.0h, 1.0h)); //一个闪电 ibl*增强系数
// 移动端优化:直接叠加环境光颜色,增强氛围感(简化计算)
#if API_MOBILE_HIGH_QUALITY
ibl *= u_ambient.rgb;
#endif
这没啥好说的,基于某些变量,对ibl的值进行大小的调整

和vlm的协调

这里对ibl进行最终调整
这里对 ibl * 一个调整系数
half MixingAlpha = smoothstep(0.0h, 1.0h, saturate(Roughness * 5.0h + -0.5h));
half MixingWeight = IndirectIrradiance * Brightness / max(AverageBrightness, .0001h);
MixingWeight = min(MixingWeight, 10000.0)
Return lerp (Brightness, MixingWeight, MixingAlpha)
MixingAlpha 是 「IBL 高光的 “校准比例”」—— 控制 “是保留 IBL 原始高光权重(Brightness),还是采用‘对齐环境间接光的校准后权重(MixingWeight)
  • 低粗糙度(镜面):MixingAlpha≈0 → 不用校准,保留 IBL 原始高光(因为镜面需要锐利的方向细节,环境间接光对其影响小);
  • 高粗糙度(磨砂):MixingAlpha≈1 → 必须校准,用MixingWeight(因为磨砂材质的高光模糊,容易和环境间接光重叠,需要对齐亮度避免违和)。
MixingWeight 是 环境间接光亮度校准后的高光权重
MixingWeight = (VLM间接光亮度 + skysh间接光亮度) × (当前探针局部亮度 / 探针平均亮度)
特殊情况下,即vlm开启的情况下,IndirectIrradiance 是0。则MixingWeight是0,那么对于高粗糙度(磨砂)的材质来说,ibl高光是没有。(之后用vlm替代),但是对镜面的材质没有影响,依旧用Brightness。这很聪明,因为vlm主要是贡献漫反射,对于镜面来说,锐利的高光还需要IBL高光。但是对于粗糙表面,则全部使用vlm即可。

最终高光

最后还需要
half3 ibl_result = u_env_day2night_exposure.x * ibl *上面算的调整系数 * half(u_env_day2night_exposure.x);
u_env_day2night_exposure.x 是昼夜曝光系数,二次相乘是全局亮度微调(可能是历史优化留下的兼容逻辑,核心是统一控制高光亮度)。
  • HLSL
  • 渲染
  • 图形学
  • 各向异性-DGF中D和G各向异性的实现间接光漫反射理论基础
    Loading...