Unity帧叠加实现运动模糊出现色深失真伪影问题求助
问题根源与解决方案
你的伪影和色深丢失问题,核心原因是用8位整数格式的Color32存储运动模糊的中间累积结果,导致每次叠加时的舍入误差被不断放大。
Color32的每个颜色分量(R/G/B/A)都是0-255的整数,每次执行(byte)(dest + src/Steps)时,都会把浮点计算结果截断/舍入到最近的整数。比如当源像素分量是101、步数为10时,每次叠加的增量是10.1,转成byte后就变成10,10次叠加后得到100,但实际正确的累积值应该是101——这种微小的误差在多次叠加后会累积成明显的色阶断裂、伪影。
修复方案:用浮点纹理存储中间结果
把用于累积的帧缓冲(VFB)改成浮点格式的Texture2D,在浮点空间完成所有叠加计算,只在最后保存图像时再转换为8位格式,彻底避免中间步骤的精度丢失。
1. 初始化浮点帧缓冲
创建VFB时指定浮点纹理格式,比如RGBAFloat(32位浮点)或RGBAHalf(16位浮点,内存占用更低,精度足够):
// 假设src是你要渲染的单帧纹理 VFB = new Texture2D(src.width, src.height, TextureFormat.RGBAFloat, false); // 初始化为全透明黑色 VFB.SetPixels(Enumerable.Repeat(Color.clear, src.width * src.height).ToArray()); VFB.Apply();
2. 修改帧叠加函数
改用浮点类型的Color数组进行计算,保留完整精度:
// MoBlur_Steps 是运动模糊的迭代次数 void Fill_VFB(Texture2D src) { Color[] srcPixels = src.GetPixels(); Color[] destPixels = VFB.GetPixels(); float stepWeight = 1f / MoBlur_Steps; for (int i = 0; i < srcPixels.Length; i++) { // 浮点累加,无精度丢失 destPixels[i].r += srcPixels[i].r * stepWeight; destPixels[i].g += srcPixels[i].g * stepWeight; destPixels[i].b += srcPixels[i].b * stepWeight; destPixels[i].a += srcPixels[i].a * stepWeight; } VFB.SetPixels(destPixels); VFB.Apply(); Render_Step++; }
3. 最终保存图像
在所有帧叠加完成后,将浮点纹理转换为8位的RGBA32格式再保存:
using System.IO; using UnityEngine; // 保存最终结果 void SaveMotionBlurResult() { Texture2D finalTexture = new Texture2D(VFB.width, VFB.height, TextureFormat.RGBA32, false); finalTexture.SetPixels(VFB.GetPixels()); finalTexture.Apply(); byte[] pngData = finalTexture.EncodeToPNG(); string savePath = Path.Combine(Application.dataPath, "MotionBlur_Final.png"); File.WriteAllBytes(savePath, pngData); Destroy(finalTexture); }
额外注意事项
- 浮点纹理在Unity编辑器场景视图中可能显示异常,但实际计算精度是准确的,保存后就能看到正常的结果。
- 如果追求性能和内存平衡,优先选择
RGBAHalf格式,16位浮点的精度完全能覆盖运动模糊的累积需求,且内存占用只有RGBAFloat的一半。 - 绝对不要在每一次叠加后就把结果转成8位格式——所有中间计算必须在浮点空间完成,只在最终输出时做一次8位转换,才能彻底避免累积误差。
内容的提问来源于stack exchange,提问作者Andrew B
相关产品推荐
相关产品推荐

