如何实现纹理向近黑色平滑融合且无色彩溢出伪影?
解决方案:平滑暗化纹理至近黑色且避免色彩溢出
问题本质
这种现象根源是低精度颜色格式的量化误差:当RGB值趋近于0时,通道的离散数值无法维持原色调比例,导致色彩失衡;直接线性暗化会放大暗部的精度损失。同时Unity中关闭环境光后,光照计算的暗部缺乏精度缓冲,进一步暴露了该问题。
核心数学方法
1. 色调保持的暗化算法
先提取颜色的色调(Hue)和饱和度(Saturation),仅对明度(Value/Luminance)做非线性压缩,确保暗部色调比例稳定:
- 将RGB转换为HSV色彩空间
- 对V通道应用平滑渐近压缩,比如用
V_new = 1 - exp(-k * V_old)的逆运算,或平滑阶梯函数避免V值突变到0 - 转换回RGB空间,保证通道值在量化范围内维持比例
2. 暗部精度补偿
在暗化后的颜色中加入极细微、与原色调一致的“锚点”值,避免通道值直接掉到0,同时不破坏视觉上的近黑色效果:
finalRGB = max(darkenedRGB, anchorRGB);
其中anchorRGB是原RGB比例乘以极小值(如0.001),既能维持色调平衡,又不会影响深色表现。
Unity URP 着色器实现示例
以下是基于URP Lit着色器修改的暗化处理代码,可集成到片元着色器阶段:
float3 SmoothDarkenToBlack(float3 rgb, float darkenFactor) { // 转换到HSV空间 float3 hsv = RGBToHSV(rgb); // 对明度通道做平滑压缩,避免线性衰减的精度问题 float v = hsv.z; // 指数平滑确保暗部过渡平缓 float smoothedV = 1.0 - exp(-v / max(darkenFactor, 0.001)); // 限制最小值,防止完全黑死 smoothedV = max(smoothedV, 0.0005); hsv.z = smoothedV; float3 result = HSVToRGB(hsv); // 添加色调锚点,维持暗部比例 float3 anchor = rgb * 0.0008; result = max(result, anchor); return result; } // 片元着色器中调用 float3 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv).rgb; // _DarkenFactor控制暗化程度:1.0为原亮度,0.0为近黑色 baseColor = SmoothDarkenToBlack(baseColor, _DarkenFactor);
额外优化
- 纹理导入时勾选线性颜色空间(Unity中关闭"SRGB (Color Texture)"选项,仅伽马空间纹理需开启)
- 在URP设置中开启高精度渲染(Project Settings > Graphics > URP Settings > Precision > High),减少暗部精度损失
Gimp中的处理方法
- 将图像转换为32位浮点格式(Image > Precision > 32-bit Float),提升暗部精度
- 使用曲线调整时,在暗部区域添加多个锚点,构建平滑渐近曲线,避免线性下拉到0
- 导出前添加0.1%左右的增益,防止通道值完全为0
内容的提问来源于stack exchange,提问作者marai.hmm
相关产品推荐
相关产品推荐

