数据类型转换

以下部分介绍 Direct3D 如何处理数据类型之间的转换。

数据类型术语

随后使用下面这一组术语描述各种格式转换的特征。

术语 定义
SNORM 有符号的规范化整数,即,对于 n 位 2 的补数而言,最大值为 1.0f(例如,5 位值 01111 映射到 1.0f),最小值为 -1.0f(例如,5 位值 10000 映射到 -1.0f)。 此外,第二小的数字映射到 -1.0f(例如,5 位值 10001 映射到 -1.0f)。 因此,-1.0f 有两种整数表示形式。 0.0f 的单一表示形式和 1.0f 的单一表示形式。 这样会为范围 (-1.0f...0.0f) 中均匀分布的浮点值产生一组整数表示形式,并为范围 (0.0f...1.0f) 中的数字产生一组补充的表示形式
UNORM 无符号的规范化整数,这意味着对于一个 n 位数字,所有的 0 都表示 0.0f,所有的 1 都表示 1.0f。 表示从 0.0f 到 1.0f 的均匀分布的浮点值序列。 例如,2 位 UNORM 表示 0.0f、1/3、2/3 和 1.0f。
SINT 有符号的整数。 2 的补数整数。 例如,3 位 SINT 表示整数值 -4、-3、-2、-1、0、1、2、3。
UINT 无符号的整数。 例如,3 位 UINT 表示整数值 0、1、2、3、4、5、6、7。
FLOAT Direct3D 定义的任何表示形式中的浮点值。
SRGB 和 UNORM 类似,对于一个 n 位数字,所有的 0 都表示 0.0f,所有的 1 都表示 1.0f。 但与 UNORM 不同的是,使用 SRGB,所有 0 到所有 1 之间的无符号整数编码序列都表示 0.0f 到 1.0f 之间的数字的浮点解释中的非线性渐进。 大致而言,如果这种非线性渐进 (SRGB) 显示为一个颜色序列,它将在“一般”观看条件下在“一般”显示器上向“一般”观察者显示为亮度水平线性渐进。 有关完整的详细信息,请参阅 IEC(国际电工委员会)的 SRGB 颜色标准 IEC 61996-2-1。

 

上述术语通常用作“格式名称修饰符”,它们将说明数据在内存中的布局方式,以及要在内存与管道单元(例如着色器)之间的传输路径中执行的转换(可能包括筛选)。

浮点转换

每当发生不同表示形式之间的浮点转换(包括与非浮点表示形式之间的转换)时,以下规则将适用。

从更高的范围表示形式转换为较低的范围表示形式

  • 在转换为另一种浮点格式时,使用舍入到零。 如果目标是一种整数或固定点格式,将使用舍入到最接近的偶数,除非将转换显式记录为使用另一种舍入行为,例如将 FLOAT 转换为 SNORM、将 FLOAT 转换为 UNORM 或者将 FLOAT 转换为 SRGB 时进行就近舍入。 其他的例外包括 ftoi 和 ftou 着色器指令,它们使用舍入到零。 最后,纹理取样器和光栅器使用的浮点到固定转换具有指定的容差,以无限精确结果的最后位置单位测得。
  • 对于大于下限目标格式动态范围的源值(例如将一个较大的 32 位浮点值写入到 16 位浮点 RenderTarget),为(具有适当符号的)最大可表示值结果,不包括有符号的无穷大(由于上述的舍入到零)。
  • 如果下限格式中存在 NaN 表示形式,则将上限格式的 NaN 转换为下限格式的 NaN 表示形式。 如果下限格式没有 NaN 表示形式,结果将为 0。
  • 上限格式的 INF 将转换为下限格式的 INF(如果可用)。 如果下限格式没有 INF 表示形式,它将转换为可表示的最大值。 如果目标格式中存在符号,将会保留符号。
  • 上限格式的 Denorm 将转换为下限格式的 Denorm 表示形式(如果在下限格式中可用并且能够转换),否则结果为 0。 如果目标格式中存在符号位,将会保留符号位。

从下限范围表示形式转换为上限范围表示形式

  • 下限格式的 NaN 将转换为上限格式的 NaN 表示形式(如果在上限格式中可用)。 如果上限格式没有 NaN 表示形式,它将转换为 0。
  • 下限格式的 INF 将转换为上限格式的 INF 表示形式(如果在上限格式中可用)。 如果上限格式没有 INF 表示形式,它将转换为可表示的最大值(此种格式的 MAX_FLOAT)。 如果目标格式中存在符号,将会保留符号。
  • 如果可能,下限格式的 Denorm 将转换为上限格式的规范化表示形式,否则,如果 Denorm 表示形式存在,将转换为上限格式的 Denorm 表示形式。 当上述条件都不成立时,如果上限格式没有 Denorm 表示形式,它将转换为 0。 如果目标格式中存在符号,将会保留符号。 请注意,32 位浮点数计为没有 Denorm 表示形式的格式(因为 32 位浮点运算中的 Denorm 将刷新为符号保留的零)。

整数转换

下表说明了上述的各种表示形式与其他表示形式之间的转换。 只显示了 Direct3D 中实际发生的转换。

对于整数,除非另行指定,否则将会精确完成整数表示形式与下面所述的浮点表示形式之间的所有转换。

源数据类型 目标数据类型 转换规则
SNORM FLOAT

给定的一个表示有符号范围 [-1.0f 到 1.0f] 的 n 位整数值按如下方式转换为浮点数。

  • 最小值映射到 -1.0f。 例如,5 位值 10000 映射为 -1.0f。
  • 所有其他值都转换为浮点数(称之为 c),这样,结果 = c * (1.0f / (2⁽ⁿ⁻¹⁾-1))。 例如,5 位值 10001 转换为 -15.0f,然后除以 15.0f,得到 -1.0f。
FLOAT SNORM

给定的一个浮点数按如下方式转换为一个表示有符号范围 [-1.0f 到 1.0f] 的 n 位整数值。

  • c 表示起始值。
  • 如果 c 为 NaN,结果将为 0。
  • 如果 c > 1.0f(包括 INF),它将固定为 1.0f。
  • 如果 c < -1.0f(包括 -INF),它将固定为 -1.0f。
  • 从浮点值转换为整数值:c = c * (2ⁿ⁻¹-1)。
  • 按如下所示转换为一个整数。
    • 如果 c >= 0,则 c = c + 0.5f,否则 c = c - 0.5f。
    • 去掉小数部分,其余的浮点(整数)值将直接转换为一个整数。

允许此转换具有容差 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_Unit-Last-Place 最后位置单位(在整数端)。 这意味着,从浮点值转换为整数值之后,一个可表示的目标格式值的 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位以内的任何值都可以映射到此值。 额外的数据可逆性要求可确保转换在整个范围内以非递减顺序出现,并且所有输出值都可实现。 (在此处显示的常量中,应将 xx 替换为 Direct3D 版本,例如 10、11 或 12。)

UNORM FLOAT

起始的 n 位值将转换为浮点值(0.0f、1.0f、2.0f 等等),然后除以 (2ⁿ-1)。

FLOAT UNORM

c 表示起始值。

  • 如果 c 为 NaN,结果将为 0。
  • 如果 c > 1.0f(包括 INF),它将固定为 1.0f。
  • 如果 c < 0.0f(包括 -INF),它将固定为 0.0f。
  • 从浮点值转换为整数值:c = c * (2ⁿ-1)。
  • 转换为整数。
    • c = c + 0.5f。
    • 去掉小数部分,其余的浮点(整数)值将直接转换为一个整数。

允许此转换具有容差 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位(在整数端)。 这意味着,从浮点值转换为整数值之后,一个可表示的目标格式值的 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位以内的任何值都可以映射到此值。 额外的数据可逆性要求可确保转换在整个范围内以非递减顺序出现,并且所有输出值都可实现。

SRGB FLOAT

下面是将 SRGB 转换为 FLOAT 的理想方式。

  • 将起始的 n 位值转换为浮点数(0.0f、1.0f、2.0f 等等);称之为 c。
  • c = c * (1.0f / (2ⁿ-1))
  • 如果 (c < = D3Dxx_SRGB_TO_FLOAT_THRESHOLD),则结果 = c / D3Dxx_SRGB_TO_FLOAT_DENOMINATOR_1,否则结果 = ((c + D3Dxx_SRGB_TO_FLOAT_OFFSET)/D3Dxx_SRGB_TO_FLOAT_DENOMINATOR_2)D3Dxx_SRGB_TO_FLOAT_EXPONENT

允许此转换具有容差 D3Dxx_SRGB_TO_FLOAT_TOLERANCE_IN_ULP 最后位置单位(在 SRGB 端)。

FLOAT SRGB

下面是将 FLOAT 转换为 SRGB 的理想方式。

假设目标 SRGB 颜色分量具有 n 位:

  • 假设起始值为 c。
  • 如果 c 为 NaN,结果将为 0。
  • 如果 c > 1.0f(包括 INF),它将固定为 1.0f。
  • 如果 c < 0.0f(包括 -INF),它将固定为 0.0f。
  • 如果 (c <= D3Dxx_FLOAT_TO_SRGB_THRESHOLD),则 c = D3Dxx_FLOAT_TO_SRGB_SCALE_1 * c,否则 c = D3Dxx_FLOAT_TO_SRGB_SCALE_2 * c(D3Dxx_FLOAT_TO_SRGB_EXPONENT_NUMERATOR/D3Dxx_FLOAT_TO_SRGB_EXPONENT_DENOMINATOR) - D3Dxx_FLOAT_TO_SRGB_OFFSET
  • 从浮点值转换为整数值:c = c * (2ⁿ-1)。
  • 转换为整数:
    • c = c + 0.5f。
    • 去掉小数部分,其余的浮点(整数)值将直接转换为一个整数。

允许此转换具有容差 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位(在整数端)。 这意味着,从浮点值转换为整数值之后,一个可表示的目标格式值的 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位以内的任何值都可以映射到此值。 额外的数据可逆性要求可确保转换在整个范围内以非递减顺序出现,并且所有输出值都可实现。

SINT 具有更多位的 SINT

为从 SINT 转换为位数更多的 SINT,起始数字的最高有效位 (MSB) 已“符号扩展”至目标格式中的其他可用位。

UINT 具有更多位的 SINT

要从 UINT 转换为具有更多位的 SINT,此数字将复制到目标格式的最小有效位 (LSB),并为额外的 MSB 填充 0。

SINT 具有更多位的 UINT

要从 SINT 转换为具有更多位的 UINT:如果为负,此值将固定为 0。 否则,此数字将复制到目标格式的 LSB,并为额外的 MSB 填充 0。

UINT 具有更多位的 UINT

要从 UINT 转换为具有更多位的 UINT,此数字将复制到目标格式的 LSB,并为额外的 MSB 填充 0。

SINT 或 UINT 具有更少或相同位数的 SINT 或 UINT

要从 SINT 或 UINT 转换为具有更少或相同位数(以及/或者符号发生变化)的 SINT 或 UINT,只需将起始值固定为目标格式的限值。

 

固定点整数转换

固定点整数只是具有某个位大小的整数,并在固定位置具有一个隐式小数点。

通用的“Integer”数据类型是固定点整数的一种特殊情况,小数点位于数字的末尾。

固定点数表示形式的特征是 i.f,其中的 i 是整数位数,f 是小数位数。 例如,16.8 意味着 16 位整数,后跟 8 位小数。 整数部分存储在 2 的补数中,至少如此处定义的那样(不过,也可以为无符号的整数如此定义)。 小数部分以无符号的形式存储。 小数部分始终表示两个最接近的整数值之间的正小数,从最小值开始。

对固定点数进行的加法和减法运算只是使用标准整数算术执行的,而不考虑隐式小数的位置。 为 16.8 固定点数加 1 意味着增加 256,因为小数位数是从数字最低有效位的末尾算起的 8 个位置。 也可以使用整数算术来执行其他运算(例如乘法),前提是将对固定小数点的影响考虑在内。 例如,使用整数乘法将两个 16.8 整数相乘将得到一个 32.16 结果。

在 Direct3D 中,以两种方式使用固定点整数表示形式。

  • 将光栅器中经过后期剪裁的顶点位置贴靠到固定点,以使精度均匀分布在 RenderTarget 区域中。 很多光栅器操作(例如人脸剔除)发生在固定点贴靠位置,而其他操作(例如属性内插器设置)使用已从固定点贴靠位置转换回浮点的位置。
  • 用于执行取样操作的纹理坐标贴靠到固定点(按纹理大小缩放之后),以便在选择筛选器点击位置/权重时使精度均匀分布在纹理空间中。 在执行实际的过滤算术之前,权重值将被转换回浮点数。
源数据类型 目标数据类型 转换规则
FLOAT 固定点整数

下面是用来将浮点数 n 转换为固定点整数 i.f 的常规步骤,其中的 i 是(有符号的)整数位数,f 是小数位数。

  • 计算 FixedMin = -2⁽ⁱ⁻¹⁾
  • Compute FixedMax = 2⁽ⁱ⁻¹⁾ - 2(-f)
  • If n is a NaN, result = 0; if n is +Inf, result = FixedMax*2f; if n is -Inf, result = FixedMin*2f
  • 如果 n >= FixedMax,则结果 = Fixedmax*2f;如果 n <= FixedMin,则结果 = FixedMin*2f
  • 否则计算 n*2f 并转换为整数。

允许实现的整数结果中具有容差 D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP 最后位置单位,而不是上述最后一步之后的无限精确值 n*2f

固定点整数 FLOAT

假设要转换为浮点的特定固定点表示形式不包含总共 24 位以上的信息,其中的不超过 23 位是在小数部分中。 假设一个给定的固定点数 fxp 采用 i.f 形式(i 位整数、f 位小数)。 转换为浮点的方式类似于如下伪代码。

浮点结果 = (float)(fxp >> f) + // 提取整数

((float)(fxp & (2f - 1)) / (2f)); // 提取小数

 

附录