D3D12渲染技术之混合

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/82945496

本篇博客给读者介绍混合,见下图所示, 我们首先绘制地形,然后绘制木箱,然后将地形和木箱像素放在后面的缓冲区上。 我们使用混合将水面绘制到后面的缓冲区,以便水像素与地形混合,并在后面缓冲区上放置像素,使地形和木箱通过水显示。 从现在起,我们将研究混合技术,这些技术允许我们将当前光栅化的像素(所谓的源像素)与先前光栅化到后缓冲区的像素(所谓的目标像素)混合(组合)。 除了上面我们提到的,这种技术使我们能够渲染半透明物体,如水和玻璃。
在这里插入图片描述
为了便于学习,我们特别提到后缓冲区作为渲染目标, 但是,稍后我们将展示我们可以渲染到“屏幕外”渲染目标。 混合适用于这些渲染目标,目标像素是先前光栅化到这些屏幕外渲染目标的像素值。下面先介绍混合方程。

混合方程

设Csrc是我们当前光栅化的第i个像素(源像素)的像素着色器的颜色输出,并且让Cdst是当前在后缓冲器(目标像素)上的第i个像素的颜色。 如果没有混合,Csrc将覆盖Cdst(假设它通过深度/模板测试)并成为第i个后缓冲区像素的新颜色。 但是通过混合,Csrc和Cdst被混合在一起以获得将覆盖Cdst的新颜色C(即,混合颜色C将被写入后缓冲器的第i个像素)。 Direct3D使用以下混合方程来混合源像素和目标像素颜色:
在这里插入图片描述
颜色Fsrc(源混合因子)和Fdst(目标混合因子)可以是上述公式中描述的任何值,它们允许我们以各种方式修改原始源像素和目标像素,允许实现不同的效果, ⊗运算符意味着定义的颜色向量的分量乘法; 运算符可以是任何二元运算符。
上述混合方程仅适用于颜色的RGB分量, alpha组件实际上由一个单独的类似方程式处理:
在这里插入图片描述
等式基本相同,但混合因子和二元运算可能不同, 将RGB与alpha分离的动机很简单,因此我们可以独立处理它们,使用不同地方式处理它们,这会出现更多种可能性。
与混合RGB组件相比,混合alpha组件的频率要低得多, 这主要是因为我们不关心后缓冲区alpha值,如果我们有一些需要目标alpha值的算法,则后缓冲区alpha值才是重要的。

混合操作

混合方程中使用的二元运算符可以是以下之一:
在这里插入图片描述
在最小/最大操作中忽略混合因子。
这些相同的运算符也适用于alpha混合方程, 此外,我们可以为RGB和alpha指定不同的运算符。 例如,可以添加两个RGB术语,但减去两个alpha术语。

最近添加到Direct3D的一个功能是使用逻辑运算符而不是上面的传统混合方程来混合源颜色和目标颜色。 可用的逻辑运算符如下:

typedef 
enum D3D12_LOGIC_OP
{
  D3D12_LOGIC_OP_CLEAR   = 0,
  D3D12_LOGIC_OP_SET   = ( D3D12_LOGIC_OP_CLEAR + 1 ) ,
  D3D12_LOGIC_OP_COPY   = ( D3D12_LOGIC_OP_SET + 1 ) ,
  D3D12_LOGIC_OP_COPY_INVERTED   = ( D3D12_LOGIC_OP_COPY + 1 ) ,
  D3D12_LOGIC_OP_NOOP   = ( D3D12_LOGIC_OP_COPY_INVERTED + 1 ) ,
  D3D12_LOGIC_OP_INVERT   = ( D3D12_LOGIC_OP_NOOP + 1 ) ,
  D3D12_LOGIC_OP_AND   = ( D3D12_LOGIC_OP_INVERT + 1 ) ,
  D3D12_LOGIC_OP_NAND   = ( D3D12_LOGIC_OP_AND + 1 ) ,
  D3D12_LOGIC_OP_OR   = ( D3D12_LOGIC_OP_NAND + 1 ) ,
  D3D12_LOGIC_OP_NOR   = ( D3D12_LOGIC_OP_OR + 1 ) ,
  D3D12_LOGIC_OP_XOR   = ( D3D12_LOGIC_OP_NOR + 1 ) ,
  D3D12_LOGIC_OP_EQUIV   = ( D3D12_LOGIC_OP_XOR + 1 ) ,
  D3D12_LOGIC_OP_AND_REVERSE   = ( D3D12_LOGIC_OP_EQUIV + 1 ) ,
  D3D12_LOGIC_OP_AND_INVERTED   = ( D3D12_LOGIC_OP_AND_REVERSE + 1 ) ,
  D3D12_LOGIC_OP_OR_REVERSE   = ( D3D12_LOGIC_OP_AND_INVERTED + 1 ) ,
  D3D12_LOGIC_OP_OR_INVERTED   = ( D3D12_LOGIC_OP_OR_REVERSE + 1 ) 
} D3D12_LOGIC_OP;

请注意,我们不能同时使用传统的混合和逻辑运算符混合, 只能挑选一个或另一个。 另请注意,为了使用逻辑运算符混合,渲染目标格式必须支持 - 它应该是UINT变体的格式,否则将收到如下错误:

D3D12 ERROR: ID3D12Device::CreateGraphicsPipelineState: The render target format at slot 0 is format (R8G8B8A8_UNORM). This format does not support logic ops. The Pixel Shader output signature indicates this output could be written, and the Blend State indicates logic op is enabled for this slot. [ STATE_CREATION ERROR #678: CREATEGRAPHICSPIPELINESTATE_OM_RENDER_TARGET_DOES_NOT_SUPPORT_LOGIC_OPS]
D3D12 WARNING: ID3D12Device::CreateGraphicsPipelineState: Pixel Shader output ‘SV_Target0’ has type that is NOT unsigned int, while the corresponding Output Merger RenderTarget slot [0] has logic op enabled. This happens to be well defined: the raw bits output from the shader will simply be interpreted as UINT bits in the blender without any data conversion. This warning is to check that the application developer really intended to rely on this behavior. [ 
STATE_CREATION WARNING #677: CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_TYPE_MISMATCH]

混合因子

通过为源和目标混合因子设置不同的组合以及不同的混合运算符,可以实现许多不同的混合效果。 我们将在后面说明一些组合,但我们需要尝试其他组合以了解它们的作用。 以下列表描述了适用于Fsrc和Fdst的基本混合因子, 有关其他一些高级混合因子,请参阅SDK文档中的D3D12_BLEND枚举类型。 设Csrc =(rs,gs,bs),Asrc = as(从像素着色器输出的RGBA值),Cdst =(rd,gd,bd),Adst = ad(已存储在渲染目标中的RGBA值), F是Fsrc或Fdst而F是Fsrc或Fdst,我们有:

D3D12_BLEND_ZERO: F = (0, 0, 0) and F = 0
D3D12_BLEND_ONE: F = (1, 1, 1) and F = 1
D3D12_BLEND_SRC_COLOR: F = (rs, gs, bs)
D3D12_BLEND_INV_SRC_COLOR: Fsrc = (1 − rs, 1 − gs, 1 − bs)
D3D12_BLEND_SRC_ALPHA: F = (as, as, as) and F = as
D3D12_BLEND_INV_SRC_ALPHA: F = (1 − as, 1 − as, 1 − as) and F = (1 − as)
D3D12_BLEND_DEST_ALPHA: F = (ad, ad, ad) and F = ad
D3D12_BLEND_INV_DEST_ALPHA: F = (1 − ad, 1 − ad, 1 − ad) and F = (1 − ad) 
D3D12_BLEND_DEST_COLOR: F = (rd, gd, bd) 
D3D12_BLEND_INV_DEST_COLOR: F = (1 − rd, 1 − gd, 1 − bd)
D3D12_BLEND_SRC_ALPHA_SAT: F = (a′s, a′s, a′s) and F = a′s
  

其中a =clamp(as,0, 1)
D3D12_BLEND_BLEND_FACTOR:F =(r,g,b)和 F = a,其中颜色(r,g,b,a)被提供给ID3D12GraphicsCommandList :: OMSetBlendFactor方法的第二个参数。 这允许我们指定要直接使用的混合因子颜色,但是,在更改混合状态之前,它是恒定的。

D3D12_BLEND_INV_BLEND_FACTOR:F =(1-r,1-g,1-b)和F = 1-a,其中颜色(r,g,b,a)由ID3D12GraphicsCommandList :: OMSetBlendFactor方法的第二个参数提供, 这允许我们指定要直接使用的混合因子颜色; 但是,在您更改混合状态之前,它是恒定的。

所有上述混合因子都适用于RGB混合方程, 对于alpha混合等式,不允许以_COLOR结尾的混合因子。

Clamp函数定义如下:
在这里插入图片描述
我们可以使用以下函数设置混合因子颜色:

void ID3D12GraphicsCommandList::OMSetBlendFactor(
  const FLOAT BlendFactor[ 4 ]);
传递空指针会恢复默认的混合因子(1,1,1,1)。

混合状态

我们已经讨论过混合运算符和混合因子,但是我们在哪里用Direct3D设置这些值? 与其他Direct3D状态一样,混合状态是PSO的一部分。 到目前为止,我们一直在使用默认的混合状态,它禁用混合:

D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
…
opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);

要配置非默认混合状态,我们必须填写D3D12_BLEND_DESC结构。 D3D12_BLEND_DESC结构的定义如下:

typedef struct D3D12_BLEND_DESC {
 BOOL AlphaToCoverageEnable;  // Default: False
 BOOL IndependentBlendEnable;  // Default: False
 D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];
} D3D11_BLEND_DESC;

1、AlphaToCoverageEnable:指定true以启用alpha-to-coverage,这是一种在渲染树叶或栅栏纹理时非常有用的多重采样技术。 指定false以禁用alpha-to-coverage。 Alpha-to-coverage需要启用多重采样(即,使用多重采样创建背面和深度缓冲区)。
2、IndependentBlendEnable:Direct3D支持同时渲染多达八个渲染目标。 当此标志设置为true时,表示可以对每个渲染目标执行不同的混合(不同的混合因子,不同的混合操作,混合禁用/启用等)。 如果此标志设置为false,则表示将按照D3D12_BLEND_DESC :: RenderTarget数组中第一个元素所描述的相同方式混合所有渲染目标。 多个渲染目标用于高级算法; 现在,假设我们一次只渲染到一个渲染目标。
3、RenderTarget:一个包含8个D3D12_RENDER_TARGET_BLEND_DESC元素的数组,其中第i个元素描述了如何为第i个同时渲染目标进行混合。 如果IndependentBlendEnable设置为false,则所有渲染目标都使用RenderTarget [0]进行混合。
D3D12_RENDER_TARGET_BLEND_DESC结构的定义如下:

typedef struct D3D12_RENDER_TARGET_BLEND_DESC
{
  BOOL BlendEnable; // Default: False
  BOOL LogicOpEnable; // Default: False
  D3D12_BLEND SrcBlend; // Default: D3D12_BLEND_ONE
  D3D12_BLEND DestBlend; // Default: D3D12_BLEND_ZERO
  D3D12_BLEND_OP BlendOp; // Default: D3D12_BLEND_OP_ADD
  D3D12_BLEND SrcBlendAlpha; // Default: D3D12_BLEND_ONE
  D3D12_BLEND DestBlendAlpha; // Default: D3D12_BLEND_ZERO
  D3D12_BLEND_OP BlendOpAlpha; // Default: D3D12_BLEND_OP_ADD
  D3D12_LOGIC_OP LogicOp;   // Default: D3D12_LOGIC_OP_NOOP
  UINT8 RenderTargetWriteMask; // Default: D3D12_COLOR_WRITE_ENABLE_ALL
} D3D12_RENDER_TARGET_BLEND_DESC;

1、BlendEnable:指定true以启用混合,指定false以禁用它。 请注意,BlendEnable和LogicOpEnable不能都设置为true; 可以使用常规混合或逻辑运算符混合。
2、LogicOpEnable:指定true以启用逻辑混合操作, 请注意,BlendEnable和LogicOpEnable不能都设置为true,可以使用常规混合或逻辑运算符混合。
3、SrcBlend:D3D12_BLEND枚举类型的成员,它指定RGB混合的源混合因子Fsrc。
4、DestBlend:D3D12_BLEND枚举类型的成员,它指定RGB混合的目标混合因子Fdst。
5、BlendOp:D3D12_BLEND_OP枚举类型的成员,用于指定RGB混合运算符。
6、SrcBlendAlpha:D3D12_BLEND枚举类型的成员,它指定用于Alpha混合的目标混合因子Fsrc。
7、DestBlendAlpha:D3D12_BLEND枚举类型的成员,它指定用于Alpha混合的目标混合因子Fdst。
8、BlendOpAlpha:D3D12_BLEND_OP枚举类型的成员,指定alpha混合运算符。
9、LogicOp:D3D12_LOGIC_OP枚举类型的成员,指定用于混合源颜色和目标颜色的逻辑运算符。
10、RenderTargetWriteMask:以下一个或多个标志的组合:

typedef enum D3D12_COLOR_WRITE_ENABLE {
 D3D12_COLOR_WRITE_ENABLE_RED   = 1,
 D3D12_COLOR_WRITE_ENABLE_GREEN  = 2,
 D3D12_COLOR_WRITE_ENABLE_BLUE  = 4,
 D3D12_COLOR_WRITE_ENABLE_ALPHA  = 8,
 D3D12_COLOR_WRITE_ENABLE_ALL   = 
   ( D3D12_COLOR_WRITE_ENABLE_RED | D3D12_COLOR_WRITE_ENABLE_GREEN | 
    D3D12_COLOR_WRITE_ENABLE_BLUE | D3D12_COLOR_WRITE_ENABLE_ALPHA ) 
} D3D12_COLOR_WRITE_ENABLE;

这些标志控制在混合后写入后缓冲区中的哪些颜色通道? 例如,我们可以通过指定D3D12_COLOR_WRITE_ENABLE_ALPHA禁用对RGB通道的写入,并仅写入Alpha通道。 这种灵活性对于高级技术非常有用。 禁用混合时,将使用从像素着色器返回的颜色,而不应用写入蒙版。
混合不是免费的,并且需要额外的每像素工作,因此只有在需要时才启用它,并在完成后将其关闭。
以下代码显示了创建和设置混合状态的示例代码:

// Start from non-blended PSO 
D3D12_GRAPHICS_PIPELINE_STATE_DESC transparentPsoDesc = opaquePsoDesc;
 
D3D12_RENDER_TARGET_BLEND_DESC transparencyBlendDesc;
transparencyBlendDesc.BlendEnable = true;
transparencyBlendDesc.LogicOpEnable = false;
transparencyBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
transparencyBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
transparencyBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
transparencyBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
transparencyBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
transparencyBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
transparencyBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
transparentPsoDesc.BlendState.RenderTarget[0] = transparencyBlendDesc;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
  &transparentPsoDesc, IID_PPV_ARGS(&mPSOs["transparent"])));

与其他PSO一样,我们应该在应用程序初始化时创建它们,然后根据需要使用ID3D12GraphicsCommandList :: SetPipelineState方法在它们之间切换。

阅读更多

扫码向博主提问

海洋_

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 3D引擎架构
  • 服务器架构
  • GPU渲染
  • 客户端架构
  • 引擎优化
去开通我的Chat快问
换一批

没有更多推荐了,返回首页