DirectX12 3D 游戏开发与实战第十章内容(下)
仅供个人学习使用,请勿转载。谢谢!
10、混合
本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合。因此,该技术可以用来渲染如水和玻璃之类的半透物体。
学习目标
- 理解混合技术的工作原理,并且在Direct3D中运用该技术
- 学习Direct3D所支持的不同混合模式
- 探究如何使用alpha分量来调节图元的透明度
- 学会仅通过HLSL中的clip函数来组织向后台缓冲区中绘制像素
10.6、alpha通道
源alpha分量可以用于在RGB混合的过程中控制像素的透明度,而混合方程中的源颜色实际上来自于像素着色器。在第九章中,我们将漫反射材质中的alpha分量作为纹理着色器的alpha分量输出,因此,我们可以利用漫反射图(diffuse map)中的alpha通道来控制混合过程中的透明度了。
float4 PS(VertexOut pin) : SV_Target
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC)*gDiffuseAlbedo;
……
//从漫发射反照率获取alpha值的常用方法
litColor.a = diffuseAlbedo.a;
return litColor;
}
我们可以在常见的图像编辑软件中添加alpha通道,然后到处为支持alpha通道的格式,如DDS
10.7、裁剪像素
有些时候,我们希望彻底禁止某一个像素参与后续的处理,这时候我们可以通过HLSL中的内置函数clip(x)实现,该函数仅供像素着色器使用,当x < 0时,该像素将会不在参与后面的处理阶段,因此用这个函数来绘制透明和非透明相见的像素是最合适的了。(比如铁丝网)
在像素着色器中,我们将会采集像素的alpha分量,如果该值极小接近于0,则表明该像素是完全透明的,那么我们将会通过clip(x)函数将该像素彻底从后续的处理阶段中移除。
float4 PS(VertexOut pin) : SV_Traget
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC)*gDiffuseAlbedo;
#ifdef ALPHA_TEXT
//如果alpha < 0.1f,则抛弃该像素,我们要在着色器中尽早执行该操作,以尽快检测处满足条件的像素并
//退出着色器,从而跳过后续的处理过程
clip(diffuseAlbedo.a - 0.1f);
#endif
……
//从漫发射反照率中获取alpha值的常用手段
litColor.a = diffuseAlbedo.a;
return litColor;
}
通过上述的代码可以看出,只有在定义了ALPHA_TEXT宏的时候我们才会执行透明像素的筛选。因为我们有时候可以不希望对某些渲染项执行clip(x)方法,所以我们要有能力针对特殊的着色器开启或关闭clip函数的调用,而且alpha测试的开销也不小,因此我们不能一直开着。
注意:通过混合操作也可以实现相同的效果,但是我们要优先使用clip函数:
- 无需执行混合运算(可以禁用混合运算)
- 处理期间不需要考虑绘制顺序的问题
- 通过提前从像素着色器筛选出要抛弃的像素,可以避免该像素参与后续的处理过程
10.8、雾
接下来我们将介绍如何实现雾化效果,实现雾化效果的流程如下:
- 指明雾气的颜色、由摄像机到雾气的最近距离以及雾气的分散范围
- 将网格三角形上点的颜色设置为原色于雾气颜色的加权平均值
\]
参数s的范围为[0 , 1]之间,由一个函数确定(该函数的参数为摄像机位置、被雾气覆盖物体表面点)。参数s的定义如下:
\]
其中saturate会将其参数限制在范围[0, 1]之间,dist(p, E)表示表面点p到摄像机位置E之间的距离。
当dist(p, E)小于fogStart的时候,雾色将不会改变物体顶点的本色,即s = 0;
\]
当dist(p, E)大于fogEnd(fogStart + fogRange)时,雾色将会完全遮住物体,即s = 1;
\]
下面的代码将展示如何实现雾化效果,我们先计算距离,然后再像素层级进行插值,最后求出光照颜色。
// Default.hlsli文件
// 光照数量的默认值
#ifndef NUM_DIR_LIGHTS
#define NUM_DIR_LIGHTS 3
#endif
#ifndef NUM_POINT_LIGHTS
#define NUM_POINT_LIGHTS 0
#endif
#ifndef NUM_SPOT_LIGHTS
#define NUM_SPOT_LIGHTS 0
#endif
// 包含光照所用的结构体和函数
#include "LightingUtil.hlsli"
Texture2D gDiffuseMap : register(t0);
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
// 每一帧都在变化的常量数据
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
float4x4 gTexTransform;
};
// 绘制过程中所使用的杂项常量数据
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
float4 gAmbientLight;
// 允许应用程序在每一帧都能改变雾化效果的参数
// 比如我们可能只在一天中的特定时间才使用雾化效果
float4 gFogColor;
float gFogStart;
float gFogRange;
float2 cbPerObjectPad2;
Light gLights[MaxLights];
};
//每种材质中不同的常量数据
cbuffer cbMaterial : register(b2)
{
//漫反射反照率
float4 gDiffuseAlbedo;
//介质的一种属性
float3 gFresnelR0;
//粗造程度
float gRoughness;
//材质变换矩阵
float4x4 gMatTransform;
};
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 TexC : TEXCOORD;
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 TexC : TEXCOORD;
};
// VS.hlsl文件
#include "Default.hlsli"
VertexOut VS(VertexIn vin)
{
VertexOut vout = (VertexOut)0.0f;
//将顶点变换到世界空间
float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
vout.PosW = posW.xyz;
//假设进行的是等比变换,否则要使用世界矩阵的逆转置矩阵
vout.NormalW = mul(vin.NormalL, (float3x3) gWorld);
//将顶点变换到齐次裁剪空间
vout.PosH = mul(posW, gViewProj);
//为三角形插值输出顶点属性
float4 texC = mul(float4(vin.TexC, 1.0f, 1.0f), gTexTransform);
vout.TexC = mul(texC, gMatTransform).xy;
return vout;
}
// PS.hlsl文件
#include "Default.hlsli"
float4 PS(VertexOut pin) : SV_Target
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
//alpha通道测试
#ifndef ALPHA_TEXT
clip(diffuseAlbedo.a - 0.1f);
#endif
//对法线插值可能导致其非规范化,所以需要重新对他进行规范化处理
pin.NormalW = normalize(pin.NormalW);
//光线经过物体表面一点反射到观察点这一方向上的向量
float3 toEyeW = gEyePosW - pin.PosW;
float distToEye = length(toEyeW);
//规范化处理
toEyeW /= distToEye;
float4 ambient = gAmbientLight * diffuseAlbedo;
const float shininess = 1.0f - gRoughness;
Material mat = { diffuseAlbedo, gFresnelR0, shininess };
float3 shadowFactor = 1.0f;
float4 directLight = ComputeLighting(gLights, mat, pin.PosW, pin.NormalW, toEyeW, shadowFactor);
float4 litColor = ambient + directLight;
#ifndef FOG
float fogAmount = saturate((distToEye - gFogStart) / gFogRange);
litColor = lerp(litColor, gFogColor, fogAmount);
#endif
//从漫反射反照率中获取alpha常用的手段
litColor.a = diffuseAlbedo.a;
return litColor;
}
在演示程序中,我们通过向CompileShader函数提供下列D3D_SHADER_MACRO结构体来开启雾化效果
const D3D_SHADER_MACRO defines[] =
{
"FOG","1",
NULL,NULL
};
mShadersp["opaquePS"] = d3dUtil::CompileShader(L"HLSL\\PS.hlsl",defines,"PS","ps_5_1");
演示程序运行效果:

10.9、小结
- 混合是一种将档期那光栅化的像素(源像素)与之前的已经完成关光栅化的像素并存在后台缓冲区中的像素(目标像素)相互融合的技术,混合使我们可以渲染出半透明效果的物体,比如水和玻璃
- RGB分量和alpha分量的混合运算是各自单独展开的,目的是希望可以扩展出更多效果
- 混合因子是混合方程可以自定义的法宝,这些混合因子是枚举类型D3D12_BLEND中的成员之一。注意,对于alpha分量来说,不可以使用以COLOR为后缀的混合因子
- 源alpha的信息来自于漫发射材质,在外面自己编写的应用程序框架中,漫反射材质由纹理图来定义,而源alpha就是纹理图中的alpha通道
- 通过HLSL的内部函数clip(x)可以将源像素从后续的处理过程中完全屏蔽掉
- 可以使用雾化效果来模拟各种气象环境效果和大气透视效果,以此来掩饰远处场景渲染的失真以及物体忽然出现在视锥体之中从而闯进用户视野的情况。
DirectX12 3D 游戏开发与实战第十章内容(下)的更多相关文章
- DirectX12 3D 游戏开发与实战第十章内容(上)
仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...
- DirectX12 3D 游戏开发与实战第八章内容(下)
DirectX12 3D 游戏开发与实战第八章内容(下) 8.9.材质的实现 下面是材质结构体的部分代码: // 简单的结构体来表示我们所演示的材料 struct Material { // 材质唯一 ...
- DirectX12 3D 游戏开发与实战第八章内容(上)
8.光照 学习目标 对光照和材质的交互有基本的了解 了解局部光照和全局光照的区别 探究如何用数学来描述位于物体表面上某一点的"朝向",以此来确定入射光照射到表面的角度 学习如何正确 ...
- DirectX12 3D 游戏开发与实战第九章内容(上)
仅供个人学习使用,请勿转载. 9.纹理贴图 学习目标: 学习如何将局部纹理映射到网格三角形上 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次纹理贴图 ...
- DirectX12 3D 游戏开发与实战第一章内容
DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...
- DirectX12 3D 游戏开发与实战第二章内容
矩阵代数 学习目标 理解矩阵及其相关运算的定义 探究为何能把向量和矩阵的乘法视为一种线性组合 学习单位矩阵.转置矩阵.行列式以及矩阵的逆等概念 逐步熟悉DirectXMath库中提供的关于矩阵计算的类 ...
- DirectX12 3D 游戏开发与实战第九章内容(下)
仅供个人学习使用,请勿转载.谢谢! 9.纹理贴图 学习目标 学习如何将局部纹理映射到网格三角形中 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次贴图 ...
- DirectX12 3D 游戏开发与实战第五章内容
渲染流水线 学习目标: 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素 探索如何用Direct3D表示3D对象 学习如何建立虚拟摄像机 理解渲染流水线,根据给定的3D场景的几何描 ...
- DirectX12 3D 游戏开发与实战第四章内容(上)
Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...
随机推荐
- mall笔记
介绍 SpringBoot.SpringCloud.SpringCloudAlibaba.Nacos.Sentinel.Seata整合demo. 软件架构 JDK 1.8 Spring Boot 2. ...
- C# 如何使用代码添加控件及控件事件
1.首先简单设计一下界面: 添加了Click事件 <Window x:Class="WpfApp.MainWindow" xmlns="http://schemas ...
- 计算机网络:HTTP
计算机网络基础:HTTP 先验知识 HTTP和其他协议的关系 通过下图,了解IP协议,TCP协议,DNS服务在使用HTTP协议通信过程中各自发挥的作用: 服务器处理流程 接受客户端连接 ------& ...
- UltraSoft - Alpha - Scrum Meeting 7
Date: Apr 22th, 2020. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM 完成课程中心的json格式传递 完成邮箱验证机制 刘zh 前端 ...
- [luogu2973]driving out the piggies 驱逐猪猡【高斯消元+概率DP】
看到题面的那一刻,我是绝望的ORZ 图论加概率期望加好像不沾边的高斯消元???我人直接傻掉 还没学过概率期望的我果断向题解屈服了(然后还是傻掉了两节课来找线性方程.. Description 奶牛们建 ...
- 洛谷 P5785 [SDOI2012] 任务安排
链接: P5785 弱化版:P2365 题意: 有 \(n\) 个任务待完成,每个任务有一个完成时间 \(t_i\) 和费用系数 \(f_i\),相邻的任务可以被分成一批.从零时刻开始这些任务会被机器 ...
- Python 语法错误 except Exception, e: ^ SyntaxError: invalid syntax
出这个问题是因为python2和python3 语法有些不同 python2 和 3 处理 except 子句的语法有点不同,需要注意: Python2 try: print ("hello ...
- 一步一步学ROP之linux_x86篇(蒸米spark)
目录 一步一步学ROP之linux_x86篇(蒸米spark) 0x00 序 0x01 Control Flow Hijack 程序流劫持 0x02 Ret2libc – Bypass DEP 通过r ...
- 常用的package.json以及React相关
常用的package.json以及React相关 前言 package.json 的简单介绍 简单版的 package.json 必备属性(name & version) name 字段 ve ...
- S2-002漏洞分析
漏洞概述 Struts2-002是一个 XSS 漏洞,该漏洞发生在 <s:url> 和 <s:a>标签中,未对标签内字符进行转义,当标签的属性 includeParams=al ...