ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Marigold v2单图反照率估计:本征分解与PBR材质实战

Marigold v2单图反照率估计:本征分解与PBR材质实战

做材质重建的人多半遇到过这种场面:拿到一张网上找的产品图或别人拍的实景照,想把它变成可复用的材质贴图,结果颜色怎么调都不对——提亮暗部,高光立刻爆掉;整体压暗,白色又发灰。问题往往不在调色手艺,而在于那张图里每个像素其实是“物体本色”和“当时光照”乘出来的结果,两者被固定在一起了。把这两项拆开,就是反照率估计(albedo estimation)。Marigold v2 做的事情正是这个:给它一张普通照片,它输出一张去掉光照影响的“本色图”,以及一张描述光照分布的 shading 图。

这篇内容写给两类人:一类是想把照片转成 PBR 材质、做重光照或做图像编辑的从业者,另一类是已经跑通了深度估计、想把同一套扩散先验延伸到本征分解的开发者。我会把整条链路拆开讲——反照率在数学上到底是什么、Marigold v2 凭什么能从单张图里把它抠出来、环境怎么搭、参数怎么调、输出怎么对齐、以及我在实际项目里踩过的那几个坑。全文偏实操,代码是示意结构,具体函数签名请以你本地安装的版本为准,但参数含义和调参逻辑是通用的。

1. 反照率到底在估什么:把“物体本色”从光照里拆出来

1.1 一张照片里的颜色其实是乘法结果

先把概念理清楚,不然调参全靠猜。朗伯表面的成像模型可以写成I = A × S,其中I是相机拍到的像素值,A是反照率(albedo),S是着色项(shading,包含光源方向、强度、阴影、环境光遮蔽)。反照率的物理含义是“这个表面把多少入射光漫反射回来”,它只跟材质本身有关,不随光照变化。白色陶瓷杯的反照率接近 0.8,黑色绒布可能只有 0.03,这个数值跟你在什么灯光下拍它没关系。

这个乘法关系带来一个很麻烦的性质:单张图求解是病态问题。给你一个像素值 0.3,它可能是反照率 0.3 配中性光,也可能是反照率 0.6 配半强度的阴影,还可能是反照率 0.9 配很暗的角落。无穷多组解,数学上没法唯一确定。传统方法要么依赖多张不同光照的序列(光度立体),要么依赖人工交互画几笔标注,纯单张图基本做不动。

人眼为什么能做得不错?因为视觉系统带了强先验:物体表面颜色通常是大块均匀的、光照变化通常平滑连续、阴影有明确的几何边界、高光出现在特定位置。这些先验本质上是对“什么样的分解结果看起来合理”的判断。深度模型要做的,就是把这套先验学进去。

1.2 为什么扩散模型恰好擅长这件事

Marigold 系列的核心思路,是把预训练的文生图扩散模型“挪作他用”。这类模型在海量自然图像上训练过,它的去噪网络内部编码了极强的图像统计先验——什么样的图看起来像真实世界的图。反照率图虽然没人专门拍过,但它和自然图像共享大量结构特征:大块同色区域、边缘与物体轮廓对齐、纹理尺度合理、不含高频的光照斑点。

所以把反照率图当作“另一种图像”塞进潜空间去做去噪,模型原有的先验就能直接迁移过来,充当那个隐式的正则项,把病态问题约束成一个合理答案。这也是为什么这类方法在小样本微调后就能出效果——它学的不是“反照率长什么样”,而是“自然图像该有的样子”,后者数据量足够大。

Marigold 第一代验证了这条路对单目深度估计可行,v2 把输出从单通道的深度图扩展到了多通道的本征分解量。这个变化的难点在于:深度图是单通道的,天然带尺度不确定性,可以用仿射不变的方式处理;而反照率是三通道彩色的,每个通道都可能存在独立的增益和偏置,对齐的自由度一下变成六个,稍不注意就出现整体偏色。

1.3 输出到底是几张图,各自怎么用

跑完一次推理,你通常拿到的是一组图:反照率图、着色图、以及可选的深度和法线。它们的用途完全不同,别混着用。

反照率图是“材质底图”,理想状态下它不应该包含任何阴影、高光、渐变光斑,只剩物体的固有色和纹理细节。这张图可以直接拿去做 PBR 材质的 basecolor,也可以作为图像编辑的输入——比如你想把白天改成黄昏,只需要换掉 shading,反照率保持不变,物体的本色就不会跟着偏。

着色图是“光照分布”,它记录了每个位置的受光强度。这张图本身观感很差,灰扑扑的看不出内容,但它是重光照的关键:把它替换成一个新的光照场,再和反照率相乘,就能得到重光照后的结果。

深度和法线是副产物,主要价值在于交叉验证。你可以用估计出的法线生成一个简单的朗伯着色,再和反照率相乘,看结果和原图的差距有多大——差距集中在哪,基本就说明反照率在哪个区域估错了。这个自检手段后面会详细讲。

提示:反照率图的观感通常比原图“平”,这是正常的。如果输出看起来和原图几乎一样,八成是光照没被拆掉,模型退化成了恒等映射。

2. 环境搭建:显存、依赖和模型权重的现实约束

2.1 依赖版本与显存档位

这类基于扩散的推理管线对版本相当敏感。我遇到过最典型的问题是:主干网络和调度器的版本对不上,导致加载权重时静默丢层——不报错,但输出全是噪声。建议做法是把关键依赖固定到明确版本,并且加载完后打印一次模型参数量,和权重文件的实际大小对一遍,数量级对不上立刻回滚版本。

显存方面,给几个实测档位参考。单张 768×768 输入、半精度、集成次数 1 的情况下,推理峰值大概落在 4 到 6 GB;如果开集成 5 次,显存不会翻五倍(因为可以串行),但耗时差不多是线性增长。全精度下同样的配置要 10 GB 起步。如果你想跑 1024 以上分辨率又不分块,16 GB 卡会相当紧张。

我的建议是:先在半精度、768 分辨率、集成 1 次的配置下把整条链路跑通,确认输出合理,再去加集成、加分辨率。反过来做的话,一次失败你要排查的变量太多,非常浪费时间。

2.2 权重下载与目录组织

权重文件通常几百 MB 到几个 GB,本地一定要建规范的目录,别都堆在下载文件夹里。我自己的习惯是按“任务 + 版本 + 精度”三层组织:

checkpoints/ intrinsic/ v2/ fp16/ # 日常推理用 fp32/ # 需要精确对齐或做定量评测时用 config.json

这样做的实际收益是:做对比实验时,你可以同时挂载两个版本的权重,用同一批输入生成两份结果,差异一目了然。我做 v1 到 v2 的迁移评估时就是靠这个结构,省了大量来回切换的时间。

另外提醒一句,权重下载完最好校验一次哈希。扩散模型的权重文件损坏不会导致加载失败,只会让输出质量莫名其妙地下降,这种问题排查起来极其折磨人。

2.3 首次跑通的最小脚本

下面这段是结构示意,重点看参数含义和调用顺序,具体签名以本地版本为准:

import torch from PIL import Image # 1. 加载管线:半精度、指定设备 pipe = load_pipeline( ckpt="intrinsic/v2/fp16", dtype=torch.float16, device="cuda", ) pipe.enable_attention_slicing() # 显存紧张时打开 # 2. 读图并统一到 RGB img = Image.open("input.jpg").convert("RGB") # 3. 推理 out = pipe( img, num_inference_steps=25, # 去噪步数 ensemble_size=5, # 集成次数 processing_resolution=768, # 内部处理分辨率 match_input_resolution=True, # 输出缩回原始尺寸 output_type=["albedo", "shading"], generator=torch.Generator(device="cuda").manual_seed(20240101), ) albedo = out["albedo"] # float32, 形状 [H, W, 3], 归一到 [0, 1] shading = out["shading"] # 同上,单次估计的照射分布

这里有几个容易被忽略的点。match_input_resolution=True会把输出双三次插值回原图尺寸,方便直接相乘,但插值本身会损失一点锐度;如果你要做像素级精确的重光照,建议保持内部分辨率输出,在合成阶段再统一尺寸。另外,显式传入 generator 并固定种子很重要,否则集成过程中的随机采样每次都不一样,做 A/B 对比时你分不清差异是来自参数还是来自随机性。

跑通之后第一件事不是调参,是检查输出值域。把 albedo 的 min/max/mean 打出来,正常应该在 0 到 1 之间,均值落在 0.3 到 0.55 区间。如果出现大量负值或超过 1 的值,说明后处理里的裁剪没做,或者对齐环节出了问题。

3. 推理参数怎么调:步数、集成次数与分辨率的三角关系

3.1 去噪步数不是越多越好

扩散模型的去噪步数直接影响细节和耗时,但收益是递减的。我做过一组对比:单张室内场景图,固定其他参数,只改步数,从 10 步加到 50 步。结果是最明显的跨度出现在 10 到 25 步之间——10 步时反照率图的边界发虚,物体边缘会渗透到背景里;25 步时结构基本稳定;再往上加,肉眼几乎看不出差异,但耗时翻倍。

原因是这类方法输出的不是“生成内容”,而是“从输入图里提取的结构”,信息量有上限。步数太少,去噪没收敛,输出里残留噪声;步数过多,只是在已经收敛的解附近做无效游走。我现在的默认值是 25 步,赶时间用 20,做最终交付用 30。

还有一个更省时间的做法是从中间步启动,也就是跳过前面的纯噪声阶段,从一个加噪的输入 latent 开始。这样能把耗时压到三成左右,代价是输出的多样性下降——反正是确定性任务,多样性本来也没用。但要注意,起点不能设得太靠后,否则输入图的引导丢失,反照率会退化成原图的一层模糊滤镜。我的经验是不要跳过总步数的 60%。

3.2 集成次数与批大小的取舍

集成(ensemble)的做法是多次随机采样,然后取中位数或均值。这个机制存在的意义是降低单次采样的方差——扩散模型在保证质量的同时会引入波动,同一张图跑两次,反照率的具体数值会有几个百分点的差异,阴影边界的位置也可能小幅漂移。

次数怎么选?1 次够跑通,3 次能明显压掉噪声,5 次是我实际项目里最常用的档位,10 次以上的边际收益已经很难用肉眼分辨,除非你在做定量的统计评测。有个小技巧:做集成时用中位数而不是均值。光照边界处的误判往往是单侧的异常值,均值会被拉偏,中位数更稳。

批大小和集成次数容易混淆。集成是对同一张图做多次采样,批大小是同时处理多张不同的图。前者降低方差,后者提升吞吐。如果你的显存够,把批大小开到 4 到 8 是纯赚的;但要注意批内不同图的输出必须一一对应,别把顺序搞乱,我见过有人因为这个把 albedo 和 shading 配错了,排查了大半天。

参数典型取值调大的影响调小的风险
num_inference_steps20–30细节更稳,耗时线性增加低于 15 时边缘发虚
ensemble_size3–5方差下降,耗时线性增加为 1 时阴影边界可能抖动
processing_resolution768–1024细纹理保留更好,显存翻倍低于 512 小纹理整片丢失
denoise 起点0–0.4×总步数越快越省,多样性下降超过 0.6 引导失效,退化成滤镜

3.3 分辨率的坑:原生尺度最稳,超了必须分块

扩散模型对分辨率很敏感,因为训练时的位置编码是绑定在特定尺度上的。放到原生尺度附近(这类模型通常是 768 左右)效果最好,直接拉到 2048 会出问题:不仅是显存爆掉,更麻烦的是输出会出现规律性的重复纹理或整体偏色,因为位置编码外推失效了。

正确的做法是分块加融合。把大图切成带重叠的 768 块,每块单独推理,然后按权重(比如高斯权重,越靠中心权重越高)把重叠区域融合起来。这个方案能保留细节,但引入两个新的坑:一是块与块之间的整体亮度可能有微小差异,直接拼会看到网格状接缝;二是反照率整体偏色的自由度在每块上是独立的,拼出来可能左边偏暖右边偏冷。

解决第一个坑靠重叠权重融合,解决第二个坑靠全局对齐——把所有块的结果都对齐到同一张参考图(通常是原图下采样后的版本)上,用最小二乘拟合每块的增益和偏置,消除块间不一致。这套流程我写成了固定脚本,跑大图的时候基本是标配。

4. 单图推理的完整流程:从读图到落地成反照率贴图

4.1 输入预处理:色彩空间比你想的重要

这一步最容易被跳过,但出了问题是灾难性的。手机拍的图很多带 Display P3 或更广的色域配置,直接用常规方式读图会丢掉 ICC 信息,按 sRGB 解释,结果就是整体偏色,饱和度看着正常但红色会偏橙,绿色发灰。而反照率估计本身就是个“抠颜色”的任务,输入偏一点,输出偏一片。

我的处理流程是:读图时保留 ICC,用色彩管理库转换到 sRGB,再交给模型。如果你的场景允许,最好在转换后再做一次简单的白平衡检查——找画面里最可能是中性色的区域(白墙、灰卡、水泥地),看三个通道均值是否接近。差距超过 8% 就值得警惕,可能是原始拍摄的白平衡就偏了。

还有一件事是线性化。模型的输入通常是 sRGB 编码的非线性值,而反照率的物理意义是线性反射率。大多数管线内部会处理这个转换,但如果你要自己做后处理乘法,务必分清手上这张图在哪空间。sRGB 到线性的转换公式是:

def srgb_to_linear(c): c = c / 255.0 return torch.where(c <= 0.04045, c / 12.92, ((c + 0.055) / 1.055) ** 2.4)

反过来是linear_to_srgb,指数换成 1/2.4。这两个函数一定要写对,写错了会得到典型的“灰蒙蒙”或“发死黑”的结果,而且很难从观感反推是哪里错的。

4.2 推理与输出解读:怎么看懂那张灰图

推理本身没什么好说的,关键在于怎么读输出。反照率图初看会觉得“太平了”——所有阴影都没了,整个画面像被均匀打光。这是对的。但你要看的不是整体观感,而是几个具体位置:

第一,看暗部区域的色相。原图里一片深色阴影,反照率里应该还原成它本来的颜色。如果原来是一面深绿色的墙,在阴影里看着接近黑色,反照率里应该能看出偏绿的深灰。如果输出还是一片死黑,说明光照没拆干净。

第二,看高光区域。白色物体的镜面高光在反照率里应该被压回材质的基色。如果高光位置还是一片惨白,那就是没分开。

第三,看物体与物体之间的颜色关系。原图里因为光照不均,两个物体看着亮度不同,但在反照率里,它们如果材质相近就应该呈现相近的亮度。这个判断需要你对场景有先验认知,但对着实物比对一下很容易发现异常。

着色图则完全是另一种读法。它看起来像一张模糊的灰阶图,可能是单通道也可能是三通道(取决于版本),你主要看它的梯度走向:光源方向在哪个位置、阴影边界落在哪里。检查一下这个光照分布是否和你对原场景的判断一致——窗户应该在某个方向,室内灯光应该有明确的衰减。如果着色图完全是乱的,那反照率再好看也不可信,因为两个量是一起解出来的,一个错另一个基本也错。

4.3 后处理:色偏校正与动态范围压缩

模型输出的反照率是“相对”的,整体增益和偏置存在不确定性。直接拿去做材质会出现整体偏亮或偏暗。标准的对齐方式是对每个通道做一次线性最小二乘,把输出映射到输入图的亮度分布上:

def align_channels(albedo, image, mask=None): """ albedo, image: [H, W, 3], float, 0-1 对每个通道拟合 a * albedo + b ≈ image """ out = torch.zeros_like(albedo) for c in range(3): x = albedo[..., c].flatten() y = image[..., c].flatten() if mask is not None: m = mask.flatten().bool() x, y = x[m], y[m] A = torch.stack([x, torch.ones_like(x)], dim=1) coef, *_ = torch.linalg.lstsq(A, y.unsqueeze(1)) a, b = coef[0, 0], coef[1, 0] out[..., c] = (a * albedo[..., c] + b).clamp(0, 1) return out

这里有个重要的权衡必须说清楚:对齐越彻底,色偏消除得越干净,但光照残留也越多。因为原图里本来就含着光照信息,你把输出硬往原图上拟合,等于把一部分 shading 又“贴”回反照率里了。所以对齐要克制。

我的做法是只用亮度(三通道平均)来拟合单一的增益和偏置,而不是逐通道独立拟合。这样能校正整体明暗,但不会破坏三通道之间的比例关系,色偏问题留给前面的色彩空间处理去解决。只有在明确知道输入色彩空间正确、但输出仍有系统性色偏时,才考虑逐通道对齐,而且要对齐系数做个裁切,比如限制在 0.8 到 1.25 之间,防止过拟合。

动态范围压缩也需要提一句。反照率理论值域是 0 到 1,但模型输出会有少量越界。直接 clamp 到 [0,1] 会把高光附近的细节压平。稍微好一点的做法是先做一个软压缩,比如对超过 0.95 的部分做平滑衰减,保住层次感。这个细节在交付材质贴图时体感明显,尤其是浅色材质。

5. 踩坑记录:反照率估计最容易翻车的几种情况

5.1 阴影烧进反照率:最常见的失败模式

这是我最常遇到的问题,也是新手最容易忽略的。画面里有一块明确的投射阴影(比如窗框投在墙上的影子),理想情况下反照率图里应该完全看不出这块阴影的边界,因为阴影是光照的属性不是材质的属性。但实际输出里,阴影边界往往还留着一条淡淡的痕迹。

为什么会这样?因为模型看到的是一个“暗区域 + 明确边界”的模式,这个模式在它的先验里高度关联于“深色物体”。它没有办法百分之百确信这是阴影而不是材质,于是给了个折中答案,把一部分暗度留在了反照率里。阴影越硬、边界越锐利、和周围反差越大,这个残留越明显。

处理办法有几条。一是加大集成次数,取中位数能压掉一部分单次误判。二是分层处理:先用深度或法线估计把场景的几何结构拿到,判断哪些区域是“背光面”,在这些区域上给反照率做一次局部提亮补偿。三是接受它——如果下游用途是风格化的重光照,这点残留完全看不出来;只有做严格的材质扫描复现时才需要死磕。

有个反直觉的观察:软阴影比硬阴影更难处理。硬阴影边界清晰,模型至少能识别出“这里有个边界”;软阴影是渐变过渡,模型会误以为是材质本身的光滑渐变(比如曲面),结果把整个渐变都吸收进反照率里,反而更糟。

5.2 高饱和物体被“灰化”

纯色物体是另一个重灾区。一面正红色的墙、一件亮黄色的衣服,输入进去,输出的反照率往往会明显降低饱和度,红色变成砖红,黄色变成土黄。

根本原因还是先验。自然图像里纯粹的、大面积的、高饱和度的颜色相对少见,模型在训练中见得更多的是被光照调制过的、带各种中间色的版本。当你给它一个高饱和输入时,它的先验会把结果往“更常见的分布”拉,也就是往灰里拉。这不是 bug,是贝叶斯意义上的合理行为,但对做材质的人来说就是精度损失。

缓解手段有两个方向。一是在后处理里做一次温和的饱和度补偿,用 HSV 空间把 S 通道按比例放大,比例系数根据你对自己场景的判断来定,我通常用 1.1 到 1.2,再高就会出现色阶断裂。二是换一个思路:如果这个物体的真实颜色你有把握(比如是自己拍的标准色卡),那就别信模型的绝对值,直接用它做参考色做全局校正。

5.3 金属和镜面:模型的能力边界

这一点必须提前说清楚,因为它不是调参能解决的。反照率模型建立在朗伯漫反射假设之上,也就是“入射光向各个方向均匀反射”。金属和镜面材质根本不服从这个假设——它们的反射是方向性的,高光位置的颜色反映的是光源或环境,不是材质本身。

所以拿一张不锈钢水壶的照片去估反照率,输出一定是错的。高光区域会被强行解释成“一块很亮的白色材质”,而实际上那是一块反射了窗户的镜面。这种错误无法通过增加步数或集成次数修正,因为物理模型就不匹配。

实际工作中的处理方式是识别加排除。用估计出的法线配合简单的光照分析,找出高光区域(法线方向与视线方向接近对称于光源方向的位置),把这些区域的估计结果标记为低置信度,在下游使用时用周围区域的材质做填充,或者干脆接受这块区域需要人工修。我的经验是:产品图里金属件占比超过三成的话,纯自动流程的结果没法直接用,必须过一遍人工。

5.4 多视角批处理时的颜色不一致

做 3D 重建的朋友一定会撞上这个。同一个物体绕着拍了 20 张,逐张独立跑反照率,结果拼起来发现每张的色调都不一样,接缝处色差明显,肉眼一看就是拼的。

原因很直接:每张图是独立推理的,模型对“整体增益”存在不确定性,每张图各自收敛到一个略微不同的解。单看每张都合理,放一起就不一致了。

解决办法是强制全局一致。第一步,固定随机种子,让每张图的采样路径尽量接近,这能消掉一部分随机方差。第二步,选一张质量最好的作为参考帧,其余的用最小二乘对齐到它的颜色分布上。第三步,如果还有残留,就在重叠区域做泊松融合或者梯度域融合,把接缝抹平。这套流程做下来,一致性基本能满足视觉要求。

现象大概率原因处理方向
阴影边界残留硬阴影被误判为深色材质增加集成次数、几何辅助补偿
整体偏灰、饱和度低高饱和输入超出训练分布温和饱和度补偿或参考色校正
高光区变成死白镜面反射不满足朗伯假设识别并排除,人工修或邻域填充
多张图色差明显单张独立推理的增益不确定固定种子 + 参考帧对齐 + 融合
小纹理整片糊掉处理分辨率不足提高分辨率或分块推理

5.5 混合光源与室内场景

还有一个隐蔽的坑:混合光源。室外阳光加室内灯光、或者一个房间里有冷白灯和暖黄灯同时在照,这时候整个场景的 shading 不是单一光源能表达的。模型通常假设主导光源,把另一种颜色的光的影响“解释”成材质颜色,结果就是靠近某个光源的物体整体偏色。

判断方法很简单:看反照率图里有没有出现大片不符合物体真实材质的色偏。如果一面灰墙的反照率是暖黄色的,而你知道它就是灰的,那就是混合光源导致的误判。这种场景下,分区域处理比整图统一处理靠谱得多——按光源影响范围切分,各自做局部校正。

6. 结果怎么验:没有真值时如何判断好坏

6.1 重光照自检:最实用的验证手段

反照率估计的难点在于几乎没有真值可比。真实场景的反照率需要用专业设备在严格控光环境下采集,普通人拿不到。所以验证只能靠间接手段,其中最有效的是重光照测试。

具体做法:把你估计出的反照率图,乘以一个你自己构造的、已知的着色场(比如一个从左上方来的朗伯光照),得到一张新图。然后检查这张新图是否符合物理直觉——原来有阴影的地方现在应该没有阴影,原来背光的侧面现在应该均匀受光,物体的颜色关系应该保持一致。

如果重光照后的图里还能看出原来的阴影轮廓,说明阴影被烧进反照率了。如果某些区域的颜色在重光照后变得诡异(比如出现不该有的色相偏移),说明那块的估计本身有问题。这个方法不需要真值,纯靠视觉判断,但灵敏度相当高,我基本每次迭代都会跑一遍。

6.2 和法线、深度的交叉验证

Marigold v2 顺手能出法线和深度,这两个量可以用来做几何一致性检查。逻辑是这样的:如果反照率和法线都估对了,那么用它们加上一个估计的光照方向,重新渲染出的图像应该和原图高度接近。差异大的地方,要么是反照率错了,要么是法线错了,要么是光照模型不匹配。

实际操作中,我会把差异图当作“置信度热力图”来用:差异小的区域,反照率可信;差异大的区域,标记出来重点检查。这个手段在金属和镜面区域特别有效,那些地方的差异会异常大,正好把不可信区域圈出来。

需要注意的是,这种方法对纹理丰富的区域更敏感,对纯色平面反而不太灵——纯色平面的法线估计本身就带歧义,重渲染的差异说明不了问题。

6.3 定量指标的替代方案

如果要做客观对比(比如评估两个参数配置哪个好),拿不到真值的情况下可以构造代理指标。我常用的两个:

一是平坦区域一致性。在反照率图里手动框出几块理应是同一材质的区域(同一面墙、同一块布料),计算区域内像素的标准差。数值越小说明越均匀,估计越干净。这个指标对检测“光照残留”特别有效,因为光照残留会直接表现为区域内出现亮度梯度。

二是边缘对齐度。把反照率图的梯度幅值和原图的梯度幅值做相关性分析。理想情况下,反照率图的边缘应该主要来自材质边界(也就是原图里的真实物体边界),而不应该来自光照边界。如果两者相关性过高,说明光照信息没被剥离干净;相关性过低,说明模型把材质细节也抹掉了。这个指标需要一个合理的中间值,不是越极端越好。

两个指标都不完美,但配合视觉检查足够用了。我的习惯是每次改参数都记录这两项数值,做纵向对比,比单看某一次的绝对值有意义得多。


最后分享一点实际操作中的体会。反照率估计这件事,最大的认知转变是要接受“没有完美解”。因为问题是病态的,任何方法给出的都是一个合理的猜测,而不是唯一真相。调参的目标不是追求某个绝对正确的输出,而是让输出的错误模式落在你能接受的范围里——做重光照就盯着阴影残留,做材质提取就盯着细节保真,做图像编辑就盯着色相稳定性。目标不同,最优参数完全不同。

另外,如果你手上有同一场景的多张不同光照的图,别浪费,那是最有价值的监督信号。用单张估计的结果做初值,再用多张图做联合优化,质量能上一个台阶。单张图的价值在于“没有别的信息时也能给出一个像样的起点”,而不是替代真正的多光照采集。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进