仅个人学习用,请勿转载
漫反射反照率纹理图:diffuse albedo texture map 过滤器:fliter
纹理放大:magnification 常数插值:constant interpolation
线性插值:linear interpolation 最近邻点采样:nearest neighbor point sampling
纹理缩小:minification 三线性过滤:trilinear flitering
HRESULT DirectX::CreateDDSTextureFromFile(
_In_ ID3D12Device* device,
_In_ ID3D12GraphicsCommandList* cmdList,
_In_z_ const wchar_t* szFileName,
_Out_Microsoft::WRL::ComPtr& texture,
_Out_Microsoft::WRL::ComPtr& textureUploadHeap);
为用名为WoodCreate01.dds的图像来创建一个对应的纹理,应按照如下方式编写代码:
struct Texture
{
std::string Name;
std::wstring Filename;
Microsoft::WRL::ComPtr Resource = nullptr;
Microsoft::WRL::ComPtr UploadHeap =nullptr;
}
auto woodCreateTex = std::make_unique();
woodCreateTex->Name="woodCreateTex";
woodCreateTex->FileName=L"Textures/WoodCreate01.dds";
ThrowIfFaild(DirectX::CreateDDSTextureFromFile12(
md3dDevice.Get(),mCommandList.Get(),
woodCreateTex->Filename.c_str(),
woodCreateTex->Resource,woodCreateTex->UploadHeap));
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)));
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个纹理资源
//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)
//从纹理中提取此像素的漫反射反照率
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)削减其中的红色与绿色成分来达到目的。
struct Material
{
...
//漫反射纹理在SRV堆中的索引
int DiffuseSrvHeapIndex = -1;
...
}
void CrateApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector& ritems)
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
UINT matCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(MaterialConstants));
auto objectCB = mCurrFrameResource->ObjectCB->Resource();
auto matCB = mCurrFrameResource->MaterialCB->Resource();
// For each render item...
//对于每个渲染项
for(size_t i = 0; i < ritems.size(); ++i)
{
auto ri = ritems[i];
cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
CD3DX12_GPU_DESCRIPTOR_HANDLE tex(mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
tex.Offset(ri->Mat->DiffuseSrvHeapIndex, mCbvSrvDescriptorSize);
D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress() + ri->ObjCBIndex*objCBByteSize;
D3D12_GPU_VIRTUAL_ADDRESS matCBAddress = matCB->GetGPUVirtualAddress() + ri->Mat->MatCBIndex*matCBByteSize;
cmdList->SetGraphicsRootDescriptorTable(0, tex);
cmdList->SetGraphicsRootConstantBufferView(1, objCBAddress);
cmdList->SetGraphicsRootConstantBufferView(3, matCBAddress);
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
}
}
可将经过插值的纹理定义为一个返回向量值的函数T(u,v)=(r,g,b,a),。Direct3D允许我们采用下列4中不同方式(寻址模式: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_TEXUTRE_ADDRESS_MODO_MIRROR_ONCE =5;
}D3D12_TEXTURE_ADDRESS_MODE;
采集纹理资源时所用的过滤器和寻址模式都是采样器对象定义的,一个应用程序通常要通过若干个采样器对象以不同的方式来采集纹理
为了将采样器绑定到着色器工期使用,就要为采样器对象绑定描述符
CD3DX12_DESCRIPTOR_RANGE descRange[3];
descRange[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV,1,0);
descRange[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER);
descRange[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV,1,0);
CD3DX12_ROOT_PARAMATER rootParameters[3];
rootParameter[0].InitAsDescriptorTable(1,&descRange[0],
D3D12_SHADER_VISIBILITY_PIXEL);
rootParameter[1].InitAsDescriptorTable(1,&descRange[1],
D3D12_SHADER_VISIBILITY_PIXEL);
rootParameter[2].InitAsDescriptorTable(1,&descRange[2],
D3D12_SHADER_VISIBILITY_ALL);
CD3DX12_ROOT_SIGNATURE_DESC descRootSignature;
descRootSignature.Init(3,rootParameters,0,nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
设置采样器描述符还需要一个采样器堆。创建采样器堆则需要填写D3D12_DESCRIPTOR_HEAP_DESC结构体实例并将堆类型指定为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_VISIABLE;
ComPtr mSamplerDescriptorHeap;
ThrowIfFaild(mDevice->CreateDescriptorHeap(descHeapSampler,
_uuidof(ID3D12DescriptorHeap),
(void**)&mSamplerDescriptorHeap));
有了采样器堆之后就能创建采样器描述符了,此时,我们再通过填写D3D12_SAMPLER_DESC对象来指定寻址模式、过滤器类型以及其它一些参数。
typedef struct D3D12_SAMPLER_DESC
{
D3D12_FLITER Fliter;
D3D12_TEXTURE_ADDRESS_MODE AddressU;
D3D12_TEXTURE_ADDRESS_MODE AddressV;
D3D12_TEXTURE_ADDRESS_MODE_AddressW;
FLOAT MipLODBias;
UINT MaxAnistropy;
D3D12_COMPARISON_FUNC ComparisonFunc;
FLOAT BorderColor[4];
FLOAT MinLOD;
FLOAT MaxLOD;
}D3D12_SAMPLER_DESC;
1.Flitter:D3D12_FLITTER 枚举类型的成员之一,用来指定采样纹理时所用的过滤方式
2.AddressU:纹理在水平U轴上所使用的寻址方式
3.AddressV:纹理在垂直V轴上采用的寻址方式
4.AddressW:纹理在深度W轴方向上所用的寻址方式(仅限3D纹理)
5.MipLODBias:设置mipmap层级的偏置值。如果将此值指定为0.0,则表示mipmap层级保持不变,如果将这个值设为2,mipmap层级设为3,则将按层级3+2进行采样。
6.MaxAnisotropy:最大各向异性值,此参数的取值区间为[1,16]。只有将Fliter设置为D3D12_FLITER_ANISOTROPIC或D3D12_FLITER_COMPARISON_ANISOTROPIC之后该项才能生效,此数值越大开销越大,但是用户会获得更好的渲染效果。
7.ComparisonFunction:用于实现引用贴图这样异类特殊应用的高级选项,目前只设置为D3D12_COMPARISON_FUNC_ALWAYS。
8.BorderColor:用于指定在D3D12_TEXTURE_ADDRESS_MODE_BOREDER 寻址模式下的边框颜色。
9.MinLOD:可供选择的最小mipmap层级。
10.MaxLOD:可供选择的最大mipmap层级。
常用选项:
1. D3D12_FLITER_MIN_MAG_MIP_POINT:对纹理贴图进行点过滤
2. D3D12_FLITER_MIN_MAG_LINEAR_MIP_POINT:对纹理贴图进行线性过滤,但对mipmap层级进行点过滤。
3. D3D12_FILTER_MIN_MAG_MIP_LINEAR:对纹理图进行双线性过滤,还要在两个临近的较高较低mipmap之间进行线性过滤,因此也称为三线性过滤(trillinear flitering)
4. D3D12_FLITER_ANISOTROPIC:对纹理的放大缩小以及mipmap均采用各向异性过滤。
可以根据这些示例推测出其它可能的组合选项,也可以通过查阅SDK文档来了解D3D12_FLITER的其它枚举类型
创建描述符示例:
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Flitter = D3D12_FLITTER_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;
samplerDescComparisomFunc = D3D12_COMPARISON_FUN_ALWAYS;
md3dDevive->CreateSampler(&samplerDesc,
mSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
将采样器描述符绑定到预定的根签名参数槽:
commandList->SetGraphicsRootDescriptorTable(1,
mSamplerDescriptorHeap->GetGPUDescriptorHandleForHapStart());
enum D3D12_STATIC_BORDER_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_BORDER_COLOR;
std::array GetStaticSamplers();
std::array TexColumnsApp::GetStaticSamplers()
{
// Applications usually only need a handful of samplers. So just define them all up front
// and keep them available as part of the root signature.
//一般只会用到这些采样器中的一部分,所以就将它们全部提前定义好,并作为根签名的一部分保留下来。
const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
0, // shaderRegister 着色器寄存器
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter过滤器类型
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU U轴上的寻址模式
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV V轴上的寻址模式
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW W轴上的寻址模式
const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
1, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
2, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
3, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(
4, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressW
0.0f, // mipLODBias mipmap层级的偏置值
8); // maxAnisotropy 最大各向异性值
const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
5, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressW
0.0f, // mipLODBias mipmap层级偏置值
8); // maxAnisotropy 最大各向异性值
return {
pointWrap, pointClamp,
linearWrap, linearClamp,
anisotropicWrap, anisotropicClamp };
}
void TexColumnsApp::BuildRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE texTable;
texTable.Init(
D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
1, // number of descriptors 描述符数量
0); // register t0 着色器寄存器
// Root parameter can be a table, root descriptor or root constants.
//根参数可以是描述符表、根描述符或者根常量
CD3DX12_ROOT_PARAMETER slotRootParameter[4];
// Perfomance TIP: Order from most frequent to least frequent.
//优化建议:从最频繁到最不频繁的顺序创建
slotRootParameter[0].InitAsDescriptorTable(1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParameter[1].InitAsConstantBufferView(0); // register b0
slotRootParameter[2].InitAsConstantBufferView(1); // register b1
slotRootParameter[3].InitAsConstantBufferView(2); // register b2
auto staticSamplers = GetStaticSamplers();
// A root signature is an array of root parameters.
//跟签名即是一系列根参数
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
(UINT)staticSamplers.size(), staticSamplers.data(),
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
//创建一个具有4个槽位的根签名,第一个指向含有单个着色器资源的描述符表,另外三个各指向一个常量缓冲区视图。
ComPtr serializedRootSig = nullptr;
ComPtr errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
通过下列HLSL语法来定义纹理对象,并将其分配给特定的纹理寄存器:
texture2D gDiffuseMap : register(t0);
纹理寄存器由tn来标定。其中整数n表示的是纹理寄存器的槽号,跟签名支出了由槽位参数到着色器寄存器的映射关系,这便是应用程序代码能将SRV绑定到着色器中特定Texture2D对象的原因。
下列HLSL语法定义了多个采样器对象,并将它们分配到特定的采样器寄存器
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamperState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
这些采样器对应于上一节中所配置的静态采样器数组。采样器寄存器由sn来指定,其中n表示的是采样器寄存器的槽号。
现在我们在像素着色器中为每个像素都指定其相应的纹理坐标(u,v),并通过Texture2D::Sample方法进行纹理采样。
Texture2D gDiffuseMap : register(t0);
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
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
...
}
通过向Sample方法的第一个参数传递SamplerState对象来描述如何将纹理数据进项采样,再向第二个参数传递像素的纹理坐标为(u,v)。 这个方法会利用SamplerState对象指定的过滤方法,返回纹理图在点(u,v)处的插值颜色。
GeometryGenerator::CreateBox函数用于生成立方体的纹理坐标,因此得以使纹理图像被映射到立方体的每个表面。
GeometryGenerator::MeshData GeometryGenerator::CreateBox(float width, float height, float depth, uint32 numSubdivisions)
{
MeshData meshData;
//
// Create the vertices.
//
Vertex v[24];
float w2 = 0.5f*width;
float h2 = 0.5f*height;
float d2 = 0.5f*depth;
// Fill in the front face vertex data.
//填写立方体前表面的顶点数据
v[0] = Vertex(-w2, -h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[1] = Vertex(-w2, +h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[2] = Vertex(+w2, +h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
v[3] = Vertex(+w2, -h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
// Fill in the back face vertex data.
//填写立方体后表面的顶点数据
v[4] = Vertex(-w2, -h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[5] = Vertex(+w2, -h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[6] = Vertex(+w2, +h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[7] = Vertex(-w2, +h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
// Fill in the top face vertex data.
//填写立方体顶面的顶点数据
v[8] = Vertex(-w2, +h2, -d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[9] = Vertex(-w2, +h2, +d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[10] = Vertex(+w2, +h2, +d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
v[11] = Vertex(+w2, +h2, -d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
// Fill in the bottom face vertex data.
//填写立方体底面的顶点数据
v[12] = Vertex(-w2, -h2, -d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[13] = Vertex(+w2, -h2, -d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[14] = Vertex(+w2, -h2, +d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[15] = Vertex(-w2, -h2, +d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
// Fill in the left face vertex data.
//填写左面的顶点数据
v[16] = Vertex(-w2, -h2, +d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[17] = Vertex(-w2, +h2, +d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[18] = Vertex(-w2, +h2, -d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[19] = Vertex(-w2, -h2, -d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
// Fill in the right face vertex data.
//填写右面的顶点数据
v[20] = Vertex(+w2, -h2, -d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
v[21] = Vertex(+w2, +h2, -d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
v[22] = Vertex(+w2, +h2, +d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
v[23] = Vertex(+w2, -h2, +d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
meshData.Vertices.assign(&v[0], &v[24]);
...
}
struct Texture
{
// Unique material name for lookup.
//便于查找材质所使用的唯一名称
std::string Name;
std::wstring Filename;
Microsoft::WRL::ComPtr Resource = nullptr;
Microsoft::WRL::ComPtr UploadHeap = nullptr;
};
std::unordered_map> mTextures;
void CrateApp::LoadTextures()
{
auto woodCrateTex = std::make_unique();
woodCrateTex->Name = "woodCrateTex";
woodCrateTex->Filename = L"../../Textures/WoodCrate01.dds";
ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),
mCommandList.Get(), woodCrateTex->Filename.c_str(),
woodCrateTex->Resource, woodCrateTex->UploadHeap));
mTextures[woodCrateTex->Name] = std::move(woodCrateTex);
}
将每个彼此独立的纹理都存于一个无序映射表(unordered map)中,再根据它们各自的名称来查找相应的纹理。在产品代码中,应该在纹理加载前检测它的数据是否已经存在,以防止同一个纹理被多次加载的情况。
如果纹理已经被创建,并且它的SRV也存在于描述符堆中,就只需要把所需要的纹理设置到根签名参数以将其绑定到 渲染流水线。以供着色器使用。
//获取欲绑定纹理的SRV
CD3DX12_ROOT_DESCRIPTOR_HANDLE tex(
mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
tex.Offset(ri->Mat->DiffuseSrvHeapIndex,mCbvSrvDescritorSize);
...
//将纹理绑定到根参数0。根参数指示了纹理将具体绑定到哪一个着色器寄存器槽
cmdList->SetGraphicsRootDescriptorTable(0,Tex);
// Defaults for number of lights.
//默认光源数量
#ifndef NUM_DIR_LIGHTS
#define NUM_DIR_LIGHTS 3
#endif
#ifndef NUM_POINT_LIGHTS
#define NUM_POINT_LIGHTS 0
#endif
#ifndef NUM_SPOT_LIGHTS
#define NUM_SPOT_LIGHTS 0
#endif
// Include structures and functions for lighting.
//包含光照的结构体和函数
#include "LightingUtil.hlsl"
Texture2D gDiffuseMap : register(t0);
SamplerState gsamLinear : register(s0);
// Constant data that varies per frame.
//每一帧变化的常量数据
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
float4x4 gTexTransform;//纹理的变换矩阵
};
// Constant data that varies per material.
//绘制过程中需要的杂项常量数据(关于每个材质都需要的常量数据)
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
float4 gAmbientLight;
// Indices [0, NUM_DIR_LIGHTS) are directional lights;
// indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
// indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
// are spot lights for a maximum of MaxLights per object.
//对于每个以MaxLights为光源数量最大值的对象,索引[0,NUM_DIR_LIGHTS]表示的是方向光,[NUM_DIR_LIGHTS,NUM_DIR_LIGHTS+NUM_POINT_LIGHTS]表示点光源
//[NUM_DIR_LIGHTS+NUM_POINT_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)表示聚光灯光源
Light gLights[MaxLights];
};
//每种材质会有所区别的常量数据
cbuffer cbMaterial : register(b2)
{
float4 gDiffuseAlbedo;
float3 gFresnelR0;
float gRoughness;
float4x4 gMatTransform;//新增字段,变换矩阵
};
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 TexC : TEXCOORD;//纹理坐标,新增字段
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 TexC : TEXCOORD;//纹理坐标,新增字段
};
VertexOut VS(VertexIn vin)
{
VertexOut vout = (VertexOut)0.0f;
// Transform to world space.
//把坐标变换到世界空间
float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
vout.PosW = posW.xyz;
// Assumes nonuniform scaling; otherwise, need to use inverse-transpose of world matrix.
//假设这里进行的是等比缩放,否则需要使用世界矩阵的逆转置矩阵
vout.NormalW = mul(vin.NormalL, (float3x3)gWorld);
// Transform to homogeneous clip space.
//将顶点变换到齐次裁剪空间
vout.PosH = mul(posW, gViewProj);
// Output vertex attributes for interpolation across triangle.
//为对三角形进行插值操作而输出的顶点属性(纹理)
float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform);
vout.TexC = mul(texC, gMatTransform).xy;
return vout;
}
float4 PS(VertexOut pin) : SV_Target
{
//为纹理设置漫反射值
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamLinear, pin.TexC) * gDiffuseAlbedo;
// Interpolating normal can unnormalize it, so renormalize it.
//对法线插值可能使之非规范化,因此进行规范化处理
pin.NormalW = normalize(pin.NormalW);
// Vector from point being lit to eye.
//光线经表面上一点反射到观察点这一方向上的向量
float3 toEyeW = normalize(gEyePosW - pin.PosW);
// Light terms.
//光照项
float4 ambient = gAmbientLight*diffuseAlbedo;
const float shininess = 1.0f - gRoughness;
Material mat = { diffuseAlbedo, gFresnelR0, shininess };
float3 shadowFactor = 1.0f;
float4 directLight = ComputeLighting(gLights, mat, pin.PosW,
pin.NormalW, toEyeW, shadowFactor);
float4 litColor = ambient + directLight;
// Common convention to take alpha from diffuse material.
//从漫反射反照率获取alpha值的常见手段
litColor.a = diffuseAlbedo.a;
return litColor;
}
关于gTexTransform与gMatTransform。
float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform);
vout.TexC = mul(texC, gMatTransform).xy;
纹理坐标表示的事纹理平面中的2D点,有了这种坐标,就可以对纹理中的样点进行缩放、平移和旋转,例如:
1. 令砖块纹理随着一堵墙的模型拉伸,假设此墙顶点的当前纹理坐标范围为[0,1],在将该纹理放大4倍使它们变换到[0,4]时,该纹理会沿着墙面重复贴图44次。
2. 假设有许多云朵纹理绵延在蓝天背景之下,那么随着时间的函数来评议这些文理坐标,便能实现动态的白云浮过天空的效果。
3. 可以用于实现一些粒子效果,比如随着时间的推移让纹理进行旋转。
在crate演示程序中采用的是单位矩阵进行变换,并未对输入的纹理坐标进行任何修改。
变换2D纹理坐标需要44矩阵,因此要先将它扩充为一个4D向量:
vin.TexC—>float4(vin.TexC,0.0f,1.0f);
在完成乘法运算后再将所得的4D向量中的z分量和w分量去掉,就可以使之强制转换回2D向量。
float4 TeXC = mul(float4(vin.TexC,0.0f,1.0f),gTexTransform);
vout.TexC=mul(TexC,gMatTransform).xy;
gTexTransform和gMatTransform一种是关于材质的纹理变换,一种是关于物体属性的纹理变换。
纹理矩阵中平移了z坐标不会对纹理造成影响。