仅个人学习用,请勿转载
词汇
漫反射反照率纹理图:diffuse albedo texture map 过滤器:fliter
纹理放大:magnification 常数插值:constant interpolation
线性插值:linear interpolation 最近邻点采样:nearest neighbor point sampling
纹理缩小:minification 三线性过滤:trilinear flitering
内容
9.4.1加载DDS文件
- 微软提供了一组加载DDS文件的轻量级源代码,但是在这本书撰写的时候,改功能仅支持到DX11,因此书中的配套源代码专门提供了可以读取DDS文件且支持DX12的方法,在common文件夹中DDSTextureLoader:
HRESULT DirectX::CreateDDSTextureFromFile(
_In_ ID3D12Device* device,
_In_ ID3D12GraphicsCommandList* cmdList,
_In_z_ const wchar_t* szFileName,
_Out_Microsoft::WRL::ComPtr<ID3D12Resource>& texture,
_Out_Microsoft::WRL::ComPtr<ID3D12Resource>& textureUploadHeap);
- device:用于创建纹理资源的D3D设备的指针
- cmdList:提交GPU命令(如将纹理数据从上传堆复制到默认堆的命令)的命令列表
- szFileName:预加载的图像文件名
- texture:返回载有图像数据的纹理资源
- textureUploadHeap:返回的纹理资源,在此将它当作一个上传堆用于将图像数据复制到默认堆中的纹理资源。在GPU完成命令之前,不能销毁该资源
为用名为WoodCreate01.dds的图像来创建一个对应的纹理,应按照如下方式编写代码:
struct Texture
{
std::string Name;
std::wstring Filename;
Microsoft::WRL::ComPtr<ID3D12Resource> Resource = nullptr;
Microsoft::WRL::ComPtr<ID3D12Resource> UploadHeap =nullptr;
}
auto woodCreateTex = std::make_unique<Texture>();
woodCreateTex->Name="woodCreateTex";
woodCreateTex->FileName=L"Textures/WoodCreate01.dds";
ThrowIfFaild(DirectX::CreateDDSTextureFromFile12(
md3dDevice.Get(),mCommandList.Get(),
woodCreateTex->Filename.c_str(),
woodCreateTex->Resource,woodCreateTex->UploadHeap));
9.4.2着色器资源视图堆
- 创建纹理资源后,还要再为它创建一个SRV(着色器资源视图)描述符,并将其设置到一个根签名参数槽(root signature parameter slot)以供着色器使用,先用ID3D12Device::CreateDescriptorHeap创建描述符堆,借此存储SRV描述符,以下代买创建了可以容纳3个类型为CBV、SRV或UAV描述符的描述符堆,并使之在着色器中可见。
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 3;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIABLE;
ThrowIfFaild(md3dDevice->CreateDescriptorHeap(
&srvHeapDesc,IID_PPV_ARGS(&mSrvDescriptorHeap)));
9.4.3创建着色器资源视图描述符
- 创建了SRV堆后便可创建真正的描述符,通过填写D3D12_SHADER_RESOURCE_VIEW_DESC对象来描述SRV描述符,该结构体详述了资源的类型以及其它的信息,如格式、维数、mipmap数量等
typedef struct D3D12_SHADER_RESOURCE_VIEW_DESC
{
DXGI_FORMAT Format;
D3D12_SRV_DIMENSION ViewDimension;
UINT Shader4ComponentMapping;
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_SHADER_RESOURCE_VIEW_DESC;
typedef struct D3D12_TEX2D_SRV
{
UINT MostDetailedMip;
UINT MipLevels;
UINT PlaneSlice;
FLOAT ResourceMinLODClamp;
}D3D12_TEX2D_SRV;
对于2D纹理只需关心联合体中D3D12_TEX2D_SRV部分。
1. Format:视图格式。如果待创建的视图有具体格式,就用此资源的DXGI_FORMAT格式来填写此参数,如果是通过无类型格式来创建的该资源,则必须在此为识图填写具体类型。
2. ViewDimension:资源的维数。目前只使用2D纹理,所以该参数指定为D3D12_SRV_DIMENSION_TEXTURE2D。以下是几种常见维数:
(a)D3D12_SRV_DIMENSION_TEXTURE1D
(b)D3D12_SRV_DIMENSION_TEXTURE3D
(c)D3D12_SRV_DIMENSION_TEXTURECUBE
3. Shader4ComponentMapping:在着色器中对纹理进行采样时,它会返回特定纹理坐标处的纹理数据向量。这个字段提供了一种方法:可以将采样时返回的纹理向量中的分量进行重新排序,常用于一些特殊场合,目前不涉及这些情景,所以将它指定为D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING即可。
4. MostDetailedMip:指定此视图中图像细节最详尽的mipmap层级索引,此取值范围在0到MipLevels-1之间
5. MipLevels:此视图的mipmap层级数量,以MostDetailedMip作为起始值。通过这个字段和MostDetailedMip结合起来,就能够指定此视图mipmap层级的一段连续范子范围。可以设置为-1来标识自MostDetailedMip开始到最后一个mipmap层级之间的所有mipmap级别。
6. PlaneSlice:平面切片的索引
7. ResourceMinLODClamp:指定可以访问的最小mipmap层级,设置为0.0即为可以访问所有mipmap层级,如果指定3.0,则为3.0到MIPCount-1的mipmap层级。
- 构建3个资源描述符来填充上一小节中创建的描述符堆
//假设已创建下列3个纹理资源
//ID3D12Resource* bricktex;
//ID3D12Resource* stoneTex;
//ID3D12Resource* titleTex;
//获取指向描述符堆起始处的指针
CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = brickTex->GetDesc().Format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTUER2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = bricksTex->GetDesc().MipLevels;
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
md3dDevice->CreateShaderResourceView(birckTex.Get(),&srvDesc,hDescriptor);
//偏移到堆中的下一个描述符
hDescriptor.Offset(1,mCbvSrvDescriptorSzie);
srvDesc.Format=stoneTex->GetDesc().Format;
srvDesc.Texture2D.MipLevels=stoneTex->GetDesc().MipLevels;
md3dDevice->CreateShaderResourceView(stoneTex.Get(),&srvDesc,hDescriptor);
//偏移到堆中的下一个描述符
hDEscriptor.Offset(1,mCbvSrvDescriptorSize);
srvDesc.Format=titleTex->GetDesc().Format;
srv.Desc.Texture2D.MipLevels = titleTex->GetDesc().MipLevels;
md3dDevice->CreateShaderResourceView(titleTex.Get(),&srvDesc,hDescriptor)
9.4.4 将纹理绑定到流水线
- 目前为止在每次绘制调用时所指定的材质都是由材质常量缓冲区进行更新的,这就意味着在绘制调用中所有几何体都使用同一种材质,这对程序来说是一种极大的限制,纹理映射技术就是用纹理贴图来取代材质常量缓冲区以获取材质数据,这将使每个像素的数据都是灵活可变化的,从而为场景增加更丰富的细节和真实感。
- 本节中将添加漫反射反照率纹理图以此来给出材质的漫反射反照率分量,影响材质的两个数值gFresnelR0与gRoughness仍在每次绘制调用时由材质常量缓冲区来指定。在使用纹理贴图时,仍需在材质缓冲区中保留gDiffuseAlbedo这个分量。我们在像素着色器中会以下列方式令纹理漫反射反照率与DiffuseAlbedo相组合
//从纹理中提取此像素的漫反射反照率
float4 texDiffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC);
//将纹理样本与常量缓冲区中的反照率相乘
float4 diffuseAlbedo = texDiffuseAlbedo*gDiffuseAlbedo;
我们通常设DiffuseAlbedo=(1,1,1,1)以使texDiffuseAlbedo不会发生改变,但是有时候对DiffuseAlbedo进行适当调整却可以避免制作新的纹理,假设有一个砖块纹理,贴图师只希望让它的色调略显偏蓝,就可以通过设置DiffuseAlbedo=(0.9,0.9,1,1)削减其中的红色与绿色成分来达到目的。
- 我们向材质添加了一个索引,借此引用了与此材质相关的纹理描述堆中的一个SRV:
struct Material