map std 浮点数索引_虚幻5渲染编程(DX12篇)[顶点数据和索引数据的创建和上传]

cf592a7c3abe98156abf08f82387e08d.png
虚幻5渲染编程专栏概述及目录​zhuanlan.zhihu.com
27ae308b8b0a2a7f04d7799ad014650e.png

f5a885f4e861007f1f1002b56fc7dc7a.png

顶点数据和索引数据创建本来是很简单的东西,但是简单得过于自然,以至于很多地方被我们忽略掉了。随便去网上一搜,发现一大堆人在使用龙书给的代码框架。

geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
		mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);

很多人都没有探索到内部的具体实现。仔细想了下反正都研究到这里了,不如往里再研究试试。所以我这里打算从头开始,不使用书里给的d3dUtil来创建顶点和索引缓冲,顺便查一些资料,把一些细节的地方弄清楚。

069c698d926ca2ac4397ffa47e0037d9.png

首先我们的整体思路是:在内存里拿到顶点和索引数据。然后为顶点和索引缓冲分别创建两份资源,一份在Upload堆,一份在default堆,然后把内存里的顶点索引数据拷贝到Upload堆,然后再把Upload堆的数据拷贝到Default堆,最后渲染的时候我们使用Default堆的资源。因为Default堆的资源访问速度最快。

【1】创建顶点和索引原始数据

顶点数据和索引数据我们需要手动或从外部导入创建,然后获取它们缓冲的字节大小。最后声明ID3D12Resource类型的指针。

//创建或加载顶点数据和索引数据
std::array<Vertex, 4> vertices =
{
	Vertex({ XMFLOAT3(-1.0f, -1.0f, 0.0f), XMFLOAT4(0, 1, 0, 1) }),
	Vertex({ XMFLOAT3(-1.0f, +1.0f, 0.0f), XMFLOAT4(0, 0, 0, 1) }),
	Vertex({ XMFLOAT3(+1.0f, +1.0f, 0.0f), XMFLOAT4(1, 0, 0, 1) }),
	Vertex({ XMFLOAT3(+1.0f, -1.0f, 0.0f), XMFLOAT4(1, 1, 0, 1) }),
};

std::array<std::uint32_t, 6> indices =
{
	// front face
	0, 1, 2,
	0, 2, 3,
};

const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint32_t);

//Default buffer
Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferGPU = nullptr;
Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferGPU = nullptr;
//Upload buffer
Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferUploader = nullptr;
Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferUploader = nullptr;

【2】创建堆参数

默认堆和上传堆

//默认堆,上传堆
D3D12_HEAP_PROPERTIES defaultHeap;
memset(&defaultHeap, 0, sizeof(defaultHeap));
defaultHeap.Type = D3D12_HEAP_TYPE_DEFAULT;

D3D12_HEAP_PROPERTIES  uploadheap;
memset(&uploadheap, 0, sizeof(uploadheap));
uploadheap.Type = D3D12_HEAP_TYPE_UPLOAD;

【3】创建顶点缓冲的资源描述

DirectX12里所有资源都是ID3D12Resource,区别它们不同的就是创建时候的描述符。因为对于GPU而言它们就是一堆数据,关键看我们如何识别使用它们。

//创建VertexBuffer的资源描述
D3D12_RESOURCE_DESC DefaultVertexBufferDesc;
memset(&DefaultVertexBufferDesc, 0, sizeof(D3D12_RESOURCE_DESC));
DefaultVertexBufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
DefaultVertexBufferDesc.Alignment = 0;
DefaultVertexBufferDesc.Width = vbByteSize;
DefaultVertexBufferDesc.Height = 1;
DefaultVertexBufferDesc.DepthOrArraySize = 1;
DefaultVertexBufferDesc.MipLevels = 1;
DefaultVertexBufferDesc.Format = DXGI_FORMAT_UNKNOWN;
DefaultVertexBufferDesc.SampleDesc.Count = 1;
DefaultVertexBufferDesc.SampleDesc.Quality = 0;
DefaultVertexBufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
DefaultVertexBufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

【4】创建VertexBuffer和VertexBufferUpload

这两个Buffer分别被创建到default堆和upload堆。VertexBuffer被创建到default堆,创建的时候其初始化状态就被设置成了D3D12_RESOURCE_STATE_COPY_DEST

//为VertexBuffer和VertexBufferUploader创建资源
// Create the actual default buffer resource.
ThrowIfFailed(md3dDevice->CreateCommittedResource(
	&defaultHeap,
	D3D12_HEAP_FLAG_NONE,
	&DefaultVertexBufferDesc,
	D3D12_RESOURCE_STATE_COPY_DEST,
	nullptr,
	IID_PPV_ARGS(VertexBufferGPU.GetAddressOf())));

ThrowIfFailed(md3dDevice->CreateCommittedResource(
	&uploadheap,
	D3D12_HEAP_FLAG_NONE,
	&DefaultVertexBufferDesc,
	D3D12_RESOURCE_STATE_GENERIC_READ,
	nullptr,
	IID_PPV_ARGS(VertexBufferUploader.GetAddressOf())));

【5】获取 VertexBuffer footprint

//获取 VertexBuffer footprint
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
UINT64  vertex_total_bytes = 0;
md3dDevice->GetCopyableFootprints(&DefaultVertexBufferDesc, 0, 1, 0, &footprint, nullptr, nullptr, &vertex_total_bytes);
VertexBufferUploader->Unmap(0, nullptr);

【6】映射内存地址,并把数据拷贝到VertexBufferUploader里

void* ptr_vertex = nullptr;
VertexBufferUploader->Map(0, nullptr, &ptr_vertex);
memcpy(reinterpret_cast<char*>(ptr_vertex) + footprint.Offset, vertices.data(), vbByteSize);

【7】拷贝,把VertexBufferUploader里的数据拷贝到VertexBufferGPU里

mCommandList->CopyBufferRegion(VertexBufferGPU.Get(), 0, VertexBufferUploader.Get(), 0, vertex_total_bytes);

【8】为VertexBufferGPU插入资源屏障,因为一开始是以D3D12_RESOURCE_STATE_COPY_DEST的状态创建的资源,所以拷贝完以后需要给它设置好资源屏障。

D3D12_RESOURCE_BARRIER barrier_vertex;
memset(&barrier_vertex, 0, sizeof(barrier_vertex));
barrier_vertex.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier_vertex.Transition.pResource = VertexBufferGPU.Get();
barrier_vertex.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier_vertex.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier_vertex.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
mCommandList->ResourceBarrier(1, &barrier_vertex);

【9】创建IndexBuffer

到这里我们就完成了对VertexBuffer的处理,下面类似,需要对IndexBuffer进行处理,两者过程及其类似,我就不一步一步赘述了。

//创建IndexBuffer的资源描述
D3D12_RESOURCE_DESC DefaultIndexBufferDesc;
memset(&DefaultIndexBufferDesc, 0, sizeof(D3D12_RESOURCE_DESC));
DefaultIndexBufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
DefaultIndexBufferDesc.Alignment = 0;
DefaultIndexBufferDesc.Width = ibByteSize;
DefaultIndexBufferDesc.Height = 1;
DefaultIndexBufferDesc.DepthOrArraySize = 1;
DefaultIndexBufferDesc.MipLevels = 1;
DefaultIndexBufferDesc.Format = DXGI_FORMAT_UNKNOWN;
DefaultIndexBufferDesc.SampleDesc.Count = 1;
DefaultIndexBufferDesc.SampleDesc.Quality = 0;
DefaultIndexBufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
DefaultIndexBufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

//为IndexBuffer和IndexBufferUploader创建资源
ThrowIfFailed(md3dDevice->CreateCommittedResource(
	&defaultHeap,
	D3D12_HEAP_FLAG_NONE,
	&DefaultIndexBufferDesc,
	D3D12_RESOURCE_STATE_COPY_DEST,
	nullptr,
	IID_PPV_ARGS(IndexBufferGPU.GetAddressOf())));

ThrowIfFailed(md3dDevice->CreateCommittedResource(
	&uploadheap,
	D3D12_HEAP_FLAG_NONE,
	&DefaultIndexBufferDesc,
	D3D12_RESOURCE_STATE_GENERIC_READ,
	nullptr,
	IID_PPV_ARGS(IndexBufferUploader.GetAddressOf())));


//获取 IndexBuffer footprint
D3D12_PLACED_SUBRESOURCE_FOOTPRINT indexBufferFootprint;
UINT64  index_total_bytes = 0;
md3dDevice->GetCopyableFootprints(&DefaultIndexBufferDesc, 0, 1, 0, &indexBufferFootprint, nullptr, nullptr, &index_total_bytes);


//映射内存地址,并把数据拷贝到IndexBufferUploader里
void* ptr_index = nullptr;
IndexBufferUploader->Map(0, nullptr, &ptr_index);
memcpy(reinterpret_cast<char*>(ptr_index) + indexBufferFootprint.Offset, indices.data(), ibByteSize);
IndexBufferUploader->Unmap(0, nullptr);

//拷贝,把IndexBufferUploader里的数据拷贝到IndexBufferGPU里
mCommandList->CopyBufferRegion(IndexBufferGPU.Get(), 0, IndexBufferUploader.Get(), 0, index_total_bytes);

//为IndexBufferGPU插入资源屏障
D3D12_RESOURCE_BARRIER barrier_index;
memset(&barrier_index, 0, sizeof(barrier_index));
barrier_index.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier_index.Transition.pResource = IndexBufferGPU.Get();
barrier_index.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier_index.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier_index.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
mCommandList->ResourceBarrier(1, &barrier_index);

【10】最后我们用VertexBufferGPU和IndexBufferGPU创建VerteBufferView和IndexBufferView来渲染。

//VertexBufferView
D3D12_VERTEX_BUFFER_VIEW vbv;
vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();
vbv.StrideInBytes = sizeof(Vertex);
vbv.SizeInBytes = vbByteSize;

//IndexBufferView
D3D12_INDEX_BUFFER_VIEW ibv;
ibv.BufferLocation = IndexBufferGPU->GetGPUVirtualAddress();
ibv.Format = DXGI_FORMAT_R32_UINT;
ibv.SizeInBytes = ibByteSize;

下面是我用上述代码绘制的结果

880b540819b43f2cde936bd1712f0926.png

Shader

struct VertexIn
{
    float3 PosL  : POSITION;
    float4 Color : COLOR;
};

struct VertexOut
{
    float4 PosH  : SV_POSITION;
    float4 Color : COLOR;
};

VertexOut VS(VertexIn vin)
{
    VertexOut vout;
	
    // Transform to homogeneous clip space.
    vout.PosH = float4(vin.PosL, 1.0f);
	
    // Just pass vertex color into the pixel shader.
    vout.Color = vin.Color;
    
    return vout;
}

float4 PS(VertexOut pin) : SV_Target
{
    return pin.Color;
}

虽然简单但是我们实现了一个非常基础的功能,搭建一个UV画布:这是做一切后处理,Ray marching的基础。

a33c4a35835f3d7fb4fbd3c2806d33c2.png

其实不太理解为什么龙书要封装大量代码叫我们直接使用,这样反而对初学者不是非常友好,很容易忽略内部的细节。
有几个点需要注意一下:

【1】上面的初始化一定要放到Close之前,因为如果close以后,commandlist是无法记录命令的。其实在DX12上应该养成一个这样的习惯,不然很容易犯错,确认自己在记录command之前CommandList是否是打开的。

ff6e78db1278b126baf1ab0d24d4dfab.png

【2】Draw之前要记得设置好绘制状态,DX12比DX11多了一些东西。

4acc11db2226c74f29c24cf3cb3a4d04.png

特别注意别把mCommandList->SetGraphicsRootSignature和mCommandList->SetComputeRootSignature搞混了,有时候智能补全没看清楚就把mCommandList->SetComputeRootSignature敲了上去,查半天bug发现是这个坑。

48c64d91e36aa77228b771f94e44a292.png
YivanLee:虚幻5渲染编程(DX12篇)[DirectX3D12渲染到纹理]​zhuanlan.zhihu.com
947694768a741ac77c2d092c926e4d3a.png

fa536435c29a81e89a32229167ca6c84.png

4049e24b1e25c8453faffa9ee839a663.png

2020/10/3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值