你生成了一张棚拍人像。黑色背景、单灯柔光箱、人物戴眼镜。整张图都挺好看,直到你看到镜片:一边镜片里反着一个像窗户的东西带日光;另一边镜片反着完全不同的场景——可能是书架、可能是树。哪个都跟实际渲出来的环境对不上。两边镜片互相也对不上,照片的真实感瞬间崩掉。同样的问题还会出现在水面、镜子、抛光金属、甚至瞳孔 catchlight 上。
这是模型在一个具体的物理约束上翻车:反光必须是场景内容的某种变换,而且左右成对的反光面要一致。Diffusion 模型不懂光学物理——它只是 pattern-match 训练集里”眼镜反光通常长什么样”,而训练集里全是窗户和柔光箱。
常见原因
1. 模型对反光面没有场景一致性 prior
Diffusion 模型每块区域是半独立渲染的。左镜片和右镜片是从同一个 latent 出来但没有显式约束说要反同一个东西。训练集里眼镜带窗户反光的图特别多,每个镜片就独立地采样”这里可能是什么反光”。
怎么判断:把图其他部分挡住,只看两个镜片。如果像是从两张不同照片里抠出来的,那就是各采各的。
2. 背景 prompt 和反光 prompt 互相矛盾
你 prompt 写的是 “studio backdrop, seamless black, dramatic lighting”。但模型见过的”戴眼镜的人”图里带窗户反光的远远多于带黑棚反光的,prior 直接盖过 prompt。
怎么判断:反光内容是训练集里常见的东西(窗户、天空、树),跟你 prompt 里要求的场景没关系。
3. CFG 太低——prompt 在小区域上没被强制执行
CFG 3-5 这种低值,模型在镜片这种小细节区域上更依赖 prior。大形状听 prompt,小反光面不听。
怎么判断:CFG 7-9 时反光内容更可控,但大形状会变硬。
4. 没有显式给反光内容的 token
你写了 “wearing glasses” 但根本没说镜片里应该反什么。模型就用统计平均填补——日光窗户。
怎么判断:你的 prompt 里压根没有 “reflection” 或 “lens” 相关的内容描述。
5. 框型让镜片太小渲不动一个 coherent 场景
圆形 John-Lennon 框或者细长方框,1024x1024 下每个镜片只有 20-40 像素。没空间渲一个 coherent 的反光场景,就退回到”反正亮一点”。
怎么判断:同样设置下飞行员框或大框就 OK,小框翻车。
6. upscaler 在镜片区域瞎编了新东西
base 出图镜片反光其实还挺中性。然后 upscaler——尤其 denoise > 0.3 的 AI upscaler——在第二 pass 里凭空发明了全新的反光内容。
怎么判断:对比 base 输出和 upscale 输出。镜片区域内容如果实质性变了,就是 upscaler 干的。
7. 眼镜是后期 inpaint 加上去的
你生成的时候是没戴眼镜的人像,后面 inpaint 把眼镜加上去。Inpaint 对周围场景的上下文了解更少,特别容易凭空发明窗户反光。
怎么判断:工作流里有一个单独的”加眼镜 inpaint”步骤。
最短修复路径
Step 1: 显式加反光内容的 token
# 不要只写 "wearing glasses"
wearing glasses, lens reflections show studio softbox,
matching catchlights in eyes and lenses, no window reflection
通过明说你要什么 + 不要什么,把 prior 从”窗户”那边拉走。
Step 2: 在 negative 里加强力的反光内容否定
negative: window reflection in glasses, sky in lenses,
trees reflected in glasses, mismatched lens reflections,
double reflection
这种具体 negative 比泛泛的好用得多。
Step 3: CFG 稍微拉一点
5 → 7。CFG 高一点能让你的反光内容 token 在小区域上落地。如果整图在 CFG 7 太硬,那就 CFG 保持 7 但 hires fix 的 denoise 降低。
Step 4: 用 controlnet-depth 或 reference image 锁场景
给一张 depth map 或者简单的 reference image 表示你的棚拍布置(比如柔光箱在相机左侧)。模型就有真实的几何参考可以反进镜片里。
controlnet: depth, weight 0.7, end_step 0.6
reference: studio_setup_diagram.png
Step 5: 用同一个 mask 一起 inpaint 两个镜片
base 出完图,两个镜片用同一个 mask 一起蒙住(不是分别!),denoise 0.5 inpaint,prompt 强调反光一致:
both lenses showing the same soft studio reflection,
symmetric subtle highlight from above, no window,
no separate scene per lens
两个一起蒙 + prompt 写”两个镜片反同一个东西”是收益最大的一步。
Step 6: 终稿手动盖掉乱反光
客户活儿就别死磕了,PS 里 30 秒——克隆图章把一个干净镜片盖到乱的那个上面——比再生一轮稳得多。模型出 90%,剩下手动。
Step 7: 如果用 upscaler,镜片区域降低 denoise
如果你的工作流支持 region-specific denoise(比如 Ultimate SD Upscale 配 mask),把镜片区域 denoise 设成 0.15 而不是 0.4。少发明、多保留。
这些情况不是你的锅
SD 1.5 和 SDXL base 基本不懂几何反光。哪怕 prompt 写得完美,你得到的也只是”看起来合理”但物理上不对的结果。如果项目要求物理正确的反光(商业、产品图),用 3D 渲染或者合成。
另外人眼对眼镜反光特别敏感(我们天天看戴眼镜的人脸)。你觉得明显错的东西大多数观众根本不会注意——挑战要值得。
容易被误诊成
“眼睛位置不对”——瞳孔 catchlight 坏了看着很像镜片反光坏了,但修法不一样。瞳孔 catchlight 响应”光线方向” token;镜片反光响应”内容” token。别拿瞳孔的修法套到镜片上反之亦然。
也很像”背景溢出到主体”——都是不想要的场景内容出现在了不该出现的地方。但背景溢出的修法(更好的 mask、边缘 CFG 降低)这里没用,这是 generation 级别的 prior 问题,不是合成问题。
怎么预防
- 场景里有眼镜或者任何反光面,prompt 必须显式写反光内容。
- 默认 negative prompt 模板里加上 “window reflection in glasses, mismatched lens reflections”,特别是人像活儿。
- Inpaint 两个镜片要用同一个 mask 一起,绝对别一个一个搞。
- 商业人像最后做一次手动镜片清理 pass。
- 别用晚期 inpaint 加眼镜,在 base prompt 里就把眼镜放进去。
- 棚拍参考实际布光,走 depth controlnet。
FAQ
- 为啥两个镜片有时反完全不同的场景? 两个镜片区域是从同一个 latent 半独立采样的,没有显式的跨区域一致性 loss。一起 inpaint 一起 mask 能给模型一个协调它们的机会。
- 以后新模型会修这个吗? 大概率会——新的多模态模型(Imagen 3、GPT-4o image、FLUX)反光处理明显比 SD1.5/SDXL 好。如果反光是关键,去测这些。