实现 NanoVDB 体积云渲染

完成步骤2:读取 NanoVDB 密度并渲染体积云

修改内容:
1. NanoVDBLoader: 添加 voxel_size 输出用于调试
2. volume.hlsl: 实现完整的 ray marching 体积渲染
   - 初始化 NanoVDB 访问器
   - 密度采样函数
   - 光线步进和累积
3. wireframe.hlsl: 新增边界框线框着色器
4. main.cpp:
   - 移动相机到云的外部
   - 添加独立的常量缓冲区避免冲突
   - 同时渲染体积云和边界框

已知问题:
- 边界框位置计算需要改进
- bunny.nvdb 渲染效果不佳
- 需要进一步优化密度和步进参数
This commit is contained in:
2026-03-11 21:51:50 +08:00
parent 859b851842
commit fa50179d00
4 changed files with 128 additions and 26 deletions

View File

@@ -33,6 +33,9 @@ bool LoadNanoVDB(const char* filePath, NanoVDBData& outData, ID3D12GraphicsComma
outData.worldBBox[i] = bboxData[i];
}
double* voxelSizeData = (double*)((char*)bufferData + 608);
printf("[NanoVDB] Voxel size: [%.6f, %.6f, %.6f]\n", voxelSizeData[0], voxelSizeData[1], voxelSizeData[2]);
outData.cpuData = bufferData;
outData.byteSize = byteSize;
outData.elementCount = elementCount;

View File

@@ -28,6 +28,37 @@ struct PSInput
float3 worldPos : TEXCOORD1;
};
struct NanoVolume
{
pnanovdb_grid_handle_t grid;
pnanovdb_grid_type_t grid_type;
pnanovdb_readaccessor_t acc;
};
void initVolume(inout NanoVolume volume)
{
pnanovdb_grid_handle_t grid;
grid.address.byte_offset = 0;
pnanovdb_grid_type_t grid_type = pnanovdb_buf_read_uint32(buf, PNANOVDB_GRID_OFF_GRID_TYPE);
pnanovdb_tree_handle_t tree = pnanovdb_grid_get_tree(buf, grid);
pnanovdb_root_handle_t root = pnanovdb_tree_get_root(buf, tree);
pnanovdb_readaccessor_t acc;
pnanovdb_readaccessor_init(acc, root);
volume.grid = grid;
volume.grid_type = grid_type;
volume.acc = acc;
}
float get_value_coord(inout pnanovdb_readaccessor_t acc, float3 pos)
{
pnanovdb_vec3_t p = pos;
pnanovdb_coord_t ijk = pnanovdb_hdda_pos_to_ijk(p);
pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, ijk);
return pnanovdb_read_float(buf, address);
}
PSInput MainVS(VSInput input)
{
PSInput output;
@@ -63,20 +94,27 @@ float4 MainPS(PSInput input) : SV_TARGET
tmin = max(0, tmin);
NanoVolume volume;
initVolume(volume);
float3 color = float3(0, 0, 0);
float alpha = 0;
float transmittance = 1.0;
for (uint i = 0; i < _MaxSteps; i++) {
float t = tmin + i * _StepSize;
if (t > tmax || alpha > 0.99) break;
if (t > tmax || transmittance < 0.01) break;
float3 pos = _CameraPos + rayDir * t;
float3 worldPos = _CameraPos + rayDir * t;
float density = 0.1;
float density = get_value_coord(volume.acc, worldPos) * _DensityScale;
color += density * float3(0.5, 0.7, 1.0) * (1 - alpha);
alpha += density * (1 - alpha);
if (density > 0.001) {
float3 S = density * float3(1, 1, 1);
color += transmittance * S * _StepSize;
transmittance *= exp(-density * _StepSize);
}
}
float alpha = 1.0 - transmittance;
return float4(color, alpha);
}

26
Res/Shader/wireframe.hlsl Normal file
View File

@@ -0,0 +1,26 @@
cbuffer CB0 : register(b1)
{
float4x4 _ViewProjection;
};
struct VSInput
{
float3 position : POSITION;
};
struct PSInput
{
float4 position : SV_POSITION;
};
PSInput MainVS(VSInput input)
{
PSInput output;
output.position = mul(_ViewProjection, float4(input.position, 1.0));
return output;
}
float4 MainPS(PSInput input) : SV_TARGET
{
return float4(0.0, 1.0, 0.0, 1.0);
}

View File

@@ -79,7 +79,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
ID3D12CommandAllocator* commandAllocator = GetCommandAllocator();
NanoVDBData nanoVDBData = {};
LoadNanoVDB("Res/NanoVDB/bunny.nvdb", nanoVDBData, commandList, commandAllocator);
LoadNanoVDB("Res/NanoVDB/cloud.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],
@@ -138,6 +138,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
ID3D12RootSignature* volumeRootSignature = InitVolumeRootSignature();
printf("Volume root signature created\n");
D3D12_SHADER_BYTECODE volumeVS, volumePS;
memset(&volumeVS, 0, sizeof(volumeVS));
memset(&volumePS, 0, sizeof(volumePS));
@@ -145,12 +146,6 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
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);
}
ID3D12PipelineState* quadPSO = CreateQuadPSO(volumeRootSignature, volumeVS, volumePS);
if (!quadPSO) {
@@ -159,12 +154,31 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
printf("Quad PSO created: %p\n", quadPSO);
}
D3D12_SHADER_BYTECODE wireframeVS, wireframePS;
memset(&wireframeVS, 0, sizeof(wireframeVS));
memset(&wireframePS, 0, sizeof(wireframePS));
CreateShaderFromFile(L"Res/Shader/wireframe.hlsl", "MainVS", "vs_5_1", &wireframeVS);
CreateShaderFromFile(L"Res/Shader/wireframe.hlsl", "MainPS", "ps_5_1", &wireframePS);
printf("Wireframe VS: ptr=%p, size=%zu\n", wireframeVS.pShaderBytecode, wireframeVS.BytecodeLength);
printf("Wireframe PS: ptr=%p, size=%zu\n", wireframePS.pShaderBytecode, wireframePS.BytecodeLength);
ID3D12PipelineState* volumePSO = CreateVolumePSO(volumeRootSignature, wireframeVS, wireframePS);
if (!volumePSO) {
printf("Volume PSO creation failed!\n");
} else {
printf("Volume PSO created: %p\n", volumePSO);
}
ID3D12Resource* volumeCB = CreateConstantBufferObject(65536);
ID3D12Resource* wireframeCB = 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);
DirectX::XMMATRIX viewMatrix = DirectX::XMMatrixIdentity();
DirectX::XMMATRIX viewMatrix = DirectX::XMMatrixLookAtLH(
DirectX::XMVectorSet(-10.0f, 73.0f, -600.0f, 1.0f),
DirectX::XMVectorSet(-10.0f, 73.0f, 0.0f, 1.0f),
DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f));
DirectX::XMMATRIX modelMatrix = DirectX::XMMatrixTranslation(0.0f,0.0f,5.0f);
//modelMatrix *= DirectX::XMMatrixRotationZ(90.0f*3.141592f/180.0f);
DirectX::XMFLOAT4X4 tempMatrix;
@@ -278,17 +292,17 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
float volumeCBData[32];
memcpy(volumeCBData, &invViewProjMat, sizeof(float) * 16);
volumeCBData[16] = 0.0f;
volumeCBData[17] = 0.0f;
volumeCBData[18] = -5.0f;
volumeCBData[19] = 1.0f;
volumeCBData[20] = bboxMin[0] - bboxCenter[0];
volumeCBData[21] = bboxMin[1] - bboxCenter[1];
volumeCBData[22] = bboxMin[2] - bboxCenter[2] + 80.0f;
volumeCBData[23] = 0.5f;
volumeCBData[24] = bboxMax[0] - bboxCenter[0];
volumeCBData[25] = bboxMax[1] - bboxCenter[1];
volumeCBData[26] = bboxMax[2] - bboxCenter[2] + 80.0f;
volumeCBData[16] = -10.0f;
volumeCBData[17] = 73.0f;
volumeCBData[18] = -600.0f;
volumeCBData[19] = 0.01f; // DensityScale
volumeCBData[20] = (float)nanoVDBData.worldBBox[0];
volumeCBData[21] = (float)nanoVDBData.worldBBox[1];
volumeCBData[22] = (float)nanoVDBData.worldBBox[2];
volumeCBData[23] = 0.5f; // StepSize
volumeCBData[24] = (float)nanoVDBData.worldBBox[3];
volumeCBData[25] = (float)nanoVDBData.worldBBox[4];
volumeCBData[26] = (float)nanoVDBData.worldBBox[5];
volumeCBData[27] = 128;
UpdateConstantBuffer(volumeCB, volumeCBData, sizeof(float) * 28);
if (frameCount == 1) {
@@ -308,6 +322,27 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
commandList->DrawIndexedInstanced(6, 1, 0, 0, 0);
}
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 wireframeCBData[16];
memcpy(wireframeCBData, &bboxViewProjMat, sizeof(float) * 16);
UpdateConstantBuffer(wireframeCB, wireframeCBData, sizeof(float) * 16);
if (volumePSO) {
commandList->SetPipelineState(volumePSO);
commandList->SetGraphicsRootSignature(volumeRootSignature);
commandList->SetGraphicsRootConstantBufferView(0, wireframeCB->GetGPUVirtualAddress());
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_LINELIST);
commandList->IASetVertexBuffers(0, 1, &D3D12_VERTEX_BUFFER_VIEW{ cubeVBO->GetGPUVirtualAddress(), 8 * 3 * sizeof(float), sizeof(float) * 3 });
commandList->IASetIndexBuffer(&D3D12_INDEX_BUFFER_VIEW{ cubeIBO->GetGPUVirtualAddress(), 24 * sizeof(uint32_t), DXGI_FORMAT_R32_UINT });
commandList->DrawIndexedInstanced(24, 1, 0, 0, 0);
}
EndRenderToSwapChain(commandList);
EndCommandList();
SwapD3D12Buffers();