首页 > 其他分享 >DX12实例化

DX12实例化

时间:2023-03-19 22:15:18浏览次数:41  
标签:matData VertexOut DX12 实例 uint 缓冲区 绘制

前言

​ 本篇为DX12 实例化的学习笔记

什么是实例化,为什么需要它?

​ 想象一下,在绘制时,可能需要绘制同一物体但该物体的位置、朝向、大小、材质、纹理可能各不相同,按照常规的思路:我们需要保存对应物体的位置、朝向、大小、材质、纹理数据,若需要绘制的物体十分多,会造成draw call次数增大

​ 那么有没有方案来解决这一问题呢?你可能会想出这样的解决方法——将该物体存放在局部空间,当需要绘制它的不同状态时,再乘以世界矩阵。虽然此方法确实节省了不少空间和开销,但因绘制物体而调用API的开销还是不让人满意,毕竟需要物体的顶点数据需要乘以世界矩阵,材质同样需要。难道说还有其他的解决方案麻?没错确实有,这就是本章的主角实例化

​ 通过使用实例化技术,可以使得仅需一次绘制调用即可构造出该对象的多个实例。再者,若搭配动态索引,还能进一步优化其开销

其实在前面我们一直在绘制实例数据,只不过我们将绘制实例的数量设为1,也就是只绘制一个实例。若把此项设为10,也就是绘制10个相同的实例,但这并不能满足我们的需求,因此接下来需要思考如何让这些实例对象拥有它独特的示例数据

实例数据

  • 输入实例数据有两种方法
    1. 在IA阶段,用逐实例数据流代替逐顶点数据流,再将第二个顶点缓冲区和含有实例数据的输入流绑定:在创建输入布局时,将结构体D3D12_INPUT_ELEMENT_DESC中的InputSlotClass用枚举D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA代替D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA
    2. 为所有实例都创建一个存有对应实例数据的结构化缓冲区,也就是说将对象实例化n次,则创建一个含有100个实例数据元素的结构化缓冲区,再将结构化缓冲区资源绑定到渲染管线,并根据需要绘制的实例在VS中引用对应数据

现在存在一个问题——如何引用需要的实例?D3D提供系统值SV_InstanceID来解决这一问题。如,将第一个实例所用的顶点都统一为0,第二个实例所用的顶点都统一为1,以此类推

  • 实例

    • 创建实例缓冲区

      一个渲染项对应一个结构化缓冲区

      struct InstanceData
      {
      	DirectX::XMFLOAT4X4 World = MathHelper::Identity4x4();
      	DirectX::XMFLOAT4X4 TexTransform = MathHelper::Identity4x4();
      	UINT MaterialIndex;
      	UINT InstancePad0;
      	UINT InstancePad1;
      	UINT InstancePad2;
      };
      
      std::unique_ptr<UploadBuffer<InstanceData>> InstanceBuffer = nullptr;
      InstanceBuffer = std::make_unique<UploadBuffer<InstanceData>>(device, maxInstanceCount, false);	//上传堆
      
      struct RenderItem
      {
          //...
          std::vector<InstanceData> Instances;
          UINT InstanceCount = 0;
          //...
      }
      
      
    • shader

      物体常量缓冲区的工作将交给实例缓冲区.实例缓冲区将MaterialIndex传递给VertexOut,PS再从VertexOut中的MaterialIndex获取材质数据

      struct InstanceData
      {
      	float4x4 World;
      	float4x4 TexTransform;
      	uint     MaterialIndex;
      	uint     InstPad0;
      	uint     InstPad1;
      	uint     InstPad2;
      };
      
      struct MaterialData
      {
      	float4   DiffuseAlbedo;
          float3   FresnelR0;
          float    Roughness;
      	float4x4 MatTransform;
      	uint     DiffuseMapIndex;
      	uint     MatPad0;
      	uint     MatPad1;
      	uint     MatPad2;
      };
      
      //注意!这里类型不再是Texture2DArray,这里所用类型的特点是该数组存储的纹理尺寸和格式可以各不相同,更为灵活
      //纹理数组位于space0
      Texture2D gDiffuseMap[7] : register(t0);
      
      //该材质结构化缓冲区位于space1,使得纹理数组不会和该资源重叠
      StructuredBuffer<InstanceData> gInstanceData : register(t0, space1);
      StructuredBuffer<MaterialData> gMaterialData : register(t1, space1);
      
      struct VertexOut
      {
      	float4 PosH    : SV_POSITION;
          float3 PosW    : POSITION;
          float3 NormalW : NORMAL;
      	float2 TexC    : TEXCOORD;
      	
      	// nointerpolation表示该索引指向的是未插值的三角形
      	nointerpolation uint MatIndex  : MATINDEX;
      };
      
      VertexOut VS(VertexIn vin, uint instanceID : SV_InstanceID)
      {
      	VertexOut vout = (VertexOut)0.0f;
      	
      	// 获取实例数据
      	InstanceData instData = gInstanceData[instanceID];
      	float4x4 world = instData.World;
      	float4x4 texTransform = instData.TexTransform;
      	uint matIndex = instData.MaterialIndex;
      
      	vout.MatIndex = matIndex;
      	
      	// 获取材质数据
      	MaterialData matData = gMaterialData[matIndex];
      	//...
      }
      
      float4 PS(VertexOut pin) : SV_Target
      {
      	// 获取材质数据
      	MaterialData matData = gMaterialData[pin.MatIndex];
      	float4 diffuseAlbedo = matData.DiffuseAlbedo;
          float3 fresnelR0 = matData.FresnelR0;
          float  roughness = matData.Roughness;
      	uint diffuseTexIndex = matData.DiffuseMapIndex;
      	
      	// 动态索引。在数组中查找纹理
          diffuseAlbedo *= gDiffuseMap[diffuseTexIndex].Sample(gsamLinearWrap, pin.TexC);
      	//...
      }
      

reference

Directx12 3D 游戏开发实战

标签:matData,VertexOut,DX12,实例,uint,缓冲区,绘制
From: https://www.cnblogs.com/chenglixue/p/17234509.html

相关文章