[图形渲染]讲透RenderTarget 第七章:格式与色彩空间

张开发
2026/4/14 12:34:08 15 分钟阅读

分享文章

[图形渲染]讲透RenderTarget 第七章:格式与色彩空间
第七章格式与色彩空间一句话概括格式决定 RT 能装什么、装多精确、花多少带宽。生活类比选容器——用碗8bit还是桶16F还是游泳池32F取决于你要装多少颜色的水。⏱ 30 秒概览RT 格式决定三件事精度8bit/16F/32F、通道数R / RG / RGBA、值域UNORM 0~1 / FLOAT ±65504 / UINT 整数。常用格式速记RGBA8最终 LDR/UI、RGBA16FHDR 场景色、R11G11B10F无 Alpha 的 HDR省一半带宽、R8AO/Mask、D32F/D16深度。sRGB vs Linear 是最高频的坑带_SRGB后缀的格式在写入/读取时会自动做 gamma 转换搞混了会导致画面过亮/过暗/双重 gamma。HDR 管线推荐 RGBA16F 或 R11G11B10FHDR 显示输出需要 R10G10B10A2 或 R16G16B16A16_FLOAT Swap Chain。G-Buffer 打包的核心原则数据塞满每个通道格式选最小够用的。格式Format是创建 RT 时最重要的决策之一。选错格式轻则浪费带宽重则画面出错。本章系统讲解 RT 常用格式的特点、sRGB/Linear 的关键差异以及 HDR 管线中的格式选择。7.1 常见 RT 格式全解整数归一化格式UNORM / SNORM格式每像素通道范围典型用途R8G8B8A8_UNORM4 字节RGBA 各 8 位[0, 1]最终 LDR 输出、UI、BaseColorR8G8B8A8_SNORM4 字节RGBA 各 8 位[-1, 1]法线存储精度较低R10G10B10A2_UNORM4 字节RGB 各 10 位 A 2 位[0, 1]HDR 显示输出SDR 模式下R16G16_UNORM4 字节RG 各 16 位[0, 1]高精度 UV、Motion VectorUNORMUnsigned Normalized意味着 8 位整数值 0~255 被映射到浮点 0.0~1.0。Shader 读写时看到的是浮点值存储的是整数。浮点格式FLOAT格式每像素通道范围典型用途R16G16B16A16_FLOAT8 字节RGBA 各 16 位半精度浮点±65504HDR 场景 Color、G-BufferR32G32B32A32_FLOAT16 字节RGBA 各 32 位浮点±3.4×10³⁸科学计算、极高精度需求R32_FLOAT4 字节R 单通道 32 位浮点±3.4×10³⁸高精度深度、SSAOR16_FLOAT2 字节R 单通道 16 位半精度±65504亮度、单通道 HDR 数据R11G11B10_FLOAT4 字节R/G 各 11 位 B 10 位[0, 65024]HDR Color省带宽版R16G16B16A16_FLOAT简称 RGBA16F是 HDR 渲染的标准格式——范围够大远超 SDR 的 0~1精度够用且大部分 GPU 对它有良好的硬件支持包括混合和采样。R11G11B10_FLOAT是 RGBA16F 的省钱版——只有 4 字节/像素RGBA16F 的一半但没有 Alpha 通道且 B 通道只有 10 位精度。仅支持非负值。适合场景颜色这种不需要 Alpha、不需要负值的场景。整数格式UINT / SINT格式每像素典型用途R32_UINT4 字节物体 IDGPU 拾取、索引数据R8_UINT1 字节模板替代品、分类标记这些格式不做归一化Shader 读写的就是原始整数值。适合存储 ID、计数器等非颜色数据。深度格式格式每像素精度说明D16_UNORM2 字节低移动端省显存时用D24_UNORM_S8_UINT4 字节中最经典24 位深度 8 位模板D32_FLOAT4 字节高最高精度推荐桌面端使用D32_FLOAT_S8X24_UINT8 字节最高32 位浮点深度 8 位模板极少用D32_FLOAT 配合 Reversed-Z反向深度是当前最佳实践——在远处也能保持较好的深度精度。7.2 sRGB vs Linear行为天差地别这是实际工程中最容易踩坑的地方之一。什么是 sRGB人眼对暗部亮度的感知比亮部更敏感。sRGB 色彩空间利用了这个特性——把更多的bit分配给暗部使得 8 位存储也能让人觉得足够平滑。sRGB 对值做了一个非线性变换Gamma 编码近似γ≈2.2\gamma \approx 2.2γ≈2.2sRGB 存储值≈线性值1/2.2\text{sRGB 存储值} \approx \text{线性值}^{1/2.2}sRGB存储值≈线性值1/2.2带_SRGB后缀的格式在图形 API 中R8G8B8A8_UNORM和R8G8B8A8_SRGB的底层数据完全相同——都是 4 字节/像素、8 位/通道。但 GPU 在读写时的行为不同操作UNORMSRGBShader 写入直接存储无转换自动做 Linear → sRGB 编码Shader 读取直接读取无转换自动做 sRGB → Linear 解码这意味着如果 RT 格式是_SRGBShader 输出的线性值会被 GPU 自动转换为 sRGB 编码后存储。后续从这张 RT 采样时GPU 自动做 sRGB 解码Shader 拿到的是线性值。如果 RT 格式是_UNORMShader 输出什么就存什么不做任何转换。实际中怎么选择HDR 渲染管线现代主流做法场景渲染 → [RGBA16F RT]线性空间宽范围 ↓ Tone MappingHDR → LDR 最终输出 → [RGBA8_SRGB RT / Swap Chain]中间过程的所有 RT 用RGBA16F浮点格式本身不涉及 sRGB 编码最终输出到 Swap Chain 时用RGBA8_SRGB或手动在 Shader 中做 Gamma 编码SDR 渲染管线简单场景直接用RGBA8_SRGB作为 RT 格式GPU 帮你处理 Gamma常见踩坑错误中间 RT 用了_SRGB格式——如果你在后处理链中使用 RGBA8_SRGB 作为中间 RT每次读写都会做 Gamma 编码/解码。多次 Pass 后误差累积颜色严重失真偏亮或偏暗。中间 RT 应该用 Linear 格式UNORM 或 FLOAT。错误BaseColor 纹理以_UNORM格式采样——美术制作的贴图通常是 sRGB 编码的。如果以 UNORM 格式采样不做解码Shader 中的光照计算基于错误的值画面偏暗。应该以_SRGB格式创建纹理的 SRV。错误法线贴图以_SRGB格式采样——法线贴图存的是向量数据不是颜色不应该做 sRGB 解码。法线贴图必须用 UNORM 格式。7.3 精度与范围的权衡不同格式在精度和范围上有本质差异。精度是能区分多细微的差异范围是能表示多大/多小的值。8 位 UNORM范围[0, 1]精度1/255 ≈ 0.004只有 256 个离散级别足够普通颜色输出LDR不够HDR超过 1.0 的亮度直接截断、精确的法线存储16 位 FLOAT半精度浮点范围±65504精度小值附近约 0.001大值附近更粗足够几乎所有实时渲染场景的 HDR 颜色不够科学计算中极高精度需求11/11/10 FLOAT范围[0, 65024]仅非负精度略低于 16F尤其 B 通道只有 10 位足够场景颜色不需要 Alpha 或负值不够需要 Alpha 或负值的场景32 位 FLOAT范围±3.4×10³⁸精度约 7 位有效十进制数字足够任何实时渲染需求代价每通道 4 字节带宽是 16F 的两倍选择决策树需要超过 1.0 的值 ├── 否 → RGBA8_UNORM或 _SRGB 如果是最终输出 └── 是 → 需要 Alpha 通道 ├── 否 → R11G11B10_FLOAT最省带宽 └── 是 → RGBA16F标准 HDR 格式 超高精度 └── 是 → RGBA32F罕见7.4 HDR 渲染管线中的格式选择典型 HDR 管线的各阶段格式推荐阶段推荐格式说明G-Buffer: BaseColorRGBA8_UNORM颜色不需要 HDR 范围G-Buffer: NormalRG16_UNORM 或 RGB10A2_UNORMOctahedron 编码法线只需 2 通道G-Buffer: Metallic/RoughnessRG8_UNORM2 通道 8 位够用场景 HDR ColorRGBA16F 或 R11G11B10F需要 HDR 范围Bloom 降采样链R11G11B10FHDR 但不需要 AlphaShadow Map DepthD16 或 D32F视精度需求SSAOR8_UNORM 或 R16F单通道灰度图Motion VectorRG16F 或 RG16_SNORM2D 运动向量可以为负最终 LDR 输出RGBA8_SRGBTone Mapping 后一个技巧G-Buffer 格式打包为了减少 G-Buffer 的 RT 数量和带宽引擎经常把多种数据打包进同一个 RT 的不同通道RT0 (RGBA8): BaseColor.rgb Metallic RT1 (RGBA8): Normal.xy (Octahedron) Roughness MaterialID RT2 (R11G11B10F): Emissive.rgb Depth (D32F): 线性深度或用硬件深度缓冲只用 3 张 RT 1 个 Depth Buffer带宽远低于使用 4 张 RGBA16F 的暴力方案。7.5 HDR 显示输出Swap Chain 格式与 Tone Mapping 的衔接如果目标显示器支持 HDR如 HDR10、Dolby VisionSwap Chain 的格式需要特殊处理。SDR 输出传统Swap Chain 格式RGBA8_SRGB 或 BGRA8_SRGB Tone MappingHDR 值 → [0, 1] 范围 → 写入 Swap ChainHDR10 输出Swap Chain 格式R10G10B10A2_UNORM 色彩空间BT.2020 PQST.2084传输函数 Tone MappingHDR 值 → [0, 10000 nits 范围] → PQ 编码 → 写入 Swap ChainscRGBWindows HDRSwap Chain 格式R16G16B16A16_FLOAT 色彩空间scRGB线性可以超过 1.0 Tone Mapping直接输出线性 HDR 值值 1.0 对应 80 nits关键注意HDR 输出时Tone Mapping 的目标不再是 [0,1]而是显示器的亮度范围Swap Chain 格式必须匹配显示系统的期望——设错了画面会完全错误过亮/过暗/色彩空间偏移各 API 设置 HDR 输出的方式不同DX12IDXGISwapChain::SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)VulkanVkSurfaceFormatKHR选择VK_COLOR_SPACE_HDR10_ST2084_EXTMetalCAMetalLayer.wantsExtendedDynamicRangeContent trueFAQRGBA16F 和 R11G11B10F 哪个好看需求维度RGBA16FR11G11B10F每像素大小8 字节4 字节带宽2x1xAlpha 通道✅ 有❌ 没有负值✅ 支持❌ 仅非负精度更高略低尤其 B 通道混合Blending✅ 全部 GPU 支持⚠️ 部分移动端 GPU 不支持典型用途通用 HDR RT场景颜色、Bloom经验法则场景 HDR Color RT不需要 Alpha、不做混合→R11G11B10F省一半带宽需要 Alpha 或需要混合的 HDR RT →RGBA16F如果不确定 →RGBA16F更安全本章小结格式三要素通道数、每通道位数、数据类型UNORM/FLOAT/UINTsRGB 格式自动做 Gamma 编解码——中间 RT 不要用_SRGB最终输出才用RGBA16F 是 HDR 渲染的安全选择R11G11B10F 是省带宽的替代G-Buffer 格式应该精打细算——打包数据到不同通道减少 RT 数量HDR 显示输出需要特殊的 Swap Chain 格式和色彩空间设置 思考题R11G11B10F 每像素只有 4 字节和 RGBA8 一样但存的是 HDR 数据。它是怎么做到的代价是什么提示对比 RGBA16F 的精度范围为什么中间渲染的 RT 不应该用_SRGB格式如果你在 HDR 管线中把所有 RT 都设为 RGBA8_SRGB会发生什么如果你需要在 G-Buffer 中存储材质 ID一个 0~255 的整数应该用什么格式UNORM、FLOAT 还是 UINT为什么下一章进入实践篇首先讲解 MRTMultiple Render Targets——一次 Draw Call 同时画到多张 RT 上。这是延迟渲染 G-Buffer 的基石也是带宽优化的重灾区——你将看到省了计算、但没省带宽的残酷算术。原理篇回顾第五到第七章从三个维度填充了 RT 的技术底座——硬件怎么写入ROP/Tile/DCC、六套 API 怎么表达FBO/RTV/VkImage/MTLTexture、格式怎么选择UNORM/FLOAT/sRGB/HDR。有了这些基础从第八章开始的实践篇将聚焦怎么用 RT 构建真实的渲染效果——MRT、Compute Shader、引擎架构、以及 20 多种经典效果的 RT 拓扑图。

更多文章