| name | color-format-consistency |
| description | 诊断并修复跨不同图形 API 的颜色格式不一致问题(sRGB 与线性颜色空间)。当跨 API 渲染出现颜色发白、太亮或不正确时调用。 |
颜色格式一致性
本 skill 帮助诊断和修复多种图形 API 之间的颜色格式不匹配问题,特别是当捕获 API 与渲染框架之间传递图像数据时。
常见症状
- 颜色看起来发白 / 过亮 / 苍白
- 颜色显得过暗
- 色偏(出现意外的色调)
- 伽马校正被应用两次或根本未应用
根因分析
最常见的原因是 sRGB 与线性颜色空间不一致:
- 源格式:捕获 API 输出某种颜色空间的数据(例如线性 BGRA8)
- 中间格式:纹理格式可能会或可能不会应用 sRGB 转换
- 目标格式:表面/显示格式可能会额外应用一次转换
当这些环节不一致时,伽马校正就会被错误地应用。
诊断检查清单
第一步:确认源格式
- 捕获 API 输出的是什么格式?(例如
ColorFormat::Bgra8 通常是线性格式)
- 它是 sRGB 还是线性颜色空间?
第二步:检查中间纹理格式
- 渲染纹理使用的
wgpu::TextureFormat 是什么?
- 它是 sRGB 变体(如
Bgra8UnormSrgb)还是线性格式(如 Bgra8Unorm)?
第三步:验证表面配置
SurfaceConfiguration.format 选用了什么格式?
- 是否使用了
is_srgb() 来优先选择 sRGB 格式?
- 该格式是否与源数据的颜色空间一致?
第四步:追踪渲染管线
Capture Output -> Texture Upload -> Shader Sampling -> Surface Presentation
| | | |
Linear? Linear/sRGB? Expected? Linear/sRGB?
常见修复模式
模式 1:Windows Capture + wgpu
问题:windows_capture::settings::ColorFormat::Bgra8 输出线性数据,但表面配置为 sRGB 格式。
解决方案:
let surface_format = if surface_caps
.formats
.contains(&wgpu::TextureFormat::Bgra8Unorm)
{
wgpu::TextureFormat::Bgra8Unorm
} else {
*surface_caps
.formats
.iter()
.find(|f| !f.is_srgb())
.unwrap_or(&surface_caps.formats[0])
};
模式 2:纹理格式对齐
确保纹理格式与源数据一致:
let texture_format = wgpu::TextureFormat::Bgra8Unorm;
let texture_format = wgpu::TextureFormat::Bgra8UnormSrgb;
模式 3:采样器配置
对于像素精确的捕获,使用 Nearest 过滤:
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Capture Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
关键原则
- 端到端一致性:源格式 → 纹理格式 → 表面格式应在颜色空间上保持一致
- 了解源数据:明确捕获 API 输出的颜色空间
- 避免双重伽马:如果源数据已经是 sRGB,就不要再次应用 sRGB 转换
- 用已知颜色测试:使用颜色卡验证颜色还原是否正确
快速参考
| 源格式 | 纹理格式 | 表面格式 |
|---|
| Linear BGRA8 | Bgra8Unorm | Bgra8Unorm |
| sRGB BGRA8 | Bgra8UnormSrgb | Bgra8UnormSrgb |
| Linear RGBA8 | Rgba8Unorm | Rgba8Unorm |
| sRGB RGBA8 | Rgba8UnormSrgb | Rgba8UnormSrgb |
调试命令
- 打印所有可用的表面格式:
println!("Available formats: {:?}", surface_caps.formats);
- 检查格式是否为 sRGB:
println!("Is sRGB: {}", format.is_srgb());
- 视觉测试:显示已知颜色(例如纯红
#FF0000)并与源进行对比。