• 在DirectX12中使用Texture


    这次让我们来看看如何在DirectX12中使用Texture。由于我们创建的texture最终是和const buffer一样,存放在同一个DescriptorHeap中的,所以有必要在之前的基础上扩充一下heap的大小:

    	mCbvSrvHeapIncSize = mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    	D3D12_DESCRIPTOR_HEAP_DESC cbvSrvHeapDesc;
    	cbvSrvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    	cbvSrvHeapDesc.NumDescriptors = objCbCount + passCbCount + matCbCount + shaderResCount;
    	cbvSrvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    	cbvSrvHeapDesc.NodeMask = 0;
    	ThrowIfFailed(mDevice->CreateDescriptorHeap(&cbvSrvHeapDesc, IID_PPV_ARGS(&mCbvSrvHeap)));
    

    接下来,我们知道,采样一个纹理有多种采样过滤模式,point,linear,anisotropic;寻址模式也有wrap,mirror,clamp等等。我们可以事先定义好用到的sampler:

    		mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(0, D3D12_FILTER_MIN_MAG_MIP_POINT, 
    		D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP));
    	mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(1, D3D12_FILTER_MIN_MAG_MIP_POINT,
    		D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP));
    	mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR,
    		D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP));
    	mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(3, D3D12_FILTER_MIN_MAG_MIP_LINEAR,
    		D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP));
    	mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(4, D3D12_FILTER_ANISOTROPIC,
    		D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP));
    	mStaticSamplers.push_back(CD3DX12_STATIC_SAMPLER_DESC(5, D3D12_FILTER_ANISOTROPIC,
    		D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP));
    

    然后,在创建root signature的时候,将samplers作为参数传进去(存放于寄存器s0,s1,...)。由于这次需要传递texture资源给shader使用(存放于寄存器t0,t1,...),我们需要增加一个根参数:

    	CD3DX12_DESCRIPTOR_RANGE cbvSrvTable[4];
    	cbvSrvTable[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
    	cbvSrvTable[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);
    	cbvSrvTable[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 2);
    	cbvSrvTable[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
    
    	CD3DX12_ROOT_PARAMETER rootParams[4];
    	rootParams[0].InitAsDescriptorTable(1, &cbvSrvTable[0]);
    	rootParams[1].InitAsDescriptorTable(1, &cbvSrvTable[1]);
    	rootParams[2].InitAsDescriptorTable(1, &cbvSrvTable[2]);
    	rootParams[3].InitAsDescriptorTable(1, &cbvSrvTable[3]);
    
    	CD3DX12_ROOT_SIGNATURE_DESC sigDesc(_countof(rootParams), rootParams, mStaticSamplers.size(), 
    		mStaticSamplers.data(), D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    

    这些准备工作做完之后,就可以正式去加载texture资源了,调用加载接口之后,不要忘记手动向GPU提交执行,否则这个texture就只能CPU看见,GPU还是一无所知。加载成功之后,还要记得创建对应的shader resource view,将资源绑定到descriptor heap上,并存放到正确的位置:

    	ThrowIfFailed(mCommandAlloc->Reset());
    	ThrowIfFailed(mCommandList->Reset(mCommandAlloc.Get(), nullptr));
    	ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(mDevice.Get(), mCommandList.Get(), srcFile.c_str(), res, uploadHeap));
    	ThrowIfFailed(mCommandList->Close());
    	ID3D12CommandList *cmdList[] = { mCommandList.Get() };
    	mCommandQueue->ExecuteCommandLists(_countof(cmdList), cmdList);
    	FlushCommandQueue();
    
    	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    	srvDesc.Format = res->GetDesc().Format;
    	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    	srvDesc.Texture2D.MostDetailedMip = 0;
    	srvDesc.Texture2D.MipLevels = res->GetDesc().MipLevels;
    	srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
    
    	CD3DX12_CPU_DESCRIPTOR_HANDLE handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(
    		mCbvSrvHeap->GetCPUDescriptorHandleForHeapStart());
    	UINT srvHeapIndex = objCbCount + passCbCount + matCbCount + texId;
    
    	handle.Offset(srvHeapIndex, mCbvSrvHeapIncSize);
    	mDevice->CreateShaderResourceView(res.Get(), &srvDesc, handle);
    	return true;
    

    最后,在绘制阶段,我们要显式地告诉GPU从哪儿读取texture资源加载到对应的寄存器上:

    	UINT srvHeapIndex = objCbCount + passCbCount + matCbCount + object->mTexture->mTexId;
    	CD3DX12_GPU_DESCRIPTOR_HANDLE srvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(
    		mCbvSrvHeap->GetGPUDescriptorHandleForHeapStart());
    	srvHandle.Offset(srvHeapIndex, mCbvSrvHeapIncSize);
    	mCommandList->SetGraphicsRootDescriptorTable(3, srvHandle);
    

    这里,可能就有疑问了,为啥要把shader resource和const buffer都存放到一个descriptor heap上管理呢?分开用两个heap,各用各的不是更好吗?具体的解释可以参考这里。简单来说,首先SetDescriptorHeaps这个API同一类型的heap同时只能设置一个,而存放const buffer和shader resource的heap的type都是D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV 。其次,如果我们在绘制过程中频繁调用这个API去切换使用不同的heap,开销会非常高。所以,推荐的解决方法,就是使用一个大的heap,然后划分好不同的区域给const buffer和shader resource使用。

    如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路-

  • 相关阅读:
    类与对象
    《大道至简》第三章读后感
    动手动脑及课后作业
    课程作业一
    第三周学习进度条
    软件工程个人作业02
    第二周学习进度条
    软件工程个人作业01(2)
    软件工程个人作业01
    登录界面
  • 原文地址:https://www.cnblogs.com/back-to-the-past/p/14317459.html
Copyright © 2020-2023  润新知