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

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. python png图片生成gif

    有时候写代码就是这样别人把代码写好你在后面加一个句号就行了 我很懒不想写成函数,你自己来吧.有注释就不错了 这个依赖一个图像处理库pillow,轮子就是轮他不是车 import imageio imp ...

  2. Linux C语言多线程编程实例解析

    Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...

  3. Linux零基础之shell基础编程入门

    从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...

  4. Linux内核漏洞精准检测如何做?SCA工具不能只在软件层面

    摘要:二进制SCA工具要想更好的辅助安全人员实现安全审计.降低漏洞检测的误报率,必须向更细颗粒度的检测维度发展,而不仅仅停留在开源软件的层面,同时对漏洞库的要求也需要向细颗粒度的精准信息提出的挑战. ...

  5. DDD领域驱动设计架构模式:防腐层(Anti-corruption layer)

    在微服务(Microservices)架构实践中,架构设计借用了DDD中的一些概念和技术,比如一个微服务对应DDD中的一个限界上下文(Bounded Context):在微服务设计中应该首先识别出DD ...

  6. Nginx多种安装方式

    不指定参数配置的Nginx编译安装 ./configuremake make install wget下载或浏览器下载上传.解压进入目录[root@mcw1 nginx-1.10.2]# ls #查看 ...

  7. K8S在线部署含Dashborad

    参考文章 https://www.kubernetes.org.cn/5462.html 前言 Kubernetes作为容器编排工具,简化容器管理,提升工作效率而颇受青睐.很多新手部署Kubernet ...

  8. mongodb入门命令-创建表数据(二)

    1.mongodb入门命令 1.1 show databases; 或 show dbs; //查看当前的数据库 > show dbs; admin 0.000GB config 0.000GB ...

  9. Java测试开发--Spring Tools Suite (STS) 简介(一)

    sts是一个定制版的Eclipse,专为Spring开发定制的,方便创建调试运行维护Spring应用. 官网下载之后,可以看到一个sts-bundle,里面有三个文件夹,一个法律信息,一个tc Ser ...

  10. No versions available for io.grpc:grpc-core:jar:[1.13.1] within specified range

    No versions available for i{0}:[1.13.1] within specified range maven打包的时候报错是由于同一个jar包有多个版本导致的版本冲突 解决 ...