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图像的存储方式,页面翻 ...
随机推荐
- OO助教工作总结
\(OO\)助教的工作结束了,在这一学期中,我主要负责对作业进行测试,对指导书进行检查,讨论区管理,部分数据构造,以及完成随班助教的工作. 测试 指导书检查 每次指导书公开前我都会先把指导书看 ...
- 【Azure Redis 缓存】Windows版创建 Redis Cluster 实验 (精简版)
简介 学习Redis Cluster的第一步,即本地搭建Redis Cluster.但是在Redis的官方文档中,是介绍在Linux系统中搭建Redis Cluster.本文主要介绍在Windows系 ...
- HMS Core Keyring携手航班管家和高铁管家,打造美好出行体验
高铁管家是国内最早⽀持⼿机⽀付购买⽕⻋票App之⼀,日活用户超380万,为⽤户提供一站式铁路出⾏服务.高铁管家母公司--深圳市活⼒天汇科技股份有限公司是国内智能⼤出⾏的开创者,先后推出航班管家.⾼铁管 ...
- 安装hexo博客
前言 ** 跟着步骤一步一步来进行安装 ** 准备环境:node.js和包管理器npm 1:查看包文件 接着安装 淘宝镜像源 sudo这个需要添加获取文件夹访问权限 sudo npm install ...
- 关于STM32 (Cortex-M3) 中NVIC的分析
一.STM32 (Cortex-M3) 中的优先级概念 STM32(Cortex-M3)中有两个优先级的概念:抢占式优先级和响应优先级,也把响应优先级称作"亚优先级"或" ...
- AtCoder Beginner Contest 220部分题(G,H)题解
刚开始的时候被E题卡住了,不过发现是个数学题后就开始使劲推式子,幸运的是推出来了,之后的F题更是树形DP换根的模板吧,就草草的过了,看了一眼G,随便口胡了一下,赶紧打代码,毕竟时间不多了,最后也没打完 ...
- loto仪器_如何模拟输出凸轮轴和曲轴波形_用任意波形信号源SIG852?
loto仪器_如何模拟输出凸轮轴和曲轴波形_用任意波形信号源SIG852? 在汽车传感器的波形检测应用中,有时候需要模拟各种汽车传感器的输出信号,用来驱动和监测对应的执行机构或者电路是否正常,这其中, ...
- linux查看和修改时间
查看时间: # date Fri Jan 11 18:04:10 CST 2020设置时间 # date -s "19:20:30"设置日期+时间 # date -s " ...
- VUE的MVVM框架解析
这篇文章主要介绍了MVVM模式中ViewModel和View.Model有什么区别?本文分别解释了它们的功能和作用,然后总结了它之间的区别,需要的朋友可以参考下 Model:很简单,就是业务逻辑相关的 ...
- Django 小实例S1 简易学生选课管理系统 12 CSS样式完善
Django 小实例S1 简易学生选课管理系统 第12节--CSS样式完善 点击查看教程总目录 作者自我介绍:b站小UP主,时常直播编程+红警三,python1对1辅导老师. 课程模块的逻辑代码到这里 ...