DirectX12 3D 游戏开发与实战第九章内容(下)
仅供个人学习使用,请勿转载。谢谢!
9、纹理贴图
学习目标
- 学习如何将局部纹理映射到网格三角形中
- 探究如何创建和启用纹理
- 学会如何通过纹理过滤来创建更加平滑的图像
- 探索如何使用寻址模式来进行多次贴图
- 探究如何将多个纹理进行组合,从而创建出新的纹理和特效
- 学习如何通过纹理动画来创建一些基本效果
9.5、过滤器
9.5.1、放大
假设玩家慢慢接近了场景中的一堵墙壁,则墙壁将会被逐渐放大并占据整个屏幕,假设显示器的分辨率为1024x1024,而墙壁纹理的分辨率为256x256。那么这将会产生纹理放大(因为我们试图使用少量纹素来覆盖大量的像素)。为了解决纹理和像素分辨率不匹配的问题,我们一般会对纹素之间的颜色数据进行插值运算,从而获得指定纹素处的颜色信息。图形硬件常用的插值方法有线性插值和常数插值两种方法。其中线性插值比较常用。
补充小知识:在纹理这一语境中,通过常数插值求取纹素之间纹理坐标处的纹理数据也称为点过滤,通过线性插值求取纹素之间的纹理坐标处的纹理数据也被称为线性过滤。点过滤和线性过滤是Direct3D中常用的术语。
9.5.2、缩小
纹理缩小是纹理放大的逆运算,在缩小的过程中,大量纹素将会被映射到少数纹理之中。比如墙壁的分辨率为256x256,当玩家逐渐远离墙壁的时候,墙壁在显示器上会越来越小,它所覆盖的像素区域也将逐渐变小,这是便会产生纹理缩小(因为我们试图使用大量纹素来覆盖少量像素)。
为了解决纹理缩小的问题,我们同样可以采用点过滤和线性过滤的方法。从直观上来说,我们可以通过平均下采样(average downsampling)使256x256像素减少到64x64像素。在初始化期间,我们可以通过对图像下采样来创建mipmap链便可以制作出缩小版的纹理(提前制作出不同规格的纹理)。图像硬件将会根据程序元的设定,从以下两种执行方案中任选一种进行操作:
1、在纹理贴图的时候,选择和待投影到屏幕上的几何体分辨率最为匹配的mipmap层级别,并且根据需求选用常数插值或者线性插值,这种技术成为i针对mipmap的点过滤
2、在纹理贴图的时候,选区和待投影到屏幕上的几何体分辨率最为匹配的两个邻接的mipmap成绩,然后对这两种mipamp层级分别应用常量过滤和线性过滤,以生成他们各自对应的纹理颜色, 最后对这两种插值文件之间再次进行颜色的插值计算,这种技术称为mipmap的线性过滤
9.5.3、各向异性过滤
各向异性过滤器可以缓解当多边形法向量和摄像机观察向量的夹角过大时导致的失真,但是这种过滤器的开销是最大的,同时它所带来的效果也是最好的。(原理不进行介绍)
9.6、寻址模式
我们可以将经过常数插值或者线性插值处理的纹理定义为一个函数T,即给定一个uv纹理坐标,经过上述的纹理函数T之后将会返回颜色(r,g,b,a);Direct3D允许我们采用4种不同的方式(即寻址模式)来扩充函数T的定义域,分别为重复寻址模式、边框颜色寻址模式、钳位寻址模式、镜像寻址模式。
寻址模式 | 实现方法 |
---|---|
重复寻址模式 | 通过在坐标的每个整数点处重复绘制图像来扩充纹理函数 |
边框颜色寻址模式 | 通过将每个不在[0,1]范围内的坐标(u,v)映射为程序员指定的颜色来扩充纹理函数 |
钳位寻址模式 | 通过将[0,1]范围外的每个坐标(u,v)映射为颜色T(u0,v0)来扩充纹理函数,其中(u0,v0)为范围[0,1]内距离(u,v)最近的点 |
镜像寻址模式 | 通过在坐标的每一个整数点处绘制图像的镜像来扩充纹理函数 |
在程序中,我们一定要指定一种寻址模式(默认为重复寻址模式),所以[0,1]范围外的纹理坐标必定有定义。而重复寻址模式是最常用的寻址模式,它可以让我们将纹理反复平铺到某一表面。
在Direct3D中,寻址模式由枚举类型D3D12_TEXTURE_ADDRESS_MODE来表示
typedef enum D3D12_TEXTURE_ADDRESS_MODE
{
//重复寻址模式
D3D12_TEXTURE_ADDRESS_MODE_WRAP = 1,
//镜像寻址模式
D3D12_TEXTURE_ADDRESS_MODE_MIRROR = 2,
//钳位寻址模式
D3D12_TEXTURE_ADDRESS_MODE_CLAMP = 3,
//边框颜色寻址模式
D3D12_TEXTURE_ADDRESS_MODE_BORDER = 4,
D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE = 5
}D3D12_TEXTURE_ADDRESS_MODE;
9.7、采样器对象
在运用纹理的过程中,除了纹理数据本身之外,还有另外两个相关的重要概念,即纹理过滤和寻址模式。采集纹理资源的时候所使用的过滤器和寻址模式都是由采样器对象来定义的,一个应用程序一般会有多个采用器对象以不同的方式来采集纹理资源
9.7.1、创建采样器
采样器将会被着色器所使用,所以我们需要给采样器对象绑定一个描述符,这样采样器才可以绑定到着色器上供着色器使用。
下面的代码展示了一个根签名示例,该根签名的第二个槽位获取了一个描述符表,在这个表中有1个和采样器寄存器槽0相互绑定的采样器描述符
CD3DX12_DESCRIPTOR_RANGE descRange[3];
descRange[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
descRange[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0);
descRange[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
//创建根参数
CD3DX12_ROOT_PARAMETER rootParameters[3];
rootParameters[0].InitAsDescriptorTable(1, &descRange[0], D3D12_SHADER_VISIBILITY_PIXEL);
rootParameters[1].InitAsDescriptorTable(1, &descRange[1], D3D12_SHADER_VISIBILITY_PIXEL);
rootParameters[2].InitAsDescriptorTable(1, &descRange[2], D3D12_SHADER_VISIBILITY_ALL);
//将根参数绑定到根签名上
CD3DX12_ROOT_SIGNATURE_DESC descRootSignature;
descRootSignature.Init(1, rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
如果要设置采样器描述符,我们需要先创建一个采样器堆,因此我们要填写D3D12_DESCRIPTOR_HEAP_DESC结构体实例一个堆并将堆的类型指定为D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLE;
//填写描述符堆结构体
D3D12_DESCRIPTOR_HEAP_DESC descHeapSampler = {};
descHeapSampler.NumDescriptors = 1;
descHeapSampler.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
descHeapSampler.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
//创建采样器堆
ComPtr<ID3D12DescriptorHeap> mSamplerDescriptorHeap;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&descHeapSampler,
__uuidof(ID3D12DescriptorHeap), (void**)&mSamplerDescriptorHeap));
有了采样器堆之后,我们便可以创建采样器描述符了。
typedef struct D3D12_SAMPLE_DESC
{
//指定采集纹理时所使用的过滤方式
D3D12_FILTER Filter;
//纹理在水平u轴方向上使用的寻址模式
D3D12_TEXTURE_ADDRESS_MODE AddressU;
//纹理在垂直v轴方向上使用的寻址模式
D3D12_TEXTURE_ADDRESS_MODE AddressV;
//纹理在深度w轴方向上使用的寻址模式
D3D12_TEXTURE_ADDRESS_MODE AddressW;
//设置mipmap层级的偏置值
FLOAT MipLODBias;
//最大各向异性值(此参数的区间为[1,16],只有Filter为各项异性该参数才会生效)
UINT MaxAnisottropy;
//用于实现像阴影贴图这类特殊应用的高级选项
D3D12_COMPARISON_FUNC ComparisonFunc;
//用于指定边框颜色寻址模式的填充颜色(只有寻址模式为边框寻址模式时该参数才会生效)
FLOAT BorderColor[4];
//可供选择的最小miamap层级
FLOAT MinLOD;
//可供选择的最大mipmap层级
FLOAT MaxLOD;
}D3D12_SAMPLER_DESC;
以下实例将会展示如何在描述符堆中为采样器创建出对应的描述符,该采样器使用线性过滤和重复寻址模式,其他参数保持默认值。
//填写采样器描述符结构体
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
//为采样器创建对应的描述符
md3dDevice->CreateSampler(&samplerDesc, mSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
//将采样器描述符绑定到预定的根签名参数槽
commandList->SetGraphicsRootDescriptorTable{1,mSamplerDescriptorHeap-
>GetGPUDescriptorHandleForHeapStart()};
9.7.2、静态采样器
一般来说,图形应用程序不会使用太多的采样器,所以Direct3D有一种特殊的方法来定义采样器数组,使用户可以在不创建采样器堆的情况下也能对采样器进行配置。我们通过结构体D3D12_STATIC_SAMPLER_DESC来描述静态采样器,D3D12_STATIC_SAMPLER_DESC和D3D12_SAMPLER_DESC比较相似,但是存在一点区别:
- 边框颜色存在一些限制,即静态采样器的边框颜色必须是D3D12_STATIC_BOROER_COLOR的成员之一:
- 含有额外的字段来指定着色器寄存器、寄存器空间以及着色器的可见性。
- 用户只能定义2032个静态采样器
enum D3D12_STATIC_BOROER_COLOR{
D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK = 0,
D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK = ( D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK + 1 ) ,
D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE = ( D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK + 1 )
}D3D12_STATIC_BOROER_COLOR;
在本章的演示程序中,我们会使用静态采样器,尽管我们不会使用到所有定义的静态采样器,但是我们会保留他们的定义,因为这样在需要使用的时候比较方便。
9.8、在着色器中对纹理进行采样
通过下列的HLSL语法来定义纹理对象,并将其分配给特定的纹理寄存器
//纹理寄存器由tn标定
Texture2D gDiffuseMap : register(t0);
因为根签名的定义指定了槽位参数和着色器寄存器的映射关系,这便是SRV可以绑定到着色器中特定的Texture2D对象的原因
类似的,下列HLSL语法定义了多个采样器对象,并将其分配到特定的采样器寄存器中
//采样器寄存器由sn标定
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
现在我们在像素着色器中为每一个像素指定其对应的纹理坐标(u,v),并通过Texture2D::Sample方法进行正式采样
//纹理寄存器由tn标定
Texture2D gDiffuseMap : register(t0);
//采样器寄存器由sn标定
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 TexC : TEXCOORD;
};
float4 PS(vertexOut pin) : SV_Target
{
//第一个参数为采样器对象类型,第二个参数为纹理坐标(u,v)
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC*gDiffuseAlbedo);
……
}
我们通过向Sample方法的第一个参数传递SamplerState对象来描述如何对纹理进行采样,然后给第二个参数传递像素的纹理坐标(u,v)。这个方法将利用SamplerState对象所指定的过滤方法,返回纹理图在坐标点(u,v)处的插值颜色。
DirectX12 3D 游戏开发与实战第九章内容(下)的更多相关文章
- DirectX12 3D 游戏开发与实战第九章内容(上)
仅供个人学习使用,请勿转载. 9.纹理贴图 学习目标: 学习如何将局部纹理映射到网格三角形上 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次纹理贴图 ...
- DirectX12 3D 游戏开发与实战第一章内容
DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...
- DirectX12 3D 游戏开发与实战第二章内容
矩阵代数 学习目标 理解矩阵及其相关运算的定义 探究为何能把向量和矩阵的乘法视为一种线性组合 学习单位矩阵.转置矩阵.行列式以及矩阵的逆等概念 逐步熟悉DirectXMath库中提供的关于矩阵计算的类 ...
- DirectX12 3D 游戏开发与实战第八章内容(下)
DirectX12 3D 游戏开发与实战第八章内容(下) 8.9.材质的实现 下面是材质结构体的部分代码: // 简单的结构体来表示我们所演示的材料 struct Material { // 材质唯一 ...
- DirectX12 3D 游戏开发与实战第八章内容(上)
8.光照 学习目标 对光照和材质的交互有基本的了解 了解局部光照和全局光照的区别 探究如何用数学来描述位于物体表面上某一点的"朝向",以此来确定入射光照射到表面的角度 学习如何正确 ...
- DirectX12 3D 游戏开发与实战第十章内容(下)
仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...
- DirectX12 3D 游戏开发与实战第十章内容(上)
仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...
- DirectX12 3D 游戏开发与实战第五章内容
渲染流水线 学习目标: 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素 探索如何用Direct3D表示3D对象 学习如何建立虚拟摄像机 理解渲染流水线,根据给定的3D场景的几何描 ...
- DirectX12 3D 游戏开发与实战第四章内容(上)
Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...
随机推荐
- 项目实战:Qt文件改名工具 v1.2.0(支持递归检索,搜索:模糊匹配,前缀匹配,后缀匹配;重命名:模糊替换,前缀追加,后缀追加)
需求 在整理文件和一些其他头文件的时候,需要对其名称进行整理和修改,此工具很早就应该写了,创业后,非常忙,今天抽空写了一个顺便提供给学习. 工具和源码下载地址 本篇文章的应用包和源码包可在 ...
- 【UE4 C++】调用外部链接库 lib静态库
简述 本例以插件形式测试 使用Lib引用,打包程序运行不用再拷贝lib文件 需要 lib 文件和 .h 头文件 lib部分的代码 .h 头文件 #pragma once #ifndef __MYTES ...
- Github Actions 实践
Github Actions 实践 Github Actions 是 Github 的持续集成服务,通过在 repo 发生特定的行为时执行指定的命令实现自动测试.自动部署等功能. 基本术语 workf ...
- MyBatis源码分析(八):设计模式
Mybatis中用到的设计模式 1. 建造者(Builder)模式: 表示一个类的构建与类的表示分离,使得同样的构建过程可以创建不同的表示.建造者模式是一步一步创建一个复杂的对象,他只允许用户只通过指 ...
- P2472 [SCOI2007]蜥蜴(最大流)
P2472 [SCOI2007]蜥蜴 自己第一道独立做题且一遍AC的网络流题纪念... 看到这道题我就想到网络流建图的方式了... 首先根据每个高度,我们将每个点拆成两个点限流.之后根据跳的最大距离, ...
- hdu 2860 Regroup(并查集)
题意: AP x yA recruit with ability rate x were asked to join company y. (0<=x<2^31, 0<=y<n ...
- hdu 5171 GTY's birthday gift(数学,矩阵快速幂)
题意: 开始时集合中有n个数. 现在要进行k次操作. 每次操作:从集合中挑最大的两个数a,b进行相加,得到的数添加进集合中. 以此反复k次. 问最后集合中所有数的和是多少. (2≤n≤100000,1 ...
- for循环中创建线程执行问题
先执行以一个简单的示例: static void Main(string[] args) { List<int> taskConsumes = new List<int>() ...
- 【java+selenium3】模拟键盘操作 (十二)
一.键盘操作 用代码来模拟键盘的Enter或一系列的组合键,前面使用sendkeys()方法模拟键盘的输入,除此之外还可以模拟键盘组合键输入如下: 整理一些比较常用的键盘操作如下: sendKeys( ...
- List of devices attached 没有手机设备号 解决办法
问题: cmd下使用adb devices 没有找到手机设备,如下图 解决办法: 采用360手机助手给我们自动安装对应的手机驱动,或者去对应的手机官网下载对应的驱动,这里手机要开启调试模式 我是opp ...