图形学理论 纹理贴图

什么是纹理?

​ wikipedia上做了如下解释"The spatial arrangement of color or intensities in an image.The texture means smoothness, roughness, or bumpiness of the surface of an object"。也就是说,纹理是图像空间中颜色值的排列,且纹理表示着物体表面的平滑、粗糙。但纹理的用途不仅于此,它还可以存储向量,如法线贴图

​ 在DirectX中,纹理以ID3D12Resource来表示,包含1D纹理、2D纹理、3D纹理。纹理与缓冲区不同,因为缓冲区仅用于存储数据数组,但其可以存储任意类型的资源;而纹理却可以含有多个mipmap层级,GPU可以基于这个层级来进行一些特殊操作,如多重采样。支持这些操作的的纹理资源都会被限定为特定的数据格式

什么是纹理元素

​ 纹理中的单个颜色值称为纹理元素或纹素,每个纹理元素在纹理中都有一个唯一地址(u,v)

像素格式

​ 纹理所支持的像素数据格式以枚举类型DXGI_FORMAT表示。部分格式如下:

  1. DXGI_FORMAT_R32G32B32_FLOAT:每个元素由3个32位浮点数分量组成
  2. DXGI_FORMAT_R16G16B16A16_UNORM:4个16位无符号分量
  3. DXGI_FORMAT_R32G32_UINT:2个32位无符号整数分量
  4. DXGI_FORMAT_R8G8B8A8_SNORM:4个8位无符号分量
  5. DXGI_FORMAT_R8G8B8A8_SINT:4个8位有符号整数分量
  6. DXGI_FORMAT_R8G8B8A8_TYPELESS:这是一种无类型格式,仅仅用它来预留一块内存,待将纹理绑定至管线时会指出如何重新解释其中的数据

​ 像素格式不是只用于存储颜色信息,还可以存储其他的.如DXGI_FORMAT_R32G32B32_FLOAT,它可以存储一个3维向量浮点坐标

什么是纹理贴图?

​ 纹理贴图是用图像、函数或其他数据源来改变物体表面外观的技术。如,将一副图像应用在一个多边形,现在来观察这个多边形,便能看见这副图像出现在多边形表面

为什么需要纹理贴图?

​ 可以增加画面真实感和复杂性

​ 可以在建模、存储空间、速度方面节省很多资源

纹理空间和参数空间坐标

​ 纹理空间是一个新的单独的坐标系,用于表示纹理中的颜色值的坐标.在纹理空间之前我们回得到参数空间坐标,这个坐标范围是[0,0]到[1,1],这是归一化坐标区间,通过这种方式可以消除纹理大小的干扰。如,无论纹理多大,纹理坐标(0.5,0.5)总是表示纹理正中心的纹素illustration of a texel address as column and row numbers

纹理管线

​ 纹理贴图可以用一组管线来描述——纹理管线image-20230207143350094

​ 如下对上图进行概述:

  1. 通过投影方程空间中的点转换为一组参数空间值(也就是u,v坐标)
  2. 使用映射函数将参数空间值转换到纹理空间
  3. 使用纹理空间值从纹理中获取相应的
  4. 使用值变换函数对纹理值进行值变换,最后使用得到的新值改变表面属性,如材质、着色法线等

​ 来看个例子,下图描述了一个多边形在给定一张纹理在其表面生成样本时发生了哪些过程image-20230207144537367

  1. 首先,找到物体空间中的位置(x,y,z)
  2. 然后,使用投影函数转换为二元向量(u,v)(一般来说转换为二元向量)。再上图中也就是(0.32,0.29)
  3. 假设图像分辨率是256*256,便用256分别乘上(u,v)上两个维度的坐标并去除小数点得到纹理坐标(81,74)
  4. 现在便可以通过纹理坐标,在纹理贴图上查找到对应的颜色值,也就是(0.9,0.8,0.7)
  5. 如果原始的立方体表面太暗,我们可以使用值变换函数,对每个向量乘以1.1,将其变亮
  6. 最后,将最后得出的值用于着色方程,作为物体的颜色值,来替换之前的颜色值

投影函数

​ 函数处于纹理管线的第一步,其功能是将空间中的三维坐标点转化为纹理坐标

​ 一般来说投影函数通常用于美术建模并将投影结果存储在顶点数据中.但有些情况除外,如在顶点/像素着色器中使用投影函数实现各种效果,包括动画、渲染方法(如环境贴图)

​ 常见投影的要点:

  1. 球形投影:将点投射到一个中心位于某个点的虚拟球体上
  2. 圆柱投影:圆柱投影计算的是纹理坐标 u,而计算得到的另一个纹理坐标 v 是沿该圆柱轴线的距离。对具有自然轴的物体比较适用,如旋转表面。若表面与圆柱体轴线接近垂直时,就会出现变形
  3. 平面投影:沿着一个方向进行投影,并将纹理应用到物体的所有表面上

映射函数

​ 映射函数的功能是将参数空间坐标转换为纹理空间位置

​ 图像会出现在物体表面的(u,v)位置,且uv值处于[0,1],超出这个范围的纹理,它的显示方式由映射函数决定。在Direct3D中,这类函数叫做寻址模式,常见的映射函数如下:

  1. 重复寻址模式(warp):在坐标的每个整数点处重复绘制图像

    ​ 假设创建平方基元并指定纹理坐标为 (0.0,0.0)、(0.0,3.0)、(3.0,3.0) 、 (3.0,0.0),应用重复寻址模式会在 u、v 方向中应用三次纹理。效果如下图所示image-20230207165939424

  2. 边框颜色寻址模式(border color):将每个在[0,1]外的坐标都映射为指定的颜色

    ​ 下图中,应用程序指定将纹理应用为红色image-20230207171219255

  3. 钳位寻址模式(clamp):在[0.0,1.0]之间把纹理复制一遍,对于[0.0,1.0]之外的内容,将边缘的内容沿着 u 轴和 v 轴进行延伸

    ​ 假设创建平方基元并指定纹理坐标为 (0.0,0.0)、(0.0,3.0)、(3.0,3.0) 、 (3.0,0.0),应用钳位寻址模式会使得 列和行的像素颜色将分别延伸到基元的顶部和右侧image-20230207170820722

  4. 镜像寻址模式(mirror):图像在物体表面上不断重复,但每次重复时对图像进行镜像或者反转

    ​ 假设创建平方基元并指定纹理坐标为 (0.0,0.0)、(0.0,3.0)、(3.0,3.0) 、 (3.0,0.0),应用镜像寻址模式纹理会使得其他行、其他列都是前一行或前一列的镜像image-20230207170451009

如何将纹理映射至三角形

image-20230210153309243

对于上图,我们设三角形的三个顶点为\(p_0, p_1, p_2\),这三个顶点对应的纹理坐标分别是\(q_0, q_1, q_2\)。通过插值来求位于三角形内的任意一点p位于纹理空间的坐标q,即有\(p = p_0 + s(p_1 - p_0) + t(p_2 - p_0),当s \geq 0, t \geq 0,s + t \leq 1时,即有\space q = q_0 + s(q_1 - q_0) + t(q_2 - q_0)\)

​ 因此,我们需要对顶点结构体和输入顶点布局进行修改

struct Vertex
{
    XMFLOAT3 position;
    XMFLOAT2 uv;
};
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};

纹理数据源

什么是纹理数据源?

​ 贴图师一般会借助一些图像编辑器如Photoshop来制作纹理,最后再将这些纹理保存为某种格式的图像文件如PNG等,随后程序便会在加载时将图像文件载入ID3D12Resource对象

应当选择什么格式?

​ 图像文件格式有许多,但对于实时来说,DDS格式是不错的选择。因为它不仅可以支持GPU可原生处理,它还可以支持一些GPU自身即可解压的压缩图像格式

DDS格式

​ DDS是一种专门为GPU设计的图像格式。因为它可以支持如下:

  1. mipmap
  2. GPU能自行解压的压缩格式
  3. 纹理数组
  4. 立方体贴图
  5. 体纹理

​ DDS可以支持像素格式,但不是所有格式都适用于DDS纹理。如,非压缩图像数据一般采用以下格式:

  1. DXGI_FORMAT_B8G8R8A8_UNORM 或 DXGI_FORMAT_B8G8R8X8_UNORM:适用于低动态范围图像
  2. DXGI_FORMAT_R16G16B16A16_FLOAT:适用于高动态范围图像
  • 注意:
    • VS支持打开DDS,我们可以在VS编辑器中查看mipmap层级、修改DDS格式及查看颜色通道等信息

纹理压缩

  • 为何需要进行纹理压缩?

​ 因为纹理都是放于GPU显存中,因此随着场景不断复杂,会占用更多的显存。为了解决这一问题,便需要对纹理进行压缩

  • Direct3D支持的纹理压缩格式

    1. BC1 (DXGI_FORMAT_BC1_UNORM):当需要将图片压缩成支持3个颜色通道和一位aplha分量的格式
    2. BC2 (DXGI_FORMAT_BC2_UNORM):当需要将图片压缩成支持3个颜色通道和4位aplha分量的格式
    3. BC3 (DXGI_FORMAT_BC3_UNORM):当需要将图片压缩成支持3个颜色通道和8位aplha分量的格式
    4. BC4 (DXGI_FORMAT_BC4_UNORM):当需要将图片压缩成支持1个颜色通道的格式
    5. BC5 (DXGI_FORMAT_BC5_UNORM):当需要将图片压缩成支持2个颜色通道的格式
    6. BC6 (DXGI_FORMAT_BC6_UF16):当需要将图片压缩成HDR图像数据格式
    7. BC7 (DXGI_FORMAT_BC7_UNORM):对于RGBA数据进行高质量压缩,且可极大减少因压缩法线图而造成的误差
  • 注意

    • 经压缩后的纹理只可用于输入到管线的着色器阶段,不可用作渲染目标

创建及启动纹理

加载DDS

​ 微软官方提供了一组用于加载DDS文件的源代码

HRESULT DirectX::CreateDDSTextureFromFile12(
    _In_ ID3D12Device* device,		//指向创建纹理资源的设备
	_In_ ID3D12GraphicsCommandList* cmdList,	//指向提交GPU命令的命令列表
	_In_z_ const wchar_t* szFileName,	//欲加载的图像文件名
	_Out_ ComPtr<ID3D12Resource>& texture,		//返回载有图像数据的纹理资源
	_Out_ ComPtr<ID3D12Resource>& textureUploadHeap,	//返回装有纹理的上传堆
	_In_ size_t maxsize,	
	_Out_opt_ DDS_ALPHA_MODE* alphaMode
   )

​ 纹理结构体按如下编写,调用DirectX::CreateDDSTextureFromFile12()只需从纹理结构体中取相应对象即可

struct Texture
{
    std::string Name;
    std::wstring FileName;
    Microsoft::WRL::ComPtr<ID3D12Resource> Resource = nullptr;
    Microsoft::WRL::ComPtr<ID3D12Resource> UploadHeap = nullptr;
}

着色器资源描述符堆和描述符,绑定至管线

​ 为了将纹理资源绑定至管线,还是老流程——创建描述符->创建堆->设置根签名->绑定管线

创建着色器资源视图

​ 填写D3D12_SHADER_RESOURCE_VIEW_DESC对象来描述SRV(着色器资源视图)描述符

typedef struct D3D12_SHADER_RESOURCE_VIEW_DESC {
    DXGI_FORMAT         Format;		//像素格式
    D3D12_SRV_DIMENSION ViewDimension;	//资源的维度
    UINT                Shader4ComponentMapping;	//着色器堆纹理进行采样时,它返回纹理坐标处的纹理数据向量。可以理由这个参数堆返回的纹理向量的分量进行排序,若需要默认顺序,则指定为D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
    union {
        D3D12_BUFFER_SRV                            Buffer;
        D3D12_TEX1D_SRV                             Texture1D;
        D3D12_TEX1D_ARRAY_SRV                       Texture1DArray;
        D3D12_TEX2D_SRV                             Texture2D;
        D3D12_TEX2D_ARRAY_SRV                       Texture2DArray;
        D3D12_TEX2DMS_SRV                           Texture2DMS;
        D3D12_TEX2DMS_ARRAY_SRV                     Texture2DMSArray;
        D3D12_TEX3D_SRV                             Texture3D;
        D3D12_TEXCUBE_SRV                           TextureCube;
        D3D12_TEXCUBE_ARRAY_SRV                     TextureCubeArray;
        D3D12_RAYTRACING_ACCELERATION_STRUCTURE_SRV RaytracingAccelerationStructure;
    };
} D3D12_SHADER_RESOURCE_VIEW_DESC;

typedef struct D3D12_TEX2D_SRV {
    UINT  MostDetailedMip;	//此描述符中图像最大的mipmap的层级的索引,用于指定开始使用的纹理子资源。范围在[0,MipLevels - 1]
    UINT  MipLevels;	//从MostDetailedMip起,待创建视图的mipmap层级数量。如此可以指定此视图mipmap层级的连续子范围.设为-1表示从MostDetailedMip到最后一个mipmap层级间的所有层级
    UINT  PlaneSlice;	//平面切片的索引
    FLOAT ResourceMinLODClamp;	//可以访问的最小mipmap层级。设为0.0表示都可以访问
} D3D12_TEX2D_SRV;

void CreateShaderResourceView(
    [in, optional] ID3D12Resource                        *pResource,	//指定代表着色器资源的接口对象
    [in, optional] const D3D12_SHADER_RESOURCE_VIEW_DESC *pDesc,	//视图描述符地址
    [in]           D3D12_CPU_DESCRIPTOR_HANDLE           DestDescriptor	//目标描述符
);

创建着色器资源描述符堆

​ 用ID3D12Device::CreateDescriptorHeap()创建描述符堆,Type设为D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,Flags设为D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE

D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 3;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap( &srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));

Barycentric Coordinates重心坐标插值

绘制三角形与着色、zbuffer - 爱莉希雅 - 博客园 (cnblogs.com)

什么是重心坐标?

​ 如下图,给定一个三角形ABC,该平面内任意一点都可以用三个顶点的线性组合进行表示:\((x,y) = \alpha A + \beta B + \gamma C\)\(\alpha + \beta + \gamma = 1\)

img

​ 值得注意的是,当三个系数都为非负数时,这个点在三角形内部

为什么需要插值?

​ 因为很多操作都是在顶点上进行,而我们希望在三角形内部进行平滑过渡

插值对象是什么?

​ 插值对象有纹理坐标、颜色、法向量等等

如何求重心坐标?

​ 有两种方式:

  1. 利用面积确定系数image-20230209160210284

  2. 利用坐标确定系数image-20230209160410596

过滤器

纹理过小

纹理过小引起的问题

​ 假设显示器分辨率为4k,而图片纹理的分辨率只有720p,如果这个时候进行纹理映射,会导致多个像素映射到同一纹理元素上.比如下图中,红色点是屏幕中一像素进行映射后在纹理中的坐标,现在它会去选择离他最近的点
image-20230209160942214

如何解决纹理过小?

​ 前面我们提到插值可以用于平滑三角形内部,在这里我们可以取目标点周围临近的四个点,并对其进行双线性插值
image-20230209161247578

步骤如下:

  1. 取得目标点周围临近的四个点,分别算出该红色点在水平竖直方向上的比率s,t
  2. 取任意一个维度水平/竖直,在任意维度上进行两次插值。也就是\(u_{01}\)\(u_{11}\)间,\(u{00}\)\(u{10}\)
    image-20230209161646732
  3. 最后在求得的两个点间再进行一次插值
    image-20230209161950740

以下是效果图
image-20230209162151382

纹理过大

纹理过大引起的问题

​ 现在有一张地板,在上面铺满重复的贴图,我们期望的结果应是下图左边的样式,但实际计算出来的是下图右边的样式。可以看到进出抗锯齿严重,而远程产生了摩尔纹

​ 为什么会产生这一现象呢?这是因为一个像素映射在多个纹理元素上,由于透视在远处一个像素拥有的纹理元素实际就很少,从而导致采样频率不足无法还原信号原样,产生抗锯齿和摩尔纹
image-20230209162450087

image-20230209164241222

如何解决?

​ 几种方法如下

  1. 超采样

    ​ 正如上面所说,摩尔纹的原因是因为一个像素点实际拥有的纹理元素很少,而超采样可以细分为多个小的采样点,如此一来即可解决问题

    缺陷:超采样计算量太大
    image-20230209163810112

  2. mipmap(多级渐远纹理)

    ​ 既然采样会出问题,那么我们可以不采样而是求像素对应的footprint里所有纹理元素颜色的平均值,这也就是范围查询

    • 什么是点查询?

      ​ 给定一个点,求得一个点的值

    • 什么是范围查询?

      ​ 给定一个区域,求得区域的平均值

    ​ mipmap样式如下,有不同等级的范围查询。随着等级的提升,每提升一级将四个相邻像素点求平均合为一个像素点
    image-20230209164604975image-20230209165045190

    随后,只需根据像素的footprint大小来选定不同等级的纹理,再进行点查询

    • 如何确定等级?

      ​ 利用相邻像素点估算footprint大小来确定等级
      image-20230209165326941
      image-20230209165230755

      步骤如下:

      1. 在屏幕空间中取得当前像素点上下左右做个方向临近的任意两个点(四个点也行),分别查询这个三个点在纹理空间中的坐标
      2. 计算当前像素点与临近两个像素点在纹理空间中的距离,取这两个值中的最大值
      3. \(level = log_2 L\)
    • 如果求得mipmap的小数层(level)?

      ​ 解决之道:三线性插值

      假设我们要查询n.2层,只需在第n层进行双线性插值,再在第n+1层进行双线性插值,然后再第n层和第n+1层间进行插值,答案即为所求

      缺陷:mipmap会模糊掉远处的细节,因为它只能查询方块区域,且插值是近似的手法
      image-20230209171655310

各向异性过滤

什么是各向异性?

​ 意思是在不同的方向它的表现各不相同。如下图所示
image-20230209172403166

为什么需要各向异性过滤?

​ 因为它可以解决mipmap的缺陷。因为mipmap默认是正方形区域,所以有些情况下并不是如此。下图中,这些footprint不是正方形
image-20230209171827895

各项异性过滤如何工作的?

​ 各向异性过滤允许我们对长条的区域进行范围查询,但是对于斜着的区域是不可以的

​ 各向异性的几X意味压缩几倍,也就是从左上角向右下角多几层

缺陷

​ 开销大。生成各向异性过滤的图的开销是原来的三倍

采样器

什么是采样器?

​ 定义采集纹理资源时所用的过滤器和寻址模式,由着色器使用

为什么需要采样器?

​ 通常一个app需要使用多个采样器对象以不同方式采集纹理

创建采样器

创建采样器堆

​ 堆的Type填为D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;

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> m_samplerDescriptorHeap;
ThrowIfFailed(mDevice->CreateDescriptorHeap(
    &descHeapSampler,
    (void**)&m_samplerDescriptorHeap
));

创建采样器描述符

typedef struct D3D12_SAMPLER_DESC {
  D3D12_FILTER               Filter;	//当对纹理进行采样时,指定使用的采样方式
  D3D12_TEXTURE_ADDRESS_MODE AddressU;	//纹理在水平u轴上的寻址模式
  D3D12_TEXTURE_ADDRESS_MODE AddressV;	//纹理在水平v轴上的寻址模式
  D3D12_TEXTURE_ADDRESS_MODE AddressW;	//纹理在深度w轴上的寻址模式
  FLOAT                      MipLODBias;//mipmap层级的offset。如,若此值设为2,mipmap设为3,纹理将按照mipmap第五层进行采样
  UINT                       MaxAnisotropy;	//最大各向异性过滤值,范围在[1,16]。Filter设为D3D12_FILTER_ANISOTROPIC or D3D12_FILTER_COMPARISON_ANISOTROPIC此项才能生效
  D3D12_COMPARISON_FUNC      ComparisonFunc;	//将采样数据与现有采样数据进行比较。用于实现阴影贴图等,暂设 D3D12_COMPARISON_FUNC_ALWAYS
  FLOAT                      BorderColor[4];	//指定在D3D12_TEXTURE_ADDRESS_MODE_BORDER寻址模式下的边框颜色
  FLOAT                      MinLOD;	//mipmap最小层级
  FLOAT                      MaxLOD;	//mipmap最大层级
} D3D12_SAMPLER_DESC;
  1. D3D12_FILTERD3D12_FILTER (d3d12.h) - Win32 apps | Microsoft Learn常用的如下

    1. D3D12_FILTER_MIN_MAG_MIP_POINT

      对纹理图与mipmap层级进行点采样

    2. D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT

      对纹理图进行双线性插值,对mipmap层级进行点采样

    3. D3D12_FILTER_MIN_MAG_MIP_LINEAR

      对纹理图进行双线性插值,对mipmap进行三线性插值

    4. D3D12_FILTER_ANISOTROPIC

      对纹理的放大、缩小、mipmap均采用各向异性过滤

  2. BorderColor[4]。这个就是之前讲过的映射函数

    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
    } ;
    
  3. ComparisonFuncD3D12_COMPARISON_FUNC (d3d12.h) - Win32 apps | Microsoft Learn

示例

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.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;

m_device->CreateSampler(
    &samplerDesc,
	m_samplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart()
);

将采样器绑定至着色器

CD3DX12_DESCRIPTOR_RANGE descRange[1];
descRange[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,1, 0);

CD3DX12_ROOT_PARAMETER rootParameters[1];
rootParameters[0].InitAsDescriptorTable(1,&descRange[1], D3D12_SHADER_VISIBILITY_PIXEL);

CD3DX12_ROOT_SIGNATURE_DESC descRootSignature;
descRootSignature.Init(
    3,
    rootParameters, 
    0, 
    nullptr,
    D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
);

将采样器描述符与管线绑定

m_commandList->SetGraphicsRootDescriptorTable(
    1,
    m_samplerDescriptorHeap->GetGPUDescriptorHandleForHeapStart()
);

静态采样器

什么是静态采样器?

​ 一种特殊的方式用于定义采样器数组,使得用户即使不创建采样器堆也能对其进行配置

为什么需要静态采样器?

​ 因为一般来说app不会使用过多的采样器,这样我们便没有必要再创建采样器堆

创建静态采样器

创建静态采样器描述符

typedef struct D3D12_STATIC_SAMPLER_DESC {
  D3D12_FILTER               Filter;
  D3D12_TEXTURE_ADDRESS_MODE AddressU;
  D3D12_TEXTURE_ADDRESS_MODE AddressV;
  D3D12_TEXTURE_ADDRESS_MODE AddressW;
  FLOAT                      MipLODBias;
  UINT                       MaxAnisotropy;
  D3D12_COMPARISON_FUNC      ComparisonFunc;
  D3D12_STATIC_BORDER_COLOR  BorderColor;
  FLOAT                      MinLOD;
  FLOAT                      MaxLOD;
  UINT                       ShaderRegister;
  UINT                       RegisterSpace;
  D3D12_SHADER_VISIBILITY    ShaderVisibility;
} D3D12_STATIC_SAMPLER_DESC;
  1. 静态采样器的BorderColor必须为下列成员之一

    typedef enum D3D12_STATIC_BORDER_COLOR {
      D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK = 0,	//黑色,且完全透明
      D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,	//黑色,完全不透明
      D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE	//白色,完全不透明
    } ;
    
  2. 着色器寄存器、寄存器空间、着色器可见性,这些都是配置采样器堆的参数

​ 示例

CD3DX12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);

D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));

注意

  1. 用户只能定义2032个静态采样器,若需求量大于这个数只有建立采样器堆

在着色器中对纹理采样

定义纹理对象

​ 纹理寄存器由tn指定,n为寄存器的槽号

//定义纹理对象,并分配给指定纹理寄存器
Texture2D gDiffuseMap : register(t0);

定义采样器对象

​ 采样寄存器由sn指定,n为寄存器的槽号

SamplerState gsamPointWrap : register(s0);

对纹理进行采样

struct PSInput
{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
};

Texture2D g_texture : register(t0);
SamplerState g_sampler : register(s0);

PSInput VSMain(float4 position : POSITION, float4 uv : TEXCOORD)
{
    PSInput result;

    result.position = position;
    result.uv = uv;

    return result;
}

float4 PSMain(PSInput input) : SV_TARGET
{
    return g_texture.Sample(g_sampler, input.uv);
}

其中,Sample用于采样纹理

<Template Type> Object.Sample( sampler_state S, float Location [, int Offset] );
  1. S.定义的采样器对象
  2. Location.纹理坐标
  3. Offset.纹理坐标偏移量,可用于任何纹理对象类型,offset需要定义为static

未完待续

​ 在下一篇hello triangle中将会展示纹理贴图的流程

reference

chenglixue/Real-Time-Rendering-4th-Bibliography-Collection: Real-Time Rendering 4th (RTR4) 参考文献合集典藏 | Collection of Bibliography / Reference (github.com)

Texture - Wikipedia (jinzhao.wiki)

GAMES101:现代计算机图形学入门 – 计算机图形学与混合现实在线平台 (games-cn.org)

posted @ 2023-02-10 15:46  爱莉希雅  阅读(379)  评论(0编辑  收藏  举报