D3D12渲染技术之纹理案例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/82937390

我们现在回顾一下将纹理添加到箱子模型上面,就跟以前的博客中提到的那样。下面我们详细介绍如何实现?

指定纹理坐标

GeometryGenerator :: CreateBox生成箱子的纹理坐标,以便将整个纹理图像映射到箱子的每个面上。 为简洁起见,我们仅显示正面,背面和顶面的顶点定义。 另请注意,我们省略了顶点构造函数中法线和切线向量的坐标(纹理坐标以粗体显示)。

GeometryGenerator::MeshData GeometryGenerator::CreateBox(
  float width, float height, float depth, 
  uint32 numSubdivisions)
{
  MeshData meshData;
   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, 1.0f);
  v[1] = Vertex(-w2, +h2, -d2, …, 0.0f, 0.0f);
  v[2] = Vertex(+w2, +h2, -d2, …, 1.0f, 0.0f);
  v[3] = Vertex(+w2, -h2, -d2, …, 1.0f, 1.0f);
 
  // Fill in the back face vertex data.
  v[4] = Vertex(-w2, -h2, +d2, …, 1.0f, 1.0f);
  v[5] = Vertex(+w2, -h2, +d2, …, 0.0f, 1.0f);
  v[6] = Vertex(+w2, +h2, +d2, …, 0.0f, 0.0f);
  v[7] = Vertex(-w2, +h2, +d2, …, 1.0f, 0.0f);
  // Fill in the top face vertex data.
  v[8] = Vertex(-w2, +h2, -d2, …, 0.0f, 1.0f);
  v[9] = Vertex(-w2, +h2, +d2, …, 0.0f, 0.0f);
  v[10] = Vertex(+w2, +h2, +d2, …, 1.0f, 0.0f);
  v[11] = Vertex(+w2, +h2, -d2, …, 1.0f, 1.0f);

创建纹理

我们在初始化时从文件创建纹理,如下所示:

// Helper structure to group data related to the texture.
struct Texture
{
  // Unique material name for lookup.
  std::string Name;
 
  std::wstring Filename;
 
  Microsoft::WRL::ComPtr<ID3D12Resource> Resource = nullptr;
  Microsoft::WRL::ComPtr<ID3D12Resource> UploadHeap = nullptr;
  };
 
std::unordered_map<std::string, std::unique_ptr<Texture>> mTextures;
 
void CrateApp::LoadTextures()
{
  auto woodCrateTex = std::make_unique<Texture>();
  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);
}

我们将所有独特纹理存储在无序地图中,以便我们可以按名称查找它们。 在代码中,在加载纹理之前,需要检查纹理数据是否已经加载(即,它是否已经包含在无序映射中),以便它不会多次加载。

设置纹理

一旦创建了纹理并在描述符堆中为它创建了SRV,将纹理绑定到管道以便可以在着色器程序中使用它只需将其设置为需要纹理的根签名参数:

// Get SRV to texture we want to bind.
CD3DX12_GPU_DESCRIPTOR_HANDLE tex(
mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
tex.Offset(ri->Mat->DiffuseSrvHeapIndex, mCbvSrvDescriptorSize);
 
…
 
// Bind to root parameter 0. The root parameter description specifies which 
// shader register slot this corresponds to.
cmdList->SetGraphicsRootDescriptorTable(0, tex);

纹理转换

我们没有讨论过的两个常量缓冲区变量是gTexTransform和gMatTransform。 这些变量在顶点着色器中用于变换输入纹理坐标:

在这里插入代码片// Output vertex attributes for interpolation across triangle.
float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform);
vout.TexC = mul(texC, gMatTransform).xy;

纹理坐标表示纹理平面中的2D点, 因此,我们可以像任何其他点一样平移,旋转和缩放它们, 以下是转换纹理的一些示例用法:
1、砖纹理沿墙壁被拉伸, 墙顶点当前具有范围[0,1]中的纹理坐标。 我们将纹理坐标放大4倍,以将它们放大到范围[0,4],这样纹理将在墙上重复四到四次。
2、天空飘动的云层, 通过将纹理坐标转换为时间的函数,云在天空上就可以飘动了。
3、纹理旋转有时对粒子效果非常有用,例如,我们会随着时间的推移旋转火球纹理。

在本篇博客提供的案例中,我们使用单位矩阵变换,以便输入纹理坐标保持不变,但在下篇博客中,我们将解释使用纹理变换的演示。

请注意,要将2D纹理坐标转换为4×4矩阵,我们将其扩展为4D矢量:

vin.TexC ---> float4(vin.Tex, 0.0f, 1.0f)

在乘法完成之后,通过丢弃z分量和w分量将得到的4D矢量转回到2D矢量。

vout.TexC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform).xy;

我们使用两个单独的纹理变换矩阵gTexTransform和gMatTransform,因为有时它对材质转换纹理(对于像水这样的动画材料)更有意义,但有时它使纹理变换更有意义成为对象的属性。
因为我们正在处理2D纹理坐标,所以我们只关心对前两个坐标进行的转换。 例如,如果纹理矩阵转换了z坐标,则它对结果纹理坐标没有影响。最后给大家看一下实现的Demo:
在这里插入图片描述

Demo下载地址:链接:https://pan.baidu.com/s/1X0Vikf6qGYGPKU-Nwf-wYA 密码:h79q

阅读更多

扫码向博主提问

海洋_

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 3D引擎架构
  • 服务器架构
  • GPU渲染
  • 客户端架构
  • 引擎优化
去开通我的Chat快问
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页