实现全屏四边形 Ray Marching 体积渲染基础

步骤 1 完成:
1. 创建全屏四边形顶点数据
2. 实现 Ray Marching 着色器:
   - 从相机发射光线
   - 光线与边界框相交检测
   - 固定密度采样
   - Alpha 混合输出
3. 添加 CreateQuadPSO 函数
4. 更新渲染管线

验证结果:
- 整个背景变成淡蓝色
- Ray marching 基础逻辑工作正常
This commit is contained in:
2026-03-11 20:52:56 +08:00
parent 1e8c3710a5
commit 859b851842
4 changed files with 161 additions and 33 deletions

View File

@@ -281,6 +281,70 @@ ID3D12PipelineState* CreateVolumePSO(ID3D12RootSignature* inID3D12RootSignature,
return d3d12PSO;
}
ID3D12PipelineState* CreateQuadPSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader) {
D3D12_INPUT_ELEMENT_DESC vertexDataElementDesc[] = {
{"POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,8,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0}
};
D3D12_INPUT_LAYOUT_DESC vertexDataLayoutDesc = {};
vertexDataLayoutDesc.NumElements = 2;
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_TRIANGLE;
psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
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_ZERO;
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 = TRUE;
psoDesc.BlendState.RenderTarget[i].LogicOpEnable = FALSE;
psoDesc.BlendState.RenderTarget[i].SrcBlend = D3D12_BLEND_SRC_ALPHA;
psoDesc.BlendState.RenderTarget[i].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
psoDesc.BlendState.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD;
psoDesc.BlendState.RenderTarget[i].SrcBlendAlpha = D3D12_BLEND_ONE;
psoDesc.BlendState.RenderTarget[i].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
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("CreateQuadPSO failed: 0x%08X\n", hResult);
return nullptr;
}
return d3d12PSO;
}
bool InitD3D12(HWND inHWND, int inWidth, int inHeight) {
HRESULT hResult;
UINT dxgiFactoryFlags = 0;

View File

@@ -24,6 +24,8 @@ ID3D12PipelineState* CreatePSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inGSShader);
ID3D12PipelineState* CreateVolumePSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader);
ID3D12PipelineState* CreateQuadPSO(ID3D12RootSignature* inID3D12RootSignature,
D3D12_SHADER_BYTECODE inVertexShader, D3D12_SHADER_BYTECODE inPixelShader);
bool InitD3D12(HWND inHWND, int inWidth, int inHeight);
ID3D12GraphicsCommandList* GetCommandList();
ID3D12CommandAllocator* GetCommandAllocator();

View File

@@ -4,47 +4,79 @@
cbuffer CB0 : register(b1)
{
float4x4 _ViewProjection;
float4x4 _InverseViewProjection;
float3 _CameraPos;
float _DensityScale;
float3 _BBoxMin;
float _StepSize;
float3 _BBoxMax;
uint _MaxSteps;
};
StructuredBuffer<uint> buf : register(t1);
struct VSInput
{
float3 position : POSITION;
float2 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float3 worldPos : TEXCOORD0;
float2 texcoord : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
PSInput MainVS(VSInput input)
{
PSInput output;
output.position = mul(_ViewProjection, float4(input.position, 1.0));
output.worldPos = input.position;
output.position = float4(input.position, 0.0, 1.0);
output.texcoord = input.texcoord;
float4 worldPosH = mul(_InverseViewProjection, float4(input.position, 0.5, 1.0));
output.worldPos = worldPosH.xyz / worldPosH.w;
return output;
}
bool intersectBox(float3 origin, float3 dir, float3 boxMin, float3 boxMax, out float tmin, out float tmax)
{
float3 invDir = 1.0 / dir;
float3 t1 = (boxMin - origin) * invDir;
float3 t2 = (boxMax - origin) * invDir;
tmin = max(max(min(t1.x, t2.x), min(t1.y, t2.y)), min(t1.z, t2.z));
tmax = min(min(max(t1.x, t2.x), max(t1.y, t2.y)), max(t1.z, t2.z));
return tmax >= tmin && tmax > 0;
}
float4 MainPS(PSInput input) : SV_TARGET
{
pnanovdb_grid_handle_t grid;
grid.address.byte_offset = 0;
float3 rayDir = normalize(input.worldPos - _CameraPos);
float3 worldBboxMin = float3(
(float)pnanovdb_grid_get_world_bbox(buf, grid, 0),
(float)pnanovdb_grid_get_world_bbox(buf, grid, 1),
(float)pnanovdb_grid_get_world_bbox(buf, grid, 2)
);
float3 worldBboxMax = float3(
(float)pnanovdb_grid_get_world_bbox(buf, grid, 3),
(float)pnanovdb_grid_get_world_bbox(buf, grid, 4),
(float)pnanovdb_grid_get_world_bbox(buf, grid, 5)
);
float tmin, tmax;
if (!intersectBox(_CameraPos, rayDir, _BBoxMin, _BBoxMax, tmin, tmax)) {
return float4(0, 0, 0, 0);
}
return float4(0.0, 1.0, 0.0, 1.0);
tmin = max(0, tmin);
float3 color = float3(0, 0, 0);
float alpha = 0;
for (uint i = 0; i < _MaxSteps; i++) {
float t = tmin + i * _StepSize;
if (t > tmax || alpha > 0.99) break;
float3 pos = _CameraPos + rayDir * t;
float density = 0.1;
color += density * float3(0.5, 0.7, 1.0) * (1 - alpha);
alpha += density * (1 - alpha);
}
return float4(color, alpha);
}

View File

@@ -112,6 +112,17 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
ID3D12Resource* cubeIBO = CreateBufferObject(commandList, cubeIndices, sizeof(cubeIndices), D3D12_RESOURCE_STATE_INDEX_BUFFER);
printf("Cube mesh created\n");
float quadVertices[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
};
uint32_t quadIndices[] = { 0, 1, 2, 0, 2, 3 };
ID3D12Resource* quadVBO = CreateBufferObject(commandList, quadVertices, sizeof(quadVertices), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
ID3D12Resource* quadIBO = CreateBufferObject(commandList, quadIndices, sizeof(quadIndices), D3D12_RESOURCE_STATE_INDEX_BUFFER);
printf("Quad mesh created\n");
StaticMeshComponent staticMeshComponent;
staticMeshComponent.InitFromFile(commandList, "Res/Model/Sphere.lhsm");
printf("Mesh loaded\n");
@@ -141,6 +152,13 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
printf("Volume PSO created: %p\n", volumePSO);
}
ID3D12PipelineState* quadPSO = CreateQuadPSO(volumeRootSignature, volumeVS, volumePS);
if (!quadPSO) {
printf("Quad PSO creation failed!\n");
} else {
printf("Quad PSO created: %p\n", quadPSO);
}
ID3D12Resource* volumeCB = CreateConstantBufferObject(65536);
ID3D12Resource* cb = CreateConstantBufferObject(65536);//1024x64(4x4)
@@ -253,29 +271,41 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
}
staticMeshComponent.Render(commandList);
DirectX::XMMATRIX bboxScale = DirectX::XMMatrixScaling(bboxSize[0], bboxSize[1], bboxSize[2]);
DirectX::XMMATRIX bboxTranslate = DirectX::XMMatrixTranslation(0.0f, 0.0f, 80.0f);
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);
DirectX::XMMATRIX viewProj = viewMatrix * projectionMatrix;
DirectX::XMMATRIX invViewProj = DirectX::XMMatrixInverse(nullptr, viewProj);
DirectX::XMFLOAT4X4 invViewProjMat;
DirectX::XMStoreFloat4x4(&invViewProjMat, invViewProj);
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;
UpdateConstantBuffer(volumeCB, volumeCBData, sizeof(float) * 20);
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[27] = 128;
UpdateConstantBuffer(volumeCB, volumeCBData, sizeof(float) * 28);
if (frameCount == 1) {
printf("Volume BBox: [%.2f, %.2f, %.2f] - [%.2f, %.2f, %.2f]\n",
volumeCBData[20], volumeCBData[21], volumeCBData[22],
volumeCBData[24], volumeCBData[25], volumeCBData[26]);
}
if (volumePSO) {
commandList->SetPipelineState(volumePSO);
if (quadPSO) {
commandList->SetPipelineState(quadPSO);
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);
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &D3D12_VERTEX_BUFFER_VIEW{ quadVBO->GetGPUVirtualAddress(), sizeof(quadVertices), sizeof(float) * 4 });
commandList->IASetIndexBuffer(&D3D12_INDEX_BUFFER_VIEW{ quadIBO->GetGPUVirtualAddress(), sizeof(quadIndices), DXGI_FORMAT_R32_UINT });
commandList->DrawIndexedInstanced(6, 1, 0, 0, 0);
}
EndRenderToSwapChain(commandList);