仅供个人学习使用,请勿转载。谢谢!

10、混合

本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合。因此,该技术可以用来渲染如水和玻璃之类的半透物体。

学习目标

  1. 理解混合技术的工作原理,并且在Direct3D中运用该技术
  2. 学习Direct3D所支持的不同混合模式
  3. 探究如何使用alpha分量来调节图元的透明度
  4. 学会仅通过调用HLSL中的clip函数来阻止向后台缓冲区中绘制像素

10.1、混合方程

设C为像素着色器输出的当前正在光栅化的第i行、第j列像素(源像素)的颜色值,D为目前在后台缓冲区中与之对应的第i行、第j列像素(目标像素)的颜色值。如果不使用混合技术,C将会直接覆盖D,从而使后台缓冲区中第i行、第j列的像素更新为新的颜色值C,如果使用混合技术,C将会和D混合成新的颜色值再去覆盖D。

Direct3D使用下列混合方程来使源像素和目标像素融合:

\[new = C·F1*D·F2
\]

其中F1和F2分别为源混合因子目标混合因子,我们将会在10.3节介绍,运算符 · 表示针对颜色向量而定义的分量式乘法,* 表示将会在10.2节介绍的二元运算符。

上述混合方程仅用于RGB分量,而alpha分量由另一条混合方程处理。这里不介绍了。两条混合方程的本质是一样的,之所以分开处理RGB分量和alpha分量主要是希望可以由此产生更多不同的混合效果

10.2、混合运算

下列枚举项将会用作混合方程中的二元运算符 * :

typedef enum D3D12_BLEND_OP
{
D3D12_BLEND_OP_ADD = 1, //加
D3D12_BLEND_OP_SUBTRACT = 2, //源像素·源混合因子-目标像素·目标混合因子
D3D12_BLEND_OP_REV_SUBTRACT = 3, //减数和被减数调换位置
D3D12_BLEND_OP_MIN = 4, //取最小值
D3D12_BLEND_OP_MAX = 5 //取最大值
}D3D12_BLEND_OP;

上述二元运算符也可以用于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;

注意点:我们不同同时使用传统混合方程和逻辑运算符这两种混合手段,我们只能二选一。如果我们要使用逻辑运算符混合技术,就一定要选择它所支持的渲染目标格式——UNIT(无符号整数),否则会报错

10.3、混合因子

通过为源混合因子和目标混合因子设置不同的混合运算符,我们就可以实现各种各样的混合效果。这里就不列举混合因子了,如果有兴趣的可以查看SDK文档中的D3D12_BLEND枚举类型

10.4、混合状态

前面已经介绍过混合运算符和混合因子了,那么我们要如何使用Direct3D来设置这些数值?

答:混合状态也是PSO(流水线状态对象)的一部分,只不过我们之前没有去设置它,即没有开启混合技术。

opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);

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

typedef struct D3D12_BLEND_DESC{
BOOL AlphaToCoverageEnable; //默认为false
BOOL IndependentBlendEnable; //默认为false
D3D12_RENDER_TARGET_BLEND_DESC RenderTarget[ 8 ];
}

参数介绍:

  1. AlphaToCoverageEnable:如果指定为true,则启用alpha-to-coverage功能,这是一种在渲染叶片或者门等纹理时极其有用的一种多重采样技术。(开启此项技术需要开启多重采样)
  2. IndependentBlendEnable:Direct3D最多同时支持8个渲染目标,如果该标志被设置为ture,则表明可以向每一个渲染目标执行不同的混合操作,如果该标志为false,则每个渲染目标将会采用D3D12_BLEND_DESC::RenderTarget数组中第一个元素所描述的方式进行混合操作。(多渲染目标技术常用于高级算法,我们现在每次仅向一个渲染目标进行绘制)
  3. RenderTarget:具有8个D3D12_RENDER_TARGET_BLEND_DESC元素的数组,其中第i个元素描述了如何对第i个渲染目标进行混合处理。

结构体D3D12_RENDER_TARGET_BLEND_DESC结构体的定义如下:

typedef struct D3D12_RENDER_TARGET_BLEND_DESC
{
BOOL BlendEnable; //是否开启常规混合运算
BOOL LogicOpEnable; //是否开启逻辑混合运算(常规混合运算和逻辑混合运算只能二选一)
D3D12_BLEND SrcBlend; //指定RGB混合运算中的源混合因子
D3D12_BLEND DestBlend; //指定RGB混合运算中的目标混合因子
D3D12_BLEND_OP BlendOp; //指定RGB混合运算中的混合运算符
D3D12_BLEND SrcBlendAlpha; //指定alpha混合运算中的源混合因子
D3D12_BLEND DestBlendAlpha; //指定alpha混合运算中的目标混合因子
D3D12_BLEND_OP BlendOpAlpha;//指定alpha混合运算中的混合运算符
D3D12_LOGIC_OP LogicOp; //指定源颜色和目标颜色所使用的逻辑运算符
UINT8 RenderTargetWriteMask;//控制混合后的数据可以被写入后台缓冲区中的那些颜色通道
}D3D12_RENDER_TARGET_BLEND_DESC;

下面的代码将会展示如何创建和设置混合状态:

//创建开启混合功能的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方法在不同的状态之间来回切换。

10.5、混合示例

在下面的各个小结中,我们将讨论一些用于获取特效的混合因子组合,在这些示例中,我们暂时只关注RGB分量,不考虑alpha分量。(RGB分量和alpha分量的处理方式类似)

10.5.1、禁止颜色的写操作

如果希望原始的目标像素保持不变,即不对目标像素进行覆盖,也不和目标像素进行混合。为了实现这个功能,我们只需要把源混合因子设置为D3D12_BLEND_ZERO,将目标混合因子设置为D3D12_BLEND_ONE,在将混合运算符设置为D3D12_BLEND_OP_DAA即可,此时混合方程为:

\[new = C·(0,0,0)+D·(1,,1,1)
\]

然而实际上我们有一个更简便的方法实现上述功能,即将成员D3D12_RENDER_TARGET_BLEND_DESC::RenderTargetWriteMask设置为0,便可以禁止向任何颜色通道执行写操作。

10.5.2、加法混合和减法混合

如果希望源像素和目标像素实现加法运算,我们可以将源混合因子和目标混合因子都设置为D3D12_BLEND_ONE,然后把混合运算符设置为D3D12_BLEND_OP_ADD即可,此时混合方程为

\[new = C·(1,1,1)+D·(1,,1,1)
\]

如果继续使用上述混合因子,但是将混合运算符替换为D3D12_BLEND_OP_SUBTRACT,我们便可以实现减法混合。

10.5.3、乘法混合

如果希望源像素和目标像素实现乘法运算,我们可以将源混合因子设置为D3D12_BLEND_ZERO,目标混合因子设置为D3D12_BLEND_SRC_COLOR,在将混合运算符设置为D3D12_BLEND_OP_ADD,此时混合方程为

\[new = C·(0,0,0)+D·C
\]

10.5.4、透明混合

源alpha分量a是一种可以控制源像素不透明度的百分比的分量,假设我们希望基于源像素的不透明度,将源像素和目标像素进行混合,为了实现此效果,我们可以将源混合因子设置为D3D12_BLEND_SRC_ALPHA,目标混合因子设置为D3D12_BLEND_INV_SRC_ALPHA,并且将混合运算符设置为D3D12_BLEND_OP_ADD,此时混合方程为:

\[new = C·(a,a,a)+D(1-a,1-a,1-a)
\]

例如,a = 0.25,则源像素的不透明度为25%,也就是说,源像素和目标像素进行混合的时候,最终颜色将会由25%的源像素和75%的目标像素组成(源像素会位于目标像素的后侧)。

如果使用上述透明混合方法,我们需要考虑物体的绘制顺序,首先要绘制无序混合处理的物体,然后根据混合物体到摄像机的距离对他们进行排序,最后按由远到近的顺序通过混合的方式依次绘制这些物体。因为每一个透明的物体都应该要可以看到它后面的物体,而进行混合运算时,每一个物体都会和其后所有的物体进行混合运算,因此我们需要将透明物体后面的物体的像素都事先写入后台缓冲区中,然后将透明物体的源像素与其后面的目标像素进行混合。

105.5、混合与深度缓冲区

在使用加法/减法/乘法运算进行混合时,会涉及到深度测试这一问题,这里将会通过加法混合进行讲解。

如果要使用加法混合来渲染一个物体集合S,且不希望S中的物体相互遮挡,这时我们不希望在S中开启深度测试,如果在S中开启深度测试,且没有从后到前进行绘制,那么S中的俩个物体如果存在遮挡,靠后的像素片段将会被丢弃,这意味该物体的像素颜色将不会被累加到混合求和的结果中。

为了解决上面的问题,我们可以在渲染S中的物体的时候,通过禁止向深度缓冲区的写操作来禁用S中的物体之间的加法测试,因为向深度缓冲区的写操作被禁止之后,物体的深度信息将不会写入深度缓冲区中,从而避免了深度测试。

DirectX12 3D 游戏开发与实战第十章内容(上)的更多相关文章

  1. DirectX12 3D 游戏开发与实战第十章内容(下)

    仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...

  2. DirectX12 3D 游戏开发与实战第八章内容(下)

    DirectX12 3D 游戏开发与实战第八章内容(下) 8.9.材质的实现 下面是材质结构体的部分代码: // 简单的结构体来表示我们所演示的材料 struct Material { // 材质唯一 ...

  3. DirectX12 3D 游戏开发与实战第八章内容(上)

    8.光照 学习目标 对光照和材质的交互有基本的了解 了解局部光照和全局光照的区别 探究如何用数学来描述位于物体表面上某一点的"朝向",以此来确定入射光照射到表面的角度 学习如何正确 ...

  4. DirectX12 3D 游戏开发与实战第九章内容(上)

    仅供个人学习使用,请勿转载. 9.纹理贴图 学习目标: 学习如何将局部纹理映射到网格三角形上 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次纹理贴图 ...

  5. DirectX12 3D 游戏开发与实战第一章内容

    DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...

  6. DirectX12 3D 游戏开发与实战第二章内容

    矩阵代数 学习目标 理解矩阵及其相关运算的定义 探究为何能把向量和矩阵的乘法视为一种线性组合 学习单位矩阵.转置矩阵.行列式以及矩阵的逆等概念 逐步熟悉DirectXMath库中提供的关于矩阵计算的类 ...

  7. DirectX12 3D 游戏开发与实战第九章内容(下)

    仅供个人学习使用,请勿转载.谢谢! 9.纹理贴图 学习目标 学习如何将局部纹理映射到网格三角形中 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次贴图 ...

  8. DirectX12 3D 游戏开发与实战第五章内容

    渲染流水线 学习目标: 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素 探索如何用Direct3D表示3D对象 学习如何建立虚拟摄像机 理解渲染流水线,根据给定的3D场景的几何描 ...

  9. DirectX12 3D 游戏开发与实战第四章内容(上)

    Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...

随机推荐

  1. Scrum Meeting 最终总结

    [软工小白菜]Scrum Meeting 最终总结 2020/4/28 一.会议内容 1.工作及计划 组员代号 完成的工作 明日计划 炎龙 1.整合了整个程序,生成了apk并且上传审核 无 风鹰 1. ...

  2. redis中lua脚本的简单使用

    一.背景 在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能.比如: 扣减库存操作.限流操作等等. redis的pipelining虽然 ...

  3. Noip模拟16 2021.7.15

    题目真是越来越变态了 T1 Star Way To Heaven 首先,你要看出这是一个最小生成树的题(妙吧?) 为什么可以呢? 我们发现从两点连线的中点过是最优的,但是上下边界怎么办呢? 我们把上下 ...

  4. jzoj6094

    题目描述 给定一个循环流(每个点均满足流量平衡条件),这个循环流有$n$个点,且每条边的流量只有 $1$ 或$ 2$,其中$a$条边流量为$1$,$b$条边流量为$2$,判断是否存在一个流满足上述条件 ...

  5. 设计的MOS管三极管简单开关电路驱动能力不够1

    您需要 登录 才可以下载或查看,没有帐号?注册 x . ?& P' U5 r/ ~& `: B 用AOD409设计的开关电路为什么驱动能力不够,请大家帮忙分析一下原因啊.这个电路作用就 ...

  6. 【代码更新】单细胞分析实录(20): 将多个样本的CNV定位到染色体臂,并画热图

    之前写过三篇和CNV相关的帖子,如果你做肿瘤单细胞转录组,大概率看过: 单细胞分析实录(11): inferCNV的基本用法 单细胞分析实录(12): 如何推断肿瘤细胞 单细胞分析实录(13): in ...

  7. SpringBoot配置文件自动映射到属性和实体类(8)

    一.配置文件加载 1.Controller中配置并指向文件 @Controller @PropertySource(value = { "application.properties&quo ...

  8. Linux usb 3. Host 详解

    文章目录 1. 简介 2. Usb Core 驱动设备模型 2.1 Usb Device Layer 2.1.1 device (struct usb_device) 2.1.2 driver (st ...

  9. macos command 'clang' failed with exit status 1

    export CC=$(which gcc)export CXX=$(which g++)pip install fbprophet CC=clang pip install gevent

  10. PHP、TP6框架及JavaScript的单步调试

    目录 一.PHP程序的调试 1. 单个PHP程序的调试 2. PHP框架代码的调试 二.JavaScript程序的调试 三.总结 参考资料:https://www.bilibili.com/video ...