实现 NanoVDB 边界框可视化

修改内容:
1. NanoVDBLoader 添加边界框信息读取 (worldBBox[6])
2. BattleFireDirect 添加 Volume 渲染相关函数:
   - InitVolumeRootSignature(): 体积渲染的 root signature
   - CreateVolumePSO(): 线框模式 PSO
3. main.cpp:
   - 打印 NanoVDB 边界框信息
   - 创建立方体网格表示边界框
   - 渲染绿色线框立方体
4. 修复 CreateShaderFromFile 支持 #include
5. 修复 volume.hlsl 使用 32 位地址模式

验证结果:
- 成功显示 NanoVDB 边界框线框
- 着色器能正确读取 NanoVDB 数据
This commit is contained in:
2026-03-11 20:32:54 +08:00
parent bc58abc98f
commit af3b61a45a
6 changed files with 184 additions and 3 deletions

View File

@@ -74,6 +74,37 @@ ID3D12RootSignature* InitRootSignature() {
return d3d12RootSignature;
}
ID3D12RootSignature* InitVolumeRootSignature() {
D3D12_ROOT_PARAMETER rootParameters[2];
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParameters[0].Descriptor.RegisterSpace = 0;
rootParameters[0].Descriptor.ShaderRegister = 1;
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParameters[1].Descriptor.RegisterSpace = 0;
rootParameters[1].Descriptor.ShaderRegister = 1;
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
rootSignatureDesc.NumParameters = _countof(rootParameters);
rootSignatureDesc.pParameters = rootParameters;
rootSignatureDesc.NumStaticSamplers = 0;
rootSignatureDesc.pStaticSamplers = nullptr;
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
ID3DBlob* signature;
HRESULT hResult = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, nullptr);
ID3D12RootSignature* d3d12RootSignature;
gD3D12Device->CreateRootSignature(
0, signature->GetBufferPointer(), signature->GetBufferSize(),
IID_PPV_ARGS(&d3d12RootSignature));
return d3d12RootSignature;
}
void CreateShaderFromFile(
LPCTSTR inShaderFilePath,
const char* inMainFunctionName,
@@ -81,12 +112,16 @@ void CreateShaderFromFile(
D3D12_SHADER_BYTECODE* inShader) {
ID3DBlob* shaderBuffer = nullptr;
ID3DBlob* errorBuffer = nullptr;
HRESULT hResult = D3DCompileFromFile(inShaderFilePath, nullptr, nullptr,
HRESULT hResult = D3DCompileFromFile(inShaderFilePath, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
inMainFunctionName, inTarget, D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0, &shaderBuffer, &errorBuffer);
if (FAILED(hResult)) {
char szLog[1024] = {0};
strcpy(szLog, (char*)errorBuffer->GetBufferPointer());
if (errorBuffer) {
strcpy(szLog, (char*)errorBuffer->GetBufferPointer());
} else {
strcpy(szLog, "Unknown error");
}
printf("CreateShaderFromFile error : [%s][%s]:[%s]\n", inMainFunctionName, inTarget, szLog);
errorBuffer->Release();
return;
@@ -183,6 +218,69 @@ ID3D12PipelineState* CreatePSO(ID3D12RootSignature* inID3D12RootSignature,
return d3d12PSO;
}
ID3D12PipelineState* CreateVolumePSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader) {
D3D12_INPUT_ELEMENT_DESC vertexDataElementDesc[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0}
};
D3D12_INPUT_LAYOUT_DESC vertexDataLayoutDesc = {};
vertexDataLayoutDesc.NumElements = 1;
vertexDataLayoutDesc.pInputElementDescs = vertexDataElementDesc;
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.pRootSignature = inID3D12RootSignature;
psoDesc.VS = inVertexShader;
psoDesc.PS = inPixelShader;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
psoDesc.SampleDesc.Count = 1;
psoDesc.SampleDesc.Quality = 0;
psoDesc.SampleMask = 0xffffffff;
psoDesc.InputLayout = vertexDataLayoutDesc;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
psoDesc.RasterizerState.FrontCounterClockwise = FALSE;
psoDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
psoDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
psoDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
psoDesc.RasterizerState.DepthClipEnable = TRUE;
psoDesc.RasterizerState.MultisampleEnable = FALSE;
psoDesc.RasterizerState.AntialiasedLineEnable = FALSE;
psoDesc.RasterizerState.ForcedSampleCount = 0;
psoDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
psoDesc.DepthStencilState.DepthEnable = TRUE;
psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.BlendState.AlphaToCoverageEnable = FALSE;
psoDesc.BlendState.IndependentBlendEnable = FALSE;
for (int i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) {
psoDesc.BlendState.RenderTarget[i].BlendEnable = FALSE;
psoDesc.BlendState.RenderTarget[i].LogicOpEnable = FALSE;
psoDesc.BlendState.RenderTarget[i].SrcBlend = D3D12_BLEND_ONE;
psoDesc.BlendState.RenderTarget[i].DestBlend = D3D12_BLEND_ZERO;
psoDesc.BlendState.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD;
psoDesc.BlendState.RenderTarget[i].SrcBlendAlpha = D3D12_BLEND_ONE;
psoDesc.BlendState.RenderTarget[i].DestBlendAlpha = D3D12_BLEND_ZERO;
psoDesc.BlendState.RenderTarget[i].BlendOpAlpha = D3D12_BLEND_OP_ADD;
psoDesc.BlendState.RenderTarget[i].LogicOp = D3D12_LOGIC_OP_NOOP;
psoDesc.BlendState.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
psoDesc.NumRenderTargets = 1;
ID3D12PipelineState* d3d12PSO = nullptr;
HRESULT hResult = gD3D12Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&d3d12PSO));
if (FAILED(hResult)) {
printf("CreateVolumePSO failed: 0x%08X\n", hResult);
return nullptr;
}
return d3d12PSO;
}
bool InitD3D12(HWND inHWND, int inWidth, int inHeight) {
HRESULT hResult;
UINT dxgiFactoryFlags = 0;

View File

@@ -9,6 +9,7 @@ D3D12_RESOURCE_BARRIER InitResourceBarrier(
ID3D12Resource* inResource, D3D12_RESOURCE_STATES inPrevState,
D3D12_RESOURCE_STATES inNextState);
ID3D12RootSignature* InitRootSignature();
ID3D12RootSignature* InitVolumeRootSignature();
void CreateShaderFromFile(
LPCTSTR inShaderFilePath,
const char* inMainFunctionName,
@@ -21,6 +22,8 @@ ID3D12Resource* CreateBufferObject(ID3D12GraphicsCommandList* inCommandList,
ID3D12PipelineState* CreatePSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader,
D3D12_SHADER_BYTECODE inGSShader);
ID3D12PipelineState* CreateVolumePSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader);
bool InitD3D12(HWND inHWND, int inWidth, int inHeight);
ID3D12GraphicsCommandList* GetCommandList();
ID3D12CommandAllocator* GetCommandAllocator();

View File

@@ -28,6 +28,11 @@ bool LoadNanoVDB(const char* filePath, NanoVDBData& outData, ID3D12GraphicsComma
memcpy(bufferData, gridHandle.buffer().data(), byteSize);
double* bboxData = (double*)((char*)bufferData + 560);
for (int i = 0; i < 6; i++) {
outData.worldBBox[i] = bboxData[i];
}
outData.cpuData = bufferData;
outData.byteSize = byteSize;
outData.elementCount = elementCount;

View File

@@ -8,6 +8,7 @@ struct NanoVDBData {
void* cpuData;
uint64_t byteSize;
uint64_t elementCount;
double worldBBox[6];
};
bool LoadNanoVDB(const char* filePath, NanoVDBData& outData, ID3D12GraphicsCommandList* cmdList, ID3D12CommandAllocator* cmdAlloc = nullptr);

View File

@@ -1,5 +1,5 @@
#define PNANOVDB_HLSL
#define PNANOVDB_ADDRESS_64
#define PNANOVDB_ADDRESS_32
#include "PNanoVDB.hlsl"
cbuffer CB0 : register(b1)

View File

@@ -81,6 +81,36 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
NanoVDBData nanoVDBData = {};
LoadNanoVDB("Res/NanoVDB/bunny.nvdb", nanoVDBData, commandList, commandAllocator);
printf("NanoVDB loaded: %llu bytes\n", (unsigned long long)nanoVDBData.byteSize);
printf("NanoVDB BBox: [%.2f, %.2f, %.2f] - [%.2f, %.2f, %.2f]\n",
nanoVDBData.worldBBox[0], nanoVDBData.worldBBox[1], nanoVDBData.worldBBox[2],
nanoVDBData.worldBBox[3], nanoVDBData.worldBBox[4], nanoVDBData.worldBBox[5]);
float bboxMin[3] = { (float)nanoVDBData.worldBBox[0], (float)nanoVDBData.worldBBox[1], (float)nanoVDBData.worldBBox[2] };
float bboxMax[3] = { (float)nanoVDBData.worldBBox[3], (float)nanoVDBData.worldBBox[4], (float)nanoVDBData.worldBBox[5] };
float bboxCenter[3] = { (bboxMin[0] + bboxMax[0]) / 2, (bboxMin[1] + bboxMax[1]) / 2, (bboxMin[2] + bboxMax[2]) / 2 };
float bboxSize[3] = { bboxMax[0] - bboxMin[0], bboxMax[1] - bboxMin[1], bboxMax[2] - bboxMin[2] };
printf("BBox center: [%.2f, %.2f, %.2f], size: [%.2f, %.2f, %.2f]\n",
bboxCenter[0], bboxCenter[1], bboxCenter[2],
bboxSize[0], bboxSize[1], bboxSize[2]);
float cubeVertices[] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
uint32_t cubeIndices[] = {
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
};
ID3D12Resource* cubeVBO = CreateBufferObject(commandList, cubeVertices, sizeof(cubeVertices), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
ID3D12Resource* cubeIBO = CreateBufferObject(commandList, cubeIndices, sizeof(cubeIndices), D3D12_RESOURCE_STATE_INDEX_BUFFER);
printf("Cube mesh created\n");
StaticMeshComponent staticMeshComponent;
staticMeshComponent.InitFromFile(commandList, "Res/Model/Sphere.lhsm");
@@ -95,6 +125,24 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
ID3D12PipelineState*pso=CreatePSO(rootSignature, vs, ps, gs);
printf("PSO created\n");
ID3D12RootSignature* volumeRootSignature = InitVolumeRootSignature();
printf("Volume root signature created\n");
D3D12_SHADER_BYTECODE volumeVS, volumePS;
memset(&volumeVS, 0, sizeof(volumeVS));
memset(&volumePS, 0, sizeof(volumePS));
CreateShaderFromFile(L"Res/Shader/volume.hlsl", "MainVS", "vs_5_1", &volumeVS);
CreateShaderFromFile(L"Res/Shader/volume.hlsl", "MainPS", "ps_5_1", &volumePS);
printf("Volume VS: ptr=%p, size=%zu\n", volumeVS.pShaderBytecode, volumeVS.BytecodeLength);
printf("Volume PS: ptr=%p, size=%zu\n", volumePS.pShaderBytecode, volumePS.BytecodeLength);
ID3D12PipelineState* volumePSO = CreateVolumePSO(volumeRootSignature, volumeVS, volumePS);
if (!volumePSO) {
printf("Volume PSO creation failed!\n");
} else {
printf("Volume PSO created: %p\n", volumePSO);
}
ID3D12Resource* volumeCB = CreateConstantBufferObject(65536);
ID3D12Resource* cb = CreateConstantBufferObject(65536);//1024x64(4x4)
DirectX::XMMATRIX projectionMatrix=DirectX::XMMatrixPerspectiveFovLH(
(45.0f*3.141592f)/180.0f,1280.0f/720.0f,0.1f,1000.0f);
@@ -204,6 +252,32 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
staticMeshComponent.mVertexCount, staticMeshComponent.mVBO, staticMeshComponent.mSubMeshes.size());
}
staticMeshComponent.Render(commandList);
DirectX::XMMATRIX bboxScale = DirectX::XMMatrixScaling(bboxSize[0], bboxSize[1], bboxSize[2]);
DirectX::XMMATRIX bboxTranslate = DirectX::XMMatrixTranslation(bboxCenter[0], bboxCenter[1], bboxCenter[2]);
DirectX::XMMATRIX bboxModel = bboxScale * bboxTranslate;
DirectX::XMMATRIX bboxViewProj = bboxModel * viewMatrix * projectionMatrix;
DirectX::XMFLOAT4X4 bboxViewProjMat;
DirectX::XMStoreFloat4x4(&bboxViewProjMat, bboxViewProj);
float volumeCBData[20];
memcpy(volumeCBData, &bboxViewProjMat, sizeof(float) * 16);
volumeCBData[16] = 0.0f;
volumeCBData[17] = 0.0f;
volumeCBData[18] = -5.0f;
volumeCBData[19] = 1.0f;
UpdateConstantBuffer(volumeCB, volumeCBData, sizeof(float) * 20);
if (volumePSO) {
commandList->SetPipelineState(volumePSO);
commandList->SetGraphicsRootSignature(volumeRootSignature);
commandList->SetGraphicsRootConstantBufferView(0, volumeCB->GetGPUVirtualAddress());
commandList->SetGraphicsRootShaderResourceView(1, nanoVDBData.gpuBuffer->GetGPUVirtualAddress());
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_LINELIST);
commandList->IASetVertexBuffers(0, 1, &D3D12_VERTEX_BUFFER_VIEW{ cubeVBO->GetGPUVirtualAddress(), sizeof(cubeVertices), sizeof(float) * 3 });
commandList->IASetIndexBuffer(&D3D12_INDEX_BUFFER_VIEW{ cubeIBO->GetGPUVirtualAddress(), sizeof(cubeIndices), DXGI_FORMAT_R32_UINT });
commandList->DrawIndexedInstanced(24, 1, 0, 0, 0);
}
EndRenderToSwapChain(commandList);
EndCommandList();
SwapD3D12Buffers();