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

9、纹理贴图

学习目标

  1. 学习如何将局部纹理映射到网格三角形中
  2. 探究如何创建和启用纹理
  3. 学会如何通过纹理过滤来创建更加平滑的图像
  4. 探索如何使用寻址模式来进行多次贴图
  5. 探究如何将多个纹理进行组合,从而创建出新的纹理和特效
  6. 学习如何通过纹理动画来创建一些基本效果

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比较相似,但是存在一点区别:

  1. 边框颜色存在一些限制,即静态采样器的边框颜色必须是D3D12_STATIC_BOROER_COLOR的成员之一:
  2. 含有额外的字段来指定着色器寄存器、寄存器空间以及着色器的可见性。
  3. 用户只能定义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 游戏开发与实战第九章内容(下)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. (课内)信安数基RSA-level3-5

    emmmm感觉其实自己对这个的理解完全不够,原理只能写出这么个东西(悲) 代码完全是 攻击方式中(1)(2)内容的实现. lambda是一种可以理解为匿名函数的写法:写在这里看起来很酷炫(bushi) ...

  2. 【数据结构与算法Python版学习笔记】基本数据结构——列表 List,链表实现

    无序表链表 定义 一种数据项按照相对位置存放的数据集 抽象数据类型无序列表 UnorderedList 方法 list() 创建一个新的空列表.它不需要参数,而返回一个空列表. add(item) 将 ...

  3. MySQL复习(二)MySQL基本数据类型

    MySQL基本数据类型 常用的字段类型大致可以分为数值类型.字符串类型.日期时间类型三大类 1. 数值类型 数值类型可以分为整型.浮点型.定点型三小类. 1.1 整型 (tiny:极小的, small ...

  4. 封装一个简单的ajax请求

    记录自己第一次封装ajax,肯定有很多考虑不周到,如有错误请指出,本人必将虚心改正. /** * * @param {Object} obj =>header:请求头:url:请求地址:meth ...

  5. [对对子队]会议记录4.10(Scrum Meeting 1)

    本次每日例会的开会时间是4月10日晚上20:00,使用腾讯会议作为开会工具. 今天已完成的工作 何瑞 ​ 工作内容:制作UI界面的指令编辑系统,已大致实现指令的衔接 ​ 相关issue:实现用户指令编 ...

  6. OO第三次博客作业--第三单元总结

    一.JML 语言的理论基础及应用工具链 JML 是一种行为接口规格语言,提供了对方法和类型的规格定义手段.通过 JML 和其支持工具,不仅可以基于规格自动构造测试用例,并整合了 SMT Solver ...

  7. 高斯消元de小板几

    感觉就是模拟解方程,还比手动解方程笨一些.... 但是大数据的话,他毕竟比我解得快多了.... 1 inline int Gauss(int n){ 2 int cnt=1;//真实到达的行列式行数 ...

  8. 零基础学习C语言字符串操作总结大全

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  9. Maven还停留在导jar包?快来探索Nexus私服的新世界

    写在前面 Maven,学习框架之前我们都会接触到的一个工具,感觉他的定位,似乎就跟git一样,只是方便我们开发?于是自然而然的,很多小猿对于Maven都只是停留在会用的阶段,利用他来构建,打包,引入j ...

  10. Go语言核心36讲(Go语言进阶技术十)--学习笔记

    16 | go语句及其执行规则(上) 我们已经知道,通道(也就是 channel)类型的值,可以被用来以通讯的方式共享数据.更具体地说,它一般被用来在不同的 goroutine 之间传递数据.那么 g ...