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图像的存储方式,页面翻 ...
随机推荐
- Gopher们写if err != nil是否腻了?
效果 go里面没有try catch,比较类似的有panic() 和 recover()机制,但是代价太大了,他们的场景更多使用在"程序异常,无法继续往下执行了这种场景",比如配置 ...
- 6月2日 Scrum Meeting
日期:2021年6月2日 会议主要内容概述: 取消账单类别自定义 图表属性分析取消函数输入 增加新的主题模板 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇 ...
- 【好好编程-技术博客】微信小程序开发中前后端的交互
微信小程序开发中前后端的交互 微信小程序的开发有点类似与普通网页的开发,但是也不尽然相同.小程序的主要开发语言是JavaScript,开发同普通的网页开发有很大的相似性,对于前端开发者而言,从网页开发 ...
- 轻量级 Java 基础开发框架,Solon & Solon Cloud 1.5.52 发布
Solon 已有120个生态扩展插件,此次更新主要为细节打磨: 插件 mybatis-solon-plugin 增加 mappers 单行配置支持 之前的多行模式: mybatis.db1: type ...
- Charles的简单用法
Charles的简单用法 一.抓电脑上 http 包 二.显示请求的 Request 和 Response 三.抓取电脑上 https 包 1.安装根证书 2.在钥匙串中启用根证书 3.配置哪些需要抓 ...
- Noip模拟81 2021.10.20
T1 语言 比较简单的题,然后就瞎写了,所以考场上就我一个写了线段树的,所以我的常数.... 所以就枚举动词的位置,找前面后面有没有出现$4$即可 1 #include<bits/stdc++. ...
- [调试笔记] 10.8模拟赛11 T4 甜圈
这题正解线段树维护哈希,同机房神犇已经讲的很明白了.这里只说sbwzx在调试的时候犯的sb错误. 1.关于pushdown和update 众所周知,sbwzx一写带lazy的线段树,就必在pushdo ...
- 零基础学习C语言入门必备知识
今天跟大家一起从零学C语言: 1. C语言简介 1.1 C语言发展史 C语言是一种广泛使用的面向过程的计算机程序设计语言,既适合于系统程序设计,又适合于应用程序设计.C语言的发展历程大致如图1-1所示 ...
- SpringBoot整合Easyexcel操作Excel,闲暇之余,让我们学习更多
关于封面:晚饭后回自习室的路上 Easyexcel 官方文档 Easyexcel | github 前言 最近也是在写的一个小练习中,需要用到这个.趁着这次就将写个整合的Demo给大家. 希望能够让大 ...
- 数组中出现次数超过一半的数字 牛客网 剑指Offer
数组中出现次数超过一半的数字 牛客网 剑指Offer 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字 ...