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

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. Linux主机入侵检测

    检查系统信息.用户账号信息 ● 操作系统信息 cat /proc/version 用户信息 用户信息文件 /etc/passwd root:x:0:0:root:/root:/bin/bash 用户名 ...

  2. vue3.x新特性之setup函数,看完就会用了

    最近有小伙伴跟我聊起setup函数,因为习惯了vue2.x的写法导致了,setup用起来觉得奇奇怪怪的,在一些api混编的情况下,代码变得更加混乱了,个人觉得在工程化思想比较强的团队中使用setup确 ...

  3. Spring Cloud Gateway Route Predicate Factory 的使用

    Spring Cloud Gateway的使用 一.需求 二.基本组成 1.简介 2.核型概念 1.Route 路由 2.Predicate 谓语.断言 3.Filter 过滤器 3.工作原理 三.网 ...

  4. stm32f103中断学习总结

    一.NVIC 介绍 NVIC 英文全称是 Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,它属于 M3 内核的一个外设,控制着芯片的中断相关功 ...

  5. 快速了解XML

    1. XML 定义 可扩展标记语言,标准通用标记语言的子集,简称XML.是一种用于标记电子文件使其具有结构性的标记语言. 2. XML 展示 如下是一个xml的标记展示,XML 是不作为的XML 被设 ...

  6. 跳台阶 牛客网 剑指Offer

    跳台阶 牛客网 剑指Offer 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). class Solution: #ru ...

  7. SpringBoot2.x异步任务EnableAsync

    1.springboot启动类里面使用@EnableAsync注解开启异步功能 @EnableAsync public class Demo001Application { public static ...

  8. js事件常用操作、事件流

    注册事件 给元素添加事件,称为注册事件或者绑定事件. 注册事件有两种方式:传统方式和方法监听注册方式 传统方式 on开头的事件,例如onclick <button onclick="a ...

  9. 2021 陇剑杯wp

    前言 这比赛应该叫应急响应比赛,而且flag交三次就不能交了,就因为我交错一道题然后差一道进线下,气死了. Jwt 2.1 jwt 题目提示 2.2 搜索username得到 10087#admin ...

  10. SpringCloud config native 配置

    1.概述 最近项目使用springCloud 框架,使用config搭建git作为配置中心. 在私有化部署中,出现很多比较麻烦的和鸡肋的设计. 每次部署都需要安装gitlab 有些环境安装完gitla ...