使用 Direct2D 呈现效果的应用程序必须注意在数值精度方面达到所需的质量和可预测性水平。 本主题介绍 Direct2D 中的最佳做法和相关设置,这些设置在以下情况下非常有用:
- 效果图依赖于 [0, 1] 范围之外的高数值精度或颜色,并且你希望确保这些始终可用
- 或者,效果图形依赖于呈现实现将中间颜色限制到 [0, 1] 范围,并确保此限制始终发生
Direct2D 通常将效果图划分为各部分,并在单独的步骤中呈现每个部分。 某些步骤的输出可能存储在中间 Direct3D 纹理中,默认情况下其数值范围和精度有限。 Direct2D 无法保证是否或在哪里使用这些中间纹理。 此行为可能因 GPU 功能以及 Windows 版本而异。
在 Windows 10 中,Direct2D 由于使用着色器链接,因此使用中间纹理更少。 因此,Direct2D 可能会生成与以前 Windows 版本中不同的默认设置结果。 这主要影响在效果图中可能存在着色器链接的场景,并且该效果图还包含产生扩展范围输出颜色的效果。
效果呈现和中间项概述
为了呈现效果图,Direct2D 首先查找“转换”的基础图形,其中转换是效果中使用的图形节点。 有不同类型的转换,包括那些为 Direct2D 提供 Direct3D 着色器以供使用的转换。
例如,Direct2D 可能会呈现效果图,如下所示:
Direct2D 查找减少用于呈现效果图的中间纹理的数量的机会;此逻辑对应用程序不透明。 例如,下面的图可以由 Direct2D 使用一个 Direct3D 绘制调用来呈现,而不需要中间纹理:
在 Windows 10 之前,如果在同一效果图中使用了多个像素着色器,Direct2D 将始终使用中间纹理。 大多数只需调整颜色值(例如亮度或饱和度)的内置效果都使用像素着色器。
在 Windows 10 中,Direct2D 现在可以避免在这种情况下使用中间纹理。 它通过内部链接相邻像素着色器来执行此作。 例如:
请注意,图形中的并非所有相邻像素着色器都能链接在一起,因此,只有某些图形会在 Windows 10 上生成不同的输出。 有关完整详细信息,请参阅 Effect Shader Linking。 主要限制为:
- 如果第一个效果作为输入连接到多个效果,那么该效果将不会与那些使用其输出的效果相关联。
- 如果第一个效果在与输出不同的逻辑位置对输入进行采样,则该效果将不会与作为其输入的效果集链接。 例如,颜色矩阵效果可能与其输入链接,但卷积效果则不会。
内置效果行为
许多内置效果可能会在未呈现的颜色空间中的 [0, 1] 范围之外产生颜色,即使其输入颜色位于该范围内也是如此。 发生这种情况时,这些颜色可能会受到数字裁剪的约束。 请注意,考虑非乘法空间中的颜色范围很重要,即使内置效果通常会在预乘法空间中产生颜色。 这可以确保颜色保持在指定范围内,即使其他效果随后取消预叠。
一些可能发出这些超出范围的颜色的效果提供了一个“ClampOutput”属性。 这些包括:
将这些效果的 ClampOutput 属性设置为 TRUE 可确保无论着色器链接等因素如何,都会实现一致的结果。 请注意,固定发生在不受限制的空间中。
其他内置效果也可能在非预乘空间中产生超出 [0, 1] 范围的输出颜色,即使它们的颜色像素(以及“颜色”属性,如果有的话)在该范围内。 这些包括:
在效果图中强制进行数值裁剪
在使用上面列出的不具有 ClampOutput 属性的效果时,应用程序应考虑强制进行数值限定。 这可以通过在图形中插入一个附加效果来限制其像素值来完成。 可以使用颜色矩阵效果,其“ClampOutput”属性设置为 TRUE,并将“ColorMatrix”属性保留为默认(直通)值。
实现一致结果的第二个选项是请求 Direct2D 使用精度更高的中间纹理。 下面对此进行介绍。
控制中间纹理的精度
Direct2D 提供了几种控制图形精度的方法。 在 Direct2D 中使用高精度格式之前,应用程序必须确保 GPU 充分支持它们。 若要检查此问题,请使用 ID2D1DeviceContext::IsBufferPrecisionSupported。
应用程序可以使用 WARP(软件仿真)创建 Direct3D 设备,以确保支持所有缓冲区精度,而不受设备上的实际 GPU 硬件的影响。 在将效果保存到磁盘的同时对照片应用效果等场景中,建议使用这种方法。 即使 Direct2D 支持 GPU 上的高精度缓冲区格式,在功能级别 9.X GPU 上建议使用 WARP,因为某些低功率移动 GPU 上的着色器算术和采样精度有限。
在下面的每种情况下,请求的精度实际上是 Direct2D 的最小精度。 如果不需要中间体,可以使用更高的精度。 Direct2D 还可以共享同一图形或不同图形的不同部分的中间纹理。 在这种情况下,Direct2D 使用在所有相关操作中请求的最大精度。
从 ID2D1DeviceContext::SetRenderingControls 进行精度选择
控制 Direct2D 中间纹理精度的最简单方法是使用 ID2D1DeviceContext::SetRenderingControls。 这可以控制所有中间纹理的精度,只要没有在效果或转换上手动设置精度。
if (Device->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
// Get the current rendering controls
D2D1_RENDERING_CONTROLS renderingControls = {};
Context->GetRenderingControls(&renderingControls);
// Switch the precision within the rendering controls and set it
renderingControls.bufferPrecision = D2D1_BUFFER_PRECISION_32BPC_FLOAT;
Context->SetRenderingControls(&renderingControls);
}
从输入和呈现目标中进行精度选择
应用程序还可能依赖于效果图的输入精度来控制中间纹理的精度。 只要未使用 ID2D1DeviceContext::SetRenderingControls 指定缓冲区精度,并且没有在效果和转换上直接手动设置,就是正确的。
输入效果的精度通过图传播,以选择下游中间值的精度。 当效果图中的不同分支相交时,使用任何输入的最大精度。
根据 Direct2D 位图选择的精度是由其像素格式决定的。 为 ID2D1ImageSource 选择的精度取决于用于创建 ID2D1ImageSource 的基础 IWICBitmapSource 的 WIC 像素格式。 请注意,Direct2D 不允许使用 Direct2D 和 GPU 不支持的精度通过 WIC 源创建图像源。
Direct2D 可能无法根据其输入为效果指定精度。 当效果没有输入时,或者使用没有特定精度的ID2D1CommandList时,就会发生这种情况。 在这种情况下,中间纹理的精度由作为上下文当前呈现目标的位图集确定。
精度选择直接影响效果和转换
中间纹理的最小精度也可以在效果图中的显式位置设置。 这仅适用于高级方案。
可以使用效果的属性设置最小精度,如下所示:
if (Device->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
hr = Effect->SetValue(D2D1_PROPERTY_PRECISION, D2D1_BUFFER_PRECISION_32BPC_FLOAT);
}
在效果实现中,可以使用 ID2D1RenderInfo::SetOutputPrecision 设置最小精度,如下所示:
if (EffectContext->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
hr = RenderInfo->SetOutputBuffer(
D2D1_BUFFER_PRECISION_32BPC_FLOAT,
D2D1_CHANNEL_DEPTH_4);
}
请注意,对效果设置的精度将传播到同一效果图中的下游效果,除非对这些下游效果设置了不同的精度。 在效果中对变换设置的精度不会影响下游变换节点的精度。
下面是用于确定存储给定转换节点输出的中间缓冲区的最小精度的完整递归逻辑: