1. 为什么要在Unity里复刻Photoshop的图层混合模式?
做游戏、做AR/VR、做数字孪生可视化,只要画面里出现“叠加”“透明”“光影融合”这类需求,你就绕不开图层混合——不是简单地把两张图叠在一起,而是让它们按数学规则发生化学反应。我最早在2015年做一款美术向解谜游戏时就踩过坑:美术给的PSD里用了“正片叠底”调暗阴影、“滤色”提亮高光、“叠加”增强纹理对比,结果导入Unity后全乱了——UI图标发灰、角色贴图变脏、特效边缘糊成一片。当时团队花三天手动重绘所有叠加效果,最后发现根本不是美术的问题,是引擎没理解PS那套混合逻辑。
Photoshop的混合模式本质是一套 像素级数学运算协议 ,它不依赖硬件加速,只定义“底图A和上图B在每个像素点上怎么算”,比如“正片叠底”就是 R = A×B(归一化后),而“滤色”是 R = 1-(1-A)×(1-B)。Unity默认的Blend Mode(如SrcAlpha/DstOneMinusSrcAlpha)只解决透明度混合,完全不管色彩交互。真正要还原PS效果,必须绕过内置渲染管线,用Shader逐像素实现27种标准混合公式(含“颜色加深”“线性光”“排除”等冷门但关键的模式),并处理好sRGB空间、Alpha预乘、伽马校正这三座大山。
这个项目不是炫技,而是解决实际卡点:电商商品展示需要PSD分层素材实时预览;工业设计评审要求Unity中精准还原设计师在PS里调好的材质叠加效果;教育类AR应用要让学生拖拽PS图层到3D模型上即时看到混合结果。关键词“Unity3D”“Photoshop”“图层混合模式”背后,是大量跨工具链协作场景的真实痛感——不是程序员想造轮子,是工作流逼你把PS的“视觉语言”翻译成GPU能懂的指令。如果你正在对接美术外包、做UI动效系统、或开发支持PSD导入的编辑器插件,这篇就是你省下两周调试时间的说明书。
2. 核心技术拆解:从PS公式到GPU Shader的完整映射
2.1 Photoshop混合模式的数学本质与Unity适配陷阱
Photoshop的混合模式不是魔法,而是27组确定性数学公式,每组都作用于RGBA四通道。但直接照搬会翻车,因为PS和Unity运行在 完全不同的色彩空间与数据假设 下。举个最典型的例子:“正片叠底”在PS里写为 R = A * B ,但如果你在Shader里真这么写,结果会比PS暗30%以上。原因有三:
- sRGB vs Linear空间 :PS在sRGB空间计算(人眼感知更均匀),Unity默认在Linear空间(物理渲染更准确)。sRGB值0.5对应Linear值约0.218,直接相乘会严重低估亮度。
- Alpha预乘问题 :PS的图层默认是非预乘Alpha(RGB独立存储,Alpha仅控制透明度),而Unity的Texture Import Settings勾选“Alpha is Transparency”时会自动转为预乘Alpha(RGB已乘Alpha)。若不统一,叠加时会出现半透明区域发黑或发白。
- Gamma校正残留 :老版本Unity(<2019.4)在非HDR管线中存在Gamma补偿残留,导致公式计算结果偏移。
解决方案不是“关掉sRGB”或“强制转Linear”,而是建立 双空间桥接层 :在Shader入口将输入纹理从sRGB解码为Linear( tex2D(_MainTex, uv).rgb 自动完成),执行PS公式计算,再将结果编码回sRGB输出(Unity的 #pragma target 3.0 及以上自动处理)。关键代码段如下:
// 正片叠底模式核心计算(已适配sRGB空间)
half3 Multiply(half3 base, half3 blend, half alpha) {
// base和blend均为Linear空间RGB值(0~1)
half3 result = base * blend;
// 注意:此处不乘alpha!PS混合模式操作的是RGB值本身,alpha由Blend State控制
return result;
}
提示:所有混合模式计算都 不参与Alpha混合 ,Alpha通道仅用于最终的透明度合成(由Unity的RenderState控制)。这是初学者最大误区——以为“正片叠底”要写成
base.rgb * blend.rgb * blend.a,实际PS中即使blend图层完全透明(a=0),其RGB值仍参与计算(只是结果被后续Alpha Blend覆盖)。
2.2 27种混合模式的分类实现策略
Photoshop官方定义27种混合模式,但可归纳为5类数学结构,每类用不同Shader技巧实现:
| 类别 | 代表模式 | 数学特征 | Unity实现要点 |
|---|---|---|---|
| 乘法系 | 正片叠底、颜色加深、线性加深 | f(A,B)=A*B 或 f(A,B)=max(0,A+B-1) |
使用 mul() 指令,注意Clamp避免负值 |
| 反乘法系 | 滤色、颜色减淡、线性减淡 | f(A,B)=1-(1-A)*(1-B) |
展开为 |
365




被折叠的 条评论
为什么被折叠?



