使用伽玛校正

Gamma 更正(简称 gamma)是系统用于在图像中编码和解码像素值的非线性操作的名称。

什么是伽玛,它有什么用途?

在图形管道的末尾,图像离开计算机沿监视器电缆进行旅程时,有一小块硬件可以动态转换像素值。 此硬件通常使用查阅表格来转换像素。 此硬件使用来自要显示的图面的红色、绿色和蓝色值来查找表中经过伽马更正的值,然后将更正后的值发送到监视器,而不是实际的图面值。 因此,此查阅表格是将任何颜色替换为任何其他颜色的机会。 虽然表具有该级别的功率,但典型的用法是巧妙地调整图像,以补偿监视器响应的差异。 监视器的响应是一个函数,用于将像素的红色、绿色和蓝色分量的数字值与该像素的显示亮度相关联。

这就是此表的用途,但游戏开发人员发现了它的创造性用途,例如将整个屏幕闪烁为红色以达到心理效果。 在现代游戏应用中,作为每个帧后处理的一部分,我们通常提供其他方法来执行此类操作。 事实上,我们建议你保留伽玛表,因为它可能用于校准监视器的响应,而对伽玛斜坡的批量更改将破坏这种仔细的校准。

确定伽马校正的科学是复杂的,这里没有介绍,只是为了说明“伽玛”这个名称的出处。 CRT (,即老式玻璃) 监视器的响应是一个复杂的函数,但这些监视器的物理特性意味着它们表现出可由此幂函数粗略表示的响应:

亮度 (输入) = 输入伽玛

gamma 值通常接近值 2.0。 LCD 显示器和所有其他较新的技术都经过专门设计,以表现出类似的响应,因此无需为这些新技术重新校准我们的所有软件和图像。 sRGB 标准声明此伽玛值正好为 2.2,此值已成为广泛实现的标准。

人眼还有一个响应函数,该函数大致反转 CRT 电源函数。 这意味着像素的感知亮度随该像素中的 RGB 值大致呈线性上升。

由于伽玛值 2.2 已成为事实上的标准,因此我们通常无需过多担心此表中编码的伽玛曲线,并且可以将其保留为线性的一对一映射。 当然,适当的颜色匹配需要谨慎使用此功能,但讨论超出了本主题的范围。 Windows 包含一个工具,可让用户将其显示器校准为 gamma 2.2,此工具使用查阅表格硬件为其计算机提供精心挑选的细微调整。 用户可以通过搜索“校准颜色”来运行此工具。 对于自动执行此过程的特定监视器,还有定义完善的颜色配置文件。 “校准颜色”工具可以检测这些较新的监视器,并通知用户校准已到位。

这种将幂律编码为颜色值的概念在图形管道中的其他位置也很有用,尤其是在纹理中。 对于纹理,由于我们刚才讨论的对数人眼响应,你希望更精确地使用深色。 在管道的这一部分仔细处理伽玛非常重要。 有关详细信息,请参阅 转换颜色空间的数据

本主题的其余部分仅重点介绍管道最后一部分中帧缓冲区数据和监视器之间的伽马校正。 如果你想要编写校准向导或在后处理步骤不切实际的全屏应用中创建特殊效果,以下是你需要的信息。

Windows 上伽玛的背景

Windows 计算机通常有一个 gamma 表,该表是一个查找表,它采用三元字节并输出三元字节。 这些三元组是 768 (256 x 3) 字节的 RAM。 如果显示格式包含 RGB BYTE 值的三元组,但没有足够的表现力来描述在显示格式的范围大于 [0,1] (例如浮点值)时可能需要的转换,则这很好。 随着显示格式变得越来越复杂,Windows 中控制 gamma 的 API 也随之演变。

第一个提供伽玛控制的 Windows API 是 Windows 图形设备接口 (GDI) 的 SetDeviceGammaRampGetDeviceGammaRamp。 这些 API 适用于三个由 256 个条目构成的 WORD 数组,每个 WORD 编码为零到 1,由 WORD 值 0 和 65535 表示。 WORD 的额外精度通常在实际硬件查找表中不可用,但这些 API 旨在灵活。 与本部分后面介绍的其他 API 相比,这些 API 只允许与标识函数稍有偏差。 事实上,渐变中的任何条目都必须在标识值的 32768 范围内。 此限制意味着任何应用都不能将屏幕完全变为黑色或其他一些不可读的颜色。

下一个 API 是 Microsoft Direct3D 9 的 SetGammaRamp,它遵循与 SetDeviceGammaRamp 相同的模式和数据格式。 Direct3D 9 伽玛渐变的默认值不是特别有用;它是初始化为 0-255 而不是 0-65535 的 WORD 的渐变,即使 API 是按照 0-65535 定义的。

最新的 API 是 IDXGIOutput::SetGammaControl。 此 API 具有更灵活的方案来表达伽马控制,因为该方案适用于 DXGI 增加的一组显示格式,包括每通道 10 个整数位、16 位浮点数格式和XR_BIAS扩展范围格式。

所有这些 API 在同一硬件上运行,并更改相同的值。 Direct3D 9 和 DXGI API 为“仅写入”。 无法读取硬件的值、对其进行修改和设置。 只能设置坡道。 此外,只能在应用全屏时设置 gamma。 此限制是保证桌面始终可读的另一种方法。 也就是说,应用可能会干扰自己的显示,但当应用失去全屏 (时,Windows 将还原以前的伽玛渐变,例如,通过 alt-tab 或 ctrl-alt-del) 。

显示硬件的演变

一些较新的监视器可以显示各种强度。 但是,当显示格式只能表示介于 0 和 1 之间的值时,显示器必须将零映射到其最暗值,将 1 映射到最亮的值。 这个最亮的值可能太亮了,无法舒适地在白色背景上查看带有黑色文本的网页,但对于过亮的特效(如查看阳光从湖中闪闪发光或闪电在天空中分叉)来说,效果非常出色。 因此,我们需要一种方法来表达这些更广泛的范围。 DXGI 1.1 及更高版本包含显示格式值,这些值让 1.0 表示舒适的白色值,并为过亮特效保留更宽的显示格式值。 DXGI 1.1 支持两种可以表示这些更宽值的显示格式:DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM和 16 位浮点。 有关这些格式的完整讨论,请参阅 扩展格式的详细信息。 接下来,我们了解为什么 DXGI 的 IDXGIOutput::SetGammaControl gamma API 需要大于 1.0 的像素值。

DXGI 中的 Gamma 控制功能

DXGI 允许显示驱动程序将其伽玛控件表示为阶梯线性函数。 此阶梯线性函数由此函数的控制点、函数可以转换为的值范围以及可在转换后应用的其他可选缩放和偏移运算定义。 应用可以调用 IDXGIOutput::GetGammaControlCapabilities 方法来检索 DXGI_GAMMA_CONTROL_CAPABILITIES 结构中的所有控制功能。

此图显示了一个仅包含四个控制点的线性函数。

gamma 校正线性函数

DXGI 按控制点沿图面颜色轴的位置定义控制点。 在上图中,控制点的位置为 0、0.5、0.75 和 1.0。 这些控制点指示硬件可以转换 0 到 1.0 范围内的值。 DXGI 在 DXGI_GAMMA_CONTROL_CAPABILITIESControlPointPositions 数组成员中列出这些控制点,并始终按递增顺序声明它们。 DXGI 仅填充 ControlPointPositions 数组的第一个元素,并指示具有 DXGI_GAMMA_CONTROL_CAPABILITIESNumGammaControlPoints 成员的元素数。 如果 NumGammaControlPoints 小于 1025,则 DXGI 使 其余 ControlPointPositions 元素未定义。

此图表示的硬件可以将值转换为 0 到 1.25 的范围。 因此,DXGI 将 MinConvertedValueMaxConvertedValue 成员分别设置为 0.0f 和 1.25f。

DXGI 设置 DXGI_GAMMA_CONTROL_CAPABILITIESScaleAndOffsetSupported 成员,以指示硬件是否支持缩放和偏移功能。 如果硬件支持缩放和偏移,它将保留简单的一对一查找表,但随后调整表的输出以将输出拉伸到大于 [0,1] 的范围。 硬件首先缩放查找表中的值,然后对其进行偏移。

注意

连接到同一台计算机的不同监视器可能具有不同的伽玛控制功能。 此外,伽玛控制功能实际上可能会根据输出的显示模式而变化。 因此,建议在应用进入全屏模式后,始终调用 IDXGIOutput::GetGammaControlCapabilities 来查询 gamma 控制功能。

 

可以使用这些 gamma 控制功能值来派生控件值,然后可以使用 IDXGIOutput::SetGammaControl API 设置这些值。

使用 DXGI 设置伽玛控件

若要设置伽玛控件,请在调用 IDXGIOutput::SetGammaControl API 时传递指向DXGI_GAMMA_CONTROL结构的指针。

设置 DXGI_GAMMA_CONTROLScaleOffset 成员,以指定希望硬件应用于从查找表中获取的值的缩放和偏移值。 可以安全地将 “缩放 ”设置为 1, 将“偏移量” 设置为零 (也就是说,如果不想使用缩放和偏移功能,则缩放比例为 1 且偏移量为 0 不起作用,) 如果硬件没有该功能。

DXGI_GAMMA_CONTROLGammaCurve 数组成员设置为伽玛曲线上点的DXGI_RGB结构列表。 每个 DXGI_RGB 元素指定表示该点的红色、绿色和蓝色分量浮点值。 伽玛曲线不使用 alpha 值。 使用从 DXGI_GAMMA_CONTROL_CAPABILITIESNumGammaControlPoints 获取的数字填充 GammaCurve 数组中的该数量的元素。 放置在 GammaCurve 数组中的每个元素都是每个控制点的高度。

请注意,在上图中,你现在可以控制每个控制点的垂直位置,并且可以单独控制红色、绿色和蓝色。 例如,可以将所有绿色和蓝色值设置为零,并将红色值设置为从零到 1 的升序楼梯。 在此方案中,显示的图像仅显示其红色部分,蓝色和绿色显示为黑色。 还可以为所有颜色设置降序楼梯,这会导致显示反转。 放置在 GammaCurve 数组中的任何值都必须包含在从 DXGI_GAMMA_CONTROL_CAPABILITIESMinConvertedValueMaxConvertedValue 成员获取的值中。

伽玛控制实用

DXGI 的伽玛控件仅在应用全屏时应用。 当应用退出或返回到窗口模式时,Windows 会还原先前的显示状态。 但是,如果应用重新进入全屏模式,Windows 不会还原应用的伽玛状态。 应用在重新进入全屏模式时必须显式还原其伽玛状态。

并非所有适配器都支持伽玛控制。 如果适配器不支持伽玛控制,它会忽略设置伽玛渐变的调用。

在远程桌面下运行的应用根本无法控制伽玛。

如果鼠标光标在硬件 ((大多数) )中实现,则通常不会响应伽玛设置。

DXGI 编程指南