diff --git a/INSTRUCTIONS.md b/INSTRUCTIONS.md index b5d2924..676ee8f 100644 --- a/INSTRUCTIONS.md +++ b/INSTRUCTIONS.md @@ -222,7 +222,8 @@ In DXR, building the Acceleration Structure requires multiple steps: * The geometry descriptors you built will be passed in as acceleration structure inputs * You then query the *pre-build* info for these bottom-level AS. This will output 2 things: scratch size, and result data max size. Scratch size is like extra memory the driver needs to build the AS, and result data max is an upper bound for the size of the AS. You need to allocate 2 buffers for both of these. * Finally, you tell the command list that you want to build the acceleration structure using the scratch and result data allocated. -3. Create a function that builds **instances** of your BLAS. An instance of a BLAS is basically a BLAS but with a specific world-space transform. If you were to spawn multiple boxes in your scene, you would not create multiple box BLAS - you would create only one, but 4. Build the top-level acceleration structure. This is very similar to step (2) except now your inputs to the AS is the bottom-level AS. You will need to additionally call the function you created in step (3) to describe the instances that will be held by your TLAS. +3. Create a function that builds **instances** of your BLAS. An instance of a BLAS is basically a BLAS but with a specific world-space transform. If you were to spawn multiple boxes in your scene, you would not create multiple box BLAS - you would create only one, but +4. Build the top-level acceleration structure. This is very similar to step (2) except now your inputs to the AS is the bottom-level AS. You will need to additionally call the function you created in step (3) to describe the instances that will be held by your TLAS. Files to checkout: * DXR-AccelerationStructure.cpp diff --git a/README.md b/README.md index b0189d0..1201603 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5 - DirectX Procedural Raytracing** -* (TODO) YOUR NAME HERE - * (TODO) [LinkedIn](), [personal website](), [twitter](), etc. -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Taylor Nelms + * [LinkedIn](https://www.linkedin.com/in/taylor-k-7b2110191/), [twitter](https://twitter.com/nelms_taylor) +* Tested on: Windows 10, Intel i3 Coffee Lake 4-core 3.6GHz processor, 16GB RAM, NVidia GeForce GTX1650 4GB -### (TODO: Your README) +![Ray Tracer Output](img/working_raytracer.png) -Include screenshots, analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +### DXR Ray Tracer + +In this engine implementation, we make use of DirectX's ray tracing utilities to render a hardcoded scene of procedural geometries. By making use of the built-in acceleration structures for geometry traversal, we are able to render a fully ray-traced image at real-time speeds, a feat that is impossible with CPU-based implementations. + +### Performance + +With only a few geometries to work with, we can look at how the depth of the render engine affects performance overall. It turns out, it does not take too deep of a traversal to slow the performance down to a screeching halt. + +![Effect of Ray Depth on FPS](img/raydepthchart.png) diff --git a/img/Proj5Conceptual3.png b/img/Proj5Conceptual3.png new file mode 100644 index 0000000..13d8e5d Binary files /dev/null and b/img/Proj5Conceptual3.png differ diff --git a/img/raydepthchart.png b/img/raydepthchart.png new file mode 100644 index 0000000..b8abed0 Binary files /dev/null and b/img/raydepthchart.png differ diff --git a/img/working_raytracer.png b/img/working_raytracer.png new file mode 100644 index 0000000..e2705ae Binary files /dev/null and b/img/working_raytracer.png differ diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index 084077a..c7d626b 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -5,7 +5,7 @@ using namespace std; using namespace DX; -// LOOKAT-2.6, TODO-2.6: Build geometry descs for the bottom-level AS for the plane and for the procedural geometries in the scene. +// LOOKAT-2.6, TDO-2.6: Build geometry descs for the bottom-level AS for the plane and for the procedural geometries in the scene. // A bottom-level AS is a unique (or multiple) piece(s) of geometry, each for each type of object in your scene. // Example: if you had 2 boxes in your scene, then you would make 1 bottom level AS for 1 box // and then 2 different instances. @@ -25,12 +25,22 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress()) // The number of elements of a D3D12 resource can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width) auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0]; geometryDesc = {}; + + geometryDesc.Flags = geometryFlags; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index); + geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; + + geometryDesc.Triangles.VertexBuffer = { m_vertexBuffer.resource->GetGPUVirtualAddress(), 2 * sizeof(XMFLOAT3) }; + geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex); + geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; } @@ -41,19 +51,29 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + + primitiveType * geometryDesc.AABBs.AABBs.StrideInBytes; + geometryDesc.AABBs.AABBs.StartAddress = startAddress; + + } + } } -// TODO-2.6: Given the geometry and the geometry descriptors, build the bottom-level acceleration structures. +// TDO-2.6: Given the geometry and the geometry descriptors, build the bottom-level acceleration structures. AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vector& geometryDescs, D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags) { auto device = m_deviceResources->GetD3DDevice(); @@ -66,8 +86,14 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // The bottom-level inputs are the geometries you build in BuildGeometryDescsForBottomLevelAS() // Again, these tell the AS where the actual geometry data is and how it is laid out. - // TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout. + // TDO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs; + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bottomLevelInputs.Flags = buildFlags; + bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bottomLevelInputs.NumDescs = geometryDescs.size(); + bottomLevelInputs.pGeometryDescs = geometryDescs.data(); + // Query the driver for resource requirements to build an acceleration structure. We've done this for you. @@ -105,9 +131,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto AllocateUAVBuffer(device, bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes, &bottomLevelAS, initialResourceState, L"BottomLevelAccelerationStructure"); } - // TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc. + // TDO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc. // Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. + bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); + bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress(); // Fill up the command list with a command that tells the GPU how to build the bottom-level AS. @@ -115,6 +143,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto { // Set the descriptor heaps to be used during acceleration structure build for the Fallback Layer. ID3D12DescriptorHeap *pDescriptorHeaps[] = { m_descriptorHeap.Get() }; + auto numDescriptorHeaps = ARRAYSIZE(pDescriptorHeaps); m_fallbackCommandList->SetDescriptorHeaps(ARRAYSIZE(pDescriptorHeaps), pDescriptorHeaps); m_fallbackCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr); } @@ -123,14 +152,19 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto m_dxrCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr); } - // TODO-2.6: After we finished building the bottom-level AS, save all the info in + // TDO-2.6: After we finished building the bottom-level AS, save all the info in // the AccelerationStructureBuffers struct so the top-level AS can use it! // Don't forget that this is the return value. // Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h - return AccelerationStructureBuffers{}; + return AccelerationStructureBuffers{ + scratch, + bottomLevelAS, + nullptr,//"used only for top-level AS" SHOULD THIS BE NULL? + bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes + };//retval } -// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before. +// TDO-2.6: Build the instance descriptor for each bottom-level AS you built before. // An instance descriptor will contain information on how these bottom-level ASes will be instanciated in the scene. // Among other things, we will pass the world transformation matrix in the instance descriptor. // InstanceDescType will either be D3D12_RAYTRACING_INSTANCE_DESC for real DXR or D3D12_RAYTRACING_FALLBACK_INSTANCE_DESC for fallback layer. @@ -142,6 +176,7 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev vector instanceDescs; instanceDescs.resize(NUM_BLAS); + int hitGroupIndexOffset = 0; // Bottom-level AS for the plane instance. { @@ -156,9 +191,10 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev auto& instanceDesc = instanceDescs[BottomLevelASType::Triangle]; instanceDesc = {}; instanceDesc.InstanceMask = 1; - instanceDesc.InstanceContributionToHitGroupIndex = 0; + instanceDesc.InstanceContributionToHitGroupIndex = hitGroupIndexOffset; instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::Triangle]; + // Calculate transformation matrix. // We multiply the width by -0.5 in the x,z plane because we want the middle of the plane // (which is currently expanded in the positive x,z plane) to be centered. @@ -173,21 +209,36 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform); } - // TODO-2.6: Create instanced bottom-level AS with procedural geometry AABBs. + // TDO-2.6: Create instanced bottom-level AS with procedural geometry AABBs. // * Make sure to set InstanceContributionToHitGroupIndex to beyond the shader records for the triangle AABB. // For triangles, we have 1 shader record for radiance rays, and another for shadow rays. // Where do you think procedural shader records would start then? Hint: right after. // * Make each instance hover above the ground by ~ half its width { + //for the sake of typing not being garbage, this is a D3D12_RAYTRACING_INSTANCE_DESC + auto& instanceDesc = instanceDescs[BottomLevelASType::AABB]; + instanceDesc = {}; + instanceDesc.InstanceMask = 1; + instanceDesc.InstanceContributionToHitGroupIndex = BottomLevelASType::AABB * RayType::Count; + instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB]; + //make translation matrix, out of wishes and pretend + XMMATRIX mScale = XMMatrixScaling(c_aabbWidth, c_aabbWidth, c_aabbWidth); + XMMATRIX mTranslation = XMMatrixTranslation(0, c_aabbWidth * 0.5f, 0); + XMMATRIX mTransform = mScale * mTranslation; + + // Store the transform in the instanceDesc. + XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform); + + } // Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource. UINT64 bufferSize = static_cast(instanceDescs.size() * sizeof(instanceDescs[0])); - AllocateUploadBuffer(device, instanceDescs.data(), bufferSize, &(*instanceDescsResource), L"InstanceDescs"); + AllocateUploadBuffer(device, instanceDescs.data(), bufferSize, &(*instanceDescsResource), L"BLInstanceDescs"); }; -// TODO-2.6: Build the top-level acceleration structure. +// TDO-2.6: Build the top-level acceleration structure. // The top-level acceleration structure is a set of bottom-level *instances*. It is basically = scene. // This should be very similar to BuildBottomLevelAS() except now we have to add in the instances! AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count], D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags) @@ -199,9 +250,13 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = {}; - // TODO-2.6: fill in the topLevelInputs, read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS. + // TDO-2.6: fill in the topLevelInputs, read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS. // Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as a DescsLayout since we are using an array of bottom-level AS. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &topLevelInputs = topLevelBuildDesc.Inputs; + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + topLevelInputs.Flags = buildFlags; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + topLevelInputs.NumDescs = BottomLevelASType::Count; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; @@ -215,8 +270,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt } ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); - // TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data. - + // TDO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data. + AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource"); // Allocate space for the top-level AS. { @@ -230,15 +285,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt initialResourceState = D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE; } - // TODO-2.6: Allocate a UAV buffer for the actual top-level AS. - + // TDO-2.6: Allocate a UAV buffer for the actual top-level AS. + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &topLevelAS, initialResourceState, L"TopLevelAccelerationStructure"); } - // Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer: - // The primary point of divergence between the DXR API and the compute-based Fallback layer is the handling of GPU pointers. - // DXR fundamentally requires that GPUs be able to dynamically read from arbitrary addresses in GPU memory. - // The existing Direct Compute API today is more rigid than DXR and requires apps to explicitly inform the GPU what - // blocks of memory it will access with SRVs/UAVs. // In order to handle the requirements of DXR, the Fallback Layer uses the concept of Emulated GPU pointers, // which requires apps to create views around all memory they will access for raytracing, // but retains the DXR-like flexibility of only needing to bind the top level acceleration structure at DispatchRays. @@ -258,8 +308,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt CreateFallbackWrappedPointer(bottomLevelAS[1].accelerationStructure.Get(), static_cast(bottomLevelAS[1].ResultDataMaxSizeInBytes) / sizeof(UINT32)) }; - // TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + // TDO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above. + BuildBottomLevelASInstanceDescs< D3D12_RAYTRACING_FALLBACK_INSTANCE_DESC, WRAPPED_GPU_POINTER> + (bottomLevelASaddresses, &instanceDescsResource); + } else // DirectX Raytracing { @@ -270,7 +322,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt bottomLevelAS[1].accelerationStructure->GetGPUVirtualAddress() }; - // TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above. + // TDO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above. + BuildBottomLevelASInstanceDescs< D3D12_RAYTRACING_INSTANCE_DESC, D3D12_GPU_VIRTUAL_ADDRESS> + (bottomLevelASaddresses, &instanceDescsResource); } @@ -281,9 +335,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt m_fallbackTopLevelAccelerationStructurePointer = CreateFallbackWrappedPointer(topLevelAS.Get(), numBufferElements); } - // TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. + // TDO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. - + topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress(); + topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); // Build acceleration structure. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -298,31 +354,43 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt m_dxrCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr); } - // TODO-2.6: After we finished building the top-level AS, save all the info in the AccelerationStructureBuffers struct. + + + // TDO-2.6: After we finished building the top-level AS, save all the info in the AccelerationStructureBuffers struct. // Very similar to how you did this in BuildBottomLevelAS() except now you have to worry about topLevelASBuffers.instanceDesc. // Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h. // Make sure to return the topLevelASBuffers before you exit the function. - return AccelerationStructureBuffers{}; + return AccelerationStructureBuffers{ + scratch, + topLevelAS, + instanceDescsResource, + topLevelPrebuildInfo.ResultDataMaxSizeInBytes + }; } -// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene. +// TDO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene. void DXProceduralProject::BuildAccelerationStructures() { auto device = m_deviceResources->GetD3DDevice(); auto commandList = m_deviceResources->GetCommandList(); auto commandQueue = m_deviceResources->GetCommandQueue(); auto commandAllocator = m_deviceResources->GetCommandAllocator(); + auto buildFlags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;//WHO KNOWS? // Reset the command list for the acceleration structure construction. commandList->Reset(commandAllocator, nullptr); - // TODO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this. + // TDO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this. array, BottomLevelASType::Count> geometryDescs; + BuildGeometryDescsForBottomLevelAS(geometryDescs); - // TODO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS. + // TDO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS. // Hint: you filled in a function that does this. AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count]; + for (int bottomLevelASType = 0; bottomLevelASType < BottomLevelASType::Count; bottomLevelASType++) { + bottomLevelAS[bottomLevelASType] = BuildBottomLevelAS(geometryDescs[bottomLevelASType], buildFlags); + } // Batch all resource barriers for bottom-level AS builds. @@ -334,8 +402,8 @@ void DXProceduralProject::BuildAccelerationStructures() } commandList->ResourceBarrier(BottomLevelASType::Count, resourceBarriers); - // TODO-2.6: Build top-level AS. Hint, you already made a function that does this. - AccelerationStructureBuffers topLevelAS; + // TDO-2.6: Build top-level AS. Hint, you already made a function that does this. + AccelerationStructureBuffers topLevelAS = BuildTopLevelAS(bottomLevelAS, buildFlags); // Kick off acceleration structure construction. @@ -344,8 +412,12 @@ void DXProceduralProject::BuildAccelerationStructures() // Wait for GPU to finish as the locally created temporary GPU resources will get released once we go out of scope. m_deviceResources->WaitForGpu(); - // TODO-2.6: Store the AS buffers. The rest of the buffers will be released once we exit the function. + // TDO-2.6: Store the AS buffers. The rest of the buffers will be released once we exit the function. // Do this for both the bottom-level and the top-level AS. Consider re-reading the DXProceduralProject class // to find what member variables should be set. + for (int bottomLevelASType = 0; bottomLevelASType < BottomLevelASType::Count; bottomLevelASType++) { + m_bottomLevelAS[bottomLevelASType] = bottomLevelAS[bottomLevelASType].accelerationStructure; + } + m_topLevelAS = topLevelAS.accelerationStructure; } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Common.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Common.cpp index 41c60ae..5e962fe 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Common.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Common.cpp @@ -57,12 +57,19 @@ void DXProceduralProject::AllocateUAVBuffer(ID3D12Device* pDevice, UINT64 buffer UINT DXProceduralProject::AllocateDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse) { auto descriptorHeapCpuBase = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart(); - if (descriptorIndexToUse >= m_descriptorHeap->GetDesc().NumDescriptors) + auto descHeapDesc = m_descriptorHeap->GetDesc(); + UINT numDescriptors = descHeapDesc.NumDescriptors; + if (descriptorIndexToUse >= numDescriptors) +// if (descriptorIndexToUse >= m_descriptorHeap->GetDesc().NumDescriptors) { - ThrowIfFalse(m_descriptorsAllocated < m_descriptorHeap->GetDesc().NumDescriptors, L"Ran out of descriptors on the heap!"); + ThrowIfFalse(m_descriptorsAllocated < numDescriptors, L"Ran out of descriptors on the heap!\n"); +// ThrowIfFalse(m_descriptorsAllocated < m_descriptorHeap->GetDesc().NumDescriptors, L"Ran out of descriptors on the heap!\n"); descriptorIndexToUse = m_descriptorsAllocated++; } *cpuDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE(descriptorHeapCpuBase, descriptorIndexToUse, m_descriptorSize); + //char buffer[50]; + //sprintf_s(buffer, "Allocated for index %d\n", descriptorIndexToUse); + //OutputDebugStringA(buffer); return descriptorIndexToUse; } diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index 03a8c58..6189a68 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -21,8 +21,10 @@ void DXProceduralProject::DoRaytracing() m_sceneCB.CopyStagingToGpu(frameIndex); commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex)); - // TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer - + // TDO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer + m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex); + //commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex)); + commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress()); // Bind the descriptor heaps. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -45,27 +47,36 @@ void DXProceduralProject::DoRaytracing() commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AccelerationStructure, m_topLevelAS->GetGPUVirtualAddress()); } - // TODO-2.8: Bind the Index/Vertex buffer (basically m_indexBuffer. Think about why this isn't m_vertexBuffer too. Hint: CreateRootSignatures() in DXR-Pipeline.cpp.) + // TDO-2.8: Bind the Index/Vertex buffer (basically m_indexBuffer. Think about why this isn't m_vertexBuffer too. Hint: CreateRootSignatures() in DXR-Pipeline.cpp.) // This should be done by telling the commandList to SetComputeRoot*(). You just have to figure out what * is. // Example: in the case of GlobalRootSignature::Slot::SceneConstant above, we used SetComputeRootConstantBufferView() // Hint: look at CreateRootSignatures() in DXR-Pipeline.cpp. + commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::VertexBuffers, m_indexBuffer.gpuDescriptorHandle); - // TODO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer. - + // TDO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer. + commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor); // This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor // This will set the hooks using the shader tables built before and call DispatchRays on the command list auto DispatchRays = [&](auto* raytracingCommandList, auto* stateObject, auto* dispatchDesc) { // You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc). - // TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE - - - // TODO-2.8: now fill in dispatchDesc->MissShaderTable + // TDO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE + //dispatchDesc->HitGroupTable = { m_hitGroupShaderTable->GetGPUVirtualAddress(), m_hitGroupShaderTableStrideInBytes }; + dispatchDesc->HitGroupTable = { m_hitGroupShaderTable->GetGPUVirtualAddress(), + m_hitGroupShaderTable->GetDesc().Width, + m_hitGroupShaderTableStrideInBytes}; + + // TDO-2.8: now fill in dispatchDesc->MissShaderTable + dispatchDesc->MissShaderTable = { m_missShaderTable->GetGPUVirtualAddress(), + m_missShaderTable->GetDesc().Width, + m_missShaderTableStrideInBytes }; - // TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord + // TDO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord + dispatchDesc->RayGenerationShaderRecord = { m_rayGenShaderTable->GetGPUVirtualAddress(), + m_rayGenShaderTable->GetDesc().Width }; // We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA! diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp index e3ff63c..926756d 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -111,7 +111,10 @@ void DXProceduralProject::CreateConstantBuffers() // structured buffers are for structs that have dynamic data (e.g lights in a scene, or AABBs in this case) void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers() { + auto device = m_deviceResources->GetD3DDevice(); + auto num_objects = m_aabbs.size(); + m_aabbPrimitiveAttributeBuffer.Create(device, num_objects, 1, L"AABB Primitive Attribute Buffer"); } // LOOKAT-2.1: Update camera matrices stored in m_sceneCB. @@ -164,6 +167,11 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime) // You can infer what the bottom level AS space to local space transform should be. // The intersection shader tests in this project work with local space, but the geometries are provided in bottom level // AS space. So this data will be used to convert back and forth from these spaces. + auto transformMatrix = XMMatrixMultiply(mScale, mRotation); + transformMatrix = XMMatrixMultiply(transformMatrix, mTranslation); + auto invTransformMatrix = XMMatrixInverse(nullptr, transformMatrix); + m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = transformMatrix; + m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = invTransformMatrix; }; UINT offset = 0; diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp index 9d93504..58995b3 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp @@ -54,12 +54,13 @@ void DXProceduralProject::BuildPlaneGeometry() ThrowIfFalse(descriptorIndexVB == descriptorIndexIB + 1, L"Vertex Buffer descriptor index must follow that of Index Buffer descriptor index"); } -// TODO-2.5: Build AABBs for procedural geometry that will be used within the acceleration structure. +// TDO-2.5: Build AABBs for procedural geometry that will be used within the acceleration structure. void DXProceduralProject::BuildProceduralGeometryAABBs() { auto device = m_deviceResources->GetD3DDevice(); // Set up AABBs on a grid. + // But fucking why though? It seems *super* arbitrary { // 9x3 slots = 9 slots. Note that one procedural geometry can take up multiple slots. // You could have a small sphere that takes up 1 slot, and another that is giant and takes up 4 slots. @@ -71,51 +72,72 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() // The division by 2 centers the grid. The base position is still at the -x,-z corner. const XMFLOAT3 basePosition = { - -(aabbGrid.x * c_aabbWidth + (aabbGrid.x - 1) * c_aabbDistance) / 2.0f, - -(aabbGrid.y * c_aabbWidth + (aabbGrid.y - 1) * c_aabbDistance) / 2.0f, - -(aabbGrid.z * c_aabbWidth + (aabbGrid.z - 1) * c_aabbDistance) / 2.0f, + -(aabbGrid.x * c_aabbWidth + (aabbGrid.x - 1) * c_aabbDistance) / 2.0f,//-(6+4)/2=-5 + -(aabbGrid.y * c_aabbWidth + (aabbGrid.y - 1) * c_aabbDistance) / 2.0f,//-(2+0)/2=-1 + -(aabbGrid.z * c_aabbWidth + (aabbGrid.z - 1) * c_aabbDistance) / 2.0f,//-(6+4)/2=-5 }; // The stride is "how much to move" for the next slot. This is basically the size of a slot + it's separation from its direct // neighbor - XMFLOAT3 stride = XMFLOAT3(c_aabbWidth + c_aabbDistance, c_aabbWidth + c_aabbDistance, c_aabbWidth + c_aabbDistance); + XMFLOAT3 stride = XMFLOAT3(c_aabbWidth + c_aabbDistance, c_aabbWidth + c_aabbDistance, c_aabbWidth + c_aabbDistance);//(4, 4, 4) - // TODO-2.5: Lookup the DXR API for the D3D12_RAYTRACING_AABB struct and fill up this lamda function that creates + // TDO-2.5: Lookup the DXR API for the D3D12_RAYTRACING_AABB struct and fill up this lamda function that creates // and returns an D3D12_RAYTRACING_AABB for you. // Note that you are only filling an axis-aligned bounding box. // This should take into account the basePosition and the stride defined above. - auto InitializeAABB = [&](auto& offsetIndex, auto& size) + auto InitializeAABB = [&](XMFLOAT3& offsetIndex, auto& size) { D3D12_RAYTRACING_AABB aabb{}; + //center coordinate for our (0, 0) index point + XMFLOAT3 baseCenter = { basePosition.x + c_aabbWidth / 2.0f, + basePosition.y + c_aabbWidth / 2.0f, + basePosition.z + c_aabbWidth / 2.0f}; + XMFLOAT3 myCenter = baseCenter;// + offsetIndex * stride + myCenter.x += stride.x * ((aabbGrid.x - 1) / 2.0f + offsetIndex.x); + myCenter.y += stride.y * ((aabbGrid.y - 1) / 2.0f + offsetIndex.y); + myCenter.z += stride.z * ((aabbGrid.z - 1) / 2.0f + offsetIndex.z); + aabb.MaxX = myCenter.x + size.x / 2.0f; + aabb.MaxY = myCenter.y + size.y / 2.0f; + aabb.MaxZ = myCenter.z + size.z / 2.0f; + aabb.MinX = myCenter.x - size.x / 2.0f; + aabb.MinY = myCenter.y - size.y / 2.0f; + aabb.MinZ = myCenter.z - size.z / 2.0f; + char buffer[300]; + sprintf_s(buffer, "AABB:\tX[%f,%f]\tY[%f,%f]\tZ[%f,%f]\n", aabb.MinX, aabb.MaxX, aabb.MinY, aabb.MaxY, aabb.MinZ, aabb.MaxZ); + OutputDebugStringA(buffer); + return aabb; }; m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount); UINT offset = 0; + //THIS IS WHERE WE ARE HARDCODING SOME GEOMETRY BECAUSE WHY WOULDN'T WE I GUESS // Analytic primitives. { using namespace AnalyticPrimitive; m_aabbs[offset + AABB] = InitializeAABB(XMFLOAT3(0.5f, 0.0f, 0.0f), XMFLOAT3(2.0f, 3.0f, 2.0f)); - m_aabbs[offset + Spheres] = InitializeAABB(XMFLOAT3(1.0f, 0.75f, -0.5f), XMFLOAT3(3, 3, 3)); + m_aabbs[offset + Spheres] = InitializeAABB(XMFLOAT3(1.0f, 0.5f, -0.5f), XMFLOAT3(3.0f, 3.0f, 3.0f)); offset += AnalyticPrimitive::Count; } // Volumetric primitives. { using namespace VolumetricPrimitive; - m_aabbs[offset + Metaballs] = InitializeAABB(XMINT3(-1, 0, 0), XMFLOAT3(6, 6, 6)); + m_aabbs[offset + Metaballs] = InitializeAABB(XMFLOAT3(-1.0f, 0.5f, 1.0f), XMFLOAT3(6.0f, 6.0f, 6.0f)); offset += VolumetricPrimitive::Count; } - // TODO-2.5: Allocate an upload buffer for this AABB data. + // TDO-2.5: Allocate an upload buffer for this AABB data. // The base data lives in m_aabbs.data() (the stuff you filled in!), but the allocationg should be pointed // towards m_aabbBuffer.resource (the actual D3D12 resource that will hold all of our AABB data as a contiguous buffer). - + AllocateUploadBuffer(device, m_aabbs.data(), m_aabbs.size() * sizeof(m_aabbs[0]), &m_aabbBuffer.resource); + } } -// TODO-2.5: Build geometry used in the project. As easy as calling both functions above :) +// TDO-2.5: Build geometry used in the project. As easy as calling both functions above :) void DXProceduralProject::BuildGeometry() { - + BuildPlaneGeometry(); + BuildProceduralGeometryAABBs(); } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp index 33899bd..6a338c2 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp @@ -1,8 +1,8 @@ -#include "stdafx.h" + #include "stdafx.h" #include "DXProceduralProject.h" #include "CompiledShaders\Raytracing.hlsl.h" -// LOOKAT-2.3, TODO-2.3: Create the hitgroup pipeline subobject. +// LOOKAT-2.3, TDO-2.3: Create the hitgroup pipeline subobject. // A hitgroup specifies closest hit (mandatory), any hit (optional) and intersection shaders (mandatory but not for triangles). // They are executed when a ray intersects the geometry. // See how we do it for 1 triangle, and apply that logic to an AABB. @@ -27,13 +27,28 @@ void DXProceduralProject::CreateHitGroupSubobjects(CD3D12_STATE_OBJECT_DESC* ray } } - // TODO-2.3: AABB geometry hit groups. Very similar to triangles, except now you have to *also* loop over the primitive types. + // TDO-2.3: AABB geometry hit groups. Very similar to triangles, except now you have to *also* loop over the primitive types. { + for (UINT rayType = 0; rayType < RayType::Count; rayType++) + { + for (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) { + auto hitGroup = raytracingPipeline->CreateSubobject(); + if (rayType == RayType::Radiance) + { + hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]); + // We import the closest hit shader name + } + hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primitiveType]); + // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type + hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primitiveType][rayType]); + hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE); + } + } } } -// TODO-2.3: Local root signature and shader association (linking) +// TDO-2.3: Local root signature and shader association (linking) void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT_DESC* raytracingPipeline) { // Ray gen and miss shaders in this project are not using a local root signature and thus one is not associated with them. @@ -51,9 +66,17 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT rootSignatureAssociation->AddExports(c_hitGroupNames_TriangleGeometry); } - // TODO-2.3: AABB geometry hitgroup/local root signature association. + // TDO-2.3: AABB geometry hitgroup/local root signature association. // Very similar to triangles, except now one for each primitive type. { - + auto localRootSignature = raytracingPipeline->CreateSubobject(); + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB].Get()); + + auto rootSignatureAssociation = raytracingPipeline->CreateSubobject(); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[IntersectionShaderType::AnalyticPrimitive]); + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[IntersectionShaderType::VolumetricPrimitive]); + + } } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp index f889597..100fd3e 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Other.cpp @@ -2,6 +2,14 @@ #include "DXProceduralProject.h" #include "CompiledShaders\Raytracing.hlsl.h" +#ifndef CAPTURING +#define CAPTURING 0 +#endif + +#if CAPTURING +#include +#endif + // LOOKAT-1.8.3: This file contains pretty much everything else we decided was not too important. Feel free to explore what's going on here though. using namespace std; @@ -55,6 +63,11 @@ void DXProceduralProject::EnableDirectXRaytracing(IDXGIAdapter1* adapter) // Create resources that depend on the device. void DXProceduralProject::CreateDeviceDependentResources() { +#if CAPTURING + ComPtr ga; + HRESULT hr = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&ga)); + ga->BeginCapture(); +#endif CreateAuxilaryDeviceResources(); // Create raytracing interfaces: raytracing device and commandlist. @@ -86,6 +99,9 @@ void DXProceduralProject::CreateDeviceDependentResources() // Create an output 2D texture to store the raytracing result to. CreateRaytracingOutputResource(); +#if CAPTURING + ga->EndCapture(); +#endif } // Selects the RTX API to use and tells the device to create a root signature given the descriptor. diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp index c9501ac..fbcdc05 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp @@ -2,7 +2,7 @@ #include "DXProceduralProject.h" #include "CompiledShaders\Raytracing.hlsl.h" -// LOOKAT-2.4, TODO-2.4: Fill in the Raytracing Pipeline State Object (RTPSO). +// LOOKAT-2.4, TDO-2.4: Fill in the Raytracing Pipeline State Object (RTPSO). // An RTPSO represents a full set of shaders reachable by a DispatchRays() call, with all configuration options resolved, // such as local signatures and other state. void DXProceduralProject::CreateRaytracingPipelineStateObject() @@ -19,7 +19,7 @@ void DXProceduralProject::CreateRaytracingPipelineStateObject() // DXIL library CreateDxilLibrarySubobject(&raytracingPipeline); - // TODO-2.4: Hit groups. Call the function you filled in in DXR-HitGroup.cpp. + // TDO-2.4: Hit groups. Call the function you filled in in DXR-HitGroup.cpp. CreateHitGroupSubobjects(&raytracingPipeline); // Shader config: defines the maximum sizes in bytes for the ray rayPayload and attribute structure. @@ -28,7 +28,7 @@ void DXProceduralProject::CreateRaytracingPipelineStateObject() UINT attributeSize = sizeof(struct ProceduralPrimitiveAttributes); shaderConfig->Config(payloadSize, attributeSize); - // TODO-2.4: Local root signature and shader association. Call the other function you did in in DXR-HitGroup.cpp. + // TDO-2.4: Local root signature and shader association. Call the other function you did in in DXR-HitGroup.cpp. CreateLocalRootSignatureSubobjects(&raytracingPipeline); // Global root signature @@ -52,4 +52,5 @@ void DXProceduralProject::CreateRaytracingPipelineStateObject() { ThrowIfFailed(m_dxrDevice->CreateStateObject(raytracingPipeline, IID_PPV_ARGS(&m_dxrStateObject)), L"Couldn't create DirectX Raytracing state object.\n"); } + } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp index 2dff8b5..dd76dd3 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp @@ -4,7 +4,7 @@ // LOOKAT-2.2: Read about root signatures here https://docs.microsoft.com/en-us/windows/win32/direct3d12/root-signatures-overview -// TODO-2.2: Create parts of the shader root signature. +// TDO-2.2: Create parts of the shader root signature. void DXProceduralProject::CreateRootSignatures() { auto device = m_deviceResources->GetD3DDevice(); @@ -19,11 +19,12 @@ void DXProceduralProject::CreateRootSignatures() CD3DX12_DESCRIPTOR_RANGE ranges[2]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture - // TODO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data. + // TDO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data. // This will effectively put the indices at register 1, and the vertices at register 2. + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 1); - // TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots. + // TDO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots. // * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are. // - The OutputView should correspond to the UAV range descriptor above (descriptor table), bound to register 0 of the UAV registers. // - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers. @@ -39,6 +40,16 @@ void DXProceduralProject::CreateRootSignatures() // t registers --> SRV // b registers --> CBV CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count]; + //g_renderTarget : register(u0); + rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &(ranges[0]), D3D12_SHADER_VISIBILITY_ALL); + //g_indices : register(t1, space0);g_vertices : register(t2, space0); + rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &(ranges[1]), D3D12_SHADER_VISIBILITY_ALL); + //g_scene : register(t0, space0) + rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0); + //g_sceneCB : register(b0); + rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0); + //g_AABBPrimitiveAttributes : register(t3, space0); + rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3); // Finally, we bundle up all the descriptors you filled up and tell the device to create this global root signature! CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); @@ -60,14 +71,21 @@ void DXProceduralProject::CreateRootSignatures() SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::Triangle]); } - // TODO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature + // TDO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature // - Remember that the AABB holds 1 slot for Material Constants, and another 1 for the geometry instance. // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means. // - Use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps // to register 1, this overlap is allowed since we are talking about *local* root signatures // --> the values they hold will depend on the shader function the local signature is bound to! { - + namespace RootSignatureSlots = LocalRootSignature::AABB::Slot; + CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count]; + rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1); + rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 2); + + CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB]); } } } diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index 150e92d..84d1eff 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -32,8 +32,10 @@ void DXProceduralProject::BuildShaderTables() // TODO-2.7: Miss shaders. // Similar to the raygen shader, but now we have 1 for each ray type (radiance, shadow) // Don't forget to update shaderIdToStringMap. - missShaderIDs[0] = nullptr; - missShaderIDs[1] = nullptr; + missShaderIDs[0] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[0]); + shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0]; + missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]); + shaderIdToStringMap[missShaderIDs[1]] = c_missShaderNames[1]; // Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray. for (UINT i = 0; i < RayType::Count; i++) @@ -42,9 +44,17 @@ void DXProceduralProject::BuildShaderTables() shaderIdToStringMap[hitGroupShaderIDs_TriangleGeometry[i]] = c_hitGroupNames_TriangleGeometry[i]; } - // TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. + // TDO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. + for (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) { + for (UINT rayType = 0; rayType < RayType::Count; rayType++) { + hitGroupShaderIDs_AABBGeometry[primitiveType][rayType] = + stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[primitiveType][rayType]); + shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[primitiveType][rayType]] + = c_hitGroupNames_AABBGeometry[primitiveType][rayType]; + }//for ray + }//for primitive - }; + };//getShaderIDs // Get shader identifiers using the lambda function defined above. UINT shaderIDSize; @@ -92,10 +102,20 @@ void DXProceduralProject::BuildShaderTables() m_rayGenShaderTable = rayGenShaderTable.GetResource(); } - // TODO-2.7: Miss shader table. Very similar to the RayGen table except now we push_back() 2 shader records + // TDO-2.7: Miss shader table. Very similar to the RayGen table except now we push_back() 2 shader records // 1 for the radiance ray, 1 for the shadow ray. Don't forget to call DebugPrint() on the table for your sanity! { - + UINT numShaderRecords = 2; + UINT shaderRecordSize = shaderIDSize; + + ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable"); + + missShaderTable.push_back(ShaderRecord(missShaderIDs[0], shaderRecordSize, nullptr, 0)); + missShaderTable.push_back(ShaderRecord(missShaderIDs[1], shaderRecordSize, nullptr, 0)); + + missShaderTable.DebugPrint(shaderIdToStringMap); + m_missShaderTable = missShaderTable.GetResource(); + m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize(); } // Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature. @@ -133,17 +153,18 @@ void DXProceduralProject::BuildShaderTables() // in DXR-Pipeline.cpp. So if you did AABB, then Sphere, then Metaballs, then follow that order. // the primitive type is used to tell the shader what type of procedural geometry this is. // Remember that hitGroupShaderIDs_AABBGeometry is a 2-array indexed like so [type of geometry][ray type] + //TODO: investigate if this is actually TODO, or if this was just magically not erased { LocalRootSignature::AABB::RootArguments rootArgs; UINT instanceIndex = 0; // Create a shader record for each primitive. - for (UINT iShader = 0, instanceIndex = 0; iShader < IntersectionShaderType::Count; iShader++) + for (UINT iShader = 0, instanceIndex = 0; iShader < IntersectionShaderType::Count; iShader++)//2, one for each primitive type { UINT numPrimitiveTypes = IntersectionShaderType::PerPrimitiveTypeCount(static_cast(iShader)); // Primitives for each intersection shader. - for (UINT primitiveIndex = 0; primitiveIndex < numPrimitiveTypes; primitiveIndex++) + for (UINT primitiveIndex = 0; primitiveIndex < numPrimitiveTypes; primitiveIndex++) //this is 2 (cube, sphere) or 1 (metaball) { rootArgs.materialCb = m_aabbMaterialCB[instanceIndex]; rootArgs.aabbCB.instanceIndex = instanceIndex; diff --git a/src/D3D12RaytracingProceduralGeometry/Main.cpp b/src/D3D12RaytracingProceduralGeometry/Main.cpp index 7f70bc6..6bbb443 100644 --- a/src/D3D12RaytracingProceduralGeometry/Main.cpp +++ b/src/D3D12RaytracingProceduralGeometry/Main.cpp @@ -16,7 +16,7 @@ #include "stdafx.h" #include "DXProceduralProject.h" -#define CPU_CODE_COMPLETE 0 +#define CPU_CODE_COMPLETE 1 _Use_decl_annotations_ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl index d066933..66f06d9 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -41,7 +41,9 @@ ConstantBuffer l_aabbCB: register(b2); // other // Remember to clamp the dot product term! float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal) { - return 0.0f; + float factor = dot(incidentLightRay, normal); + factor = min(max(factor, 0.0f), 1.0f); + return factor; } // TODO-3.6: Phong lighting specular component. @@ -49,9 +51,14 @@ float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal) // HINT: Consider using built-in DirectX functions to find the reflected ray. Remember that a reflected ray is reflected // with respect to the normal of the hit position. // Remember to normalize the reflected ray, and to clamp the dot product term -float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower) +float CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower) { - return float4(0.0f, 0.0f, 0.0f, 0.0f); + float3 reflectDirection = normalize(reflect(incidentLightRay, normal)); + float3 reverseRayDirection = -WorldRayDirection(); + float dotp = dot(reflectDirection, reverseRayDirection); + dotp = min(max(dotp, 0.0f), 1.0f); + float retval = pow(dotp, specularPower); + return retval; } // TODO-3.6: Phong lighting model = ambient + diffuse + specular components. @@ -68,6 +75,15 @@ float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInShadow, in float diffuseCoef = 1.0, in float specularCoef = 1.0, in float specularPower = 50) { + if (isInShadow){ + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + float3 hitPos = HitWorldPosition(); + float3 lightPos = g_sceneCB.lightPosition.xyz; + float3 incident = normalize(hitPos - lightPos); + + // Ambient component // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit float4 ambientColor = g_sceneCB.lightAmbientColor; @@ -76,7 +92,13 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh float a = 1 - saturate(dot(normal, float3(0, -1, 0))); ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a); - return ambientColor; + float4 specularColor = float4(1.0f, 1.0f, 1.0f, 1.0f) * CalculateSpecularCoefficient(incident, normal, specularPower); + float4 diffuseColor = albedo * diffuseCoef * CalculateDiffuseCoefficient(incident, normal); + + float4 result = ambientColor + specularColor + diffuseColor; + + return result; + } //*************************************************************************** @@ -135,23 +157,49 @@ float4 TraceRadianceRay(in Ray ray, in UINT currentRayRecursionDepth) // Hint 2: remember what the ShadowRay payload looks like. See RaytracingHlslCompat.h bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) { - return false; + + if (currentRayRecursionDepth >= MAX_RAY_RECURSION_DEPTH) + { + return false; + } + RayDesc rayDesc; + rayDesc.Origin = ray.origin; + rayDesc.Direction = ray.direction; + rayDesc.TMin = 0; + rayDesc.TMax = 10000; + + ShadowRayPayload rayPayload; + rayPayload.hit = true; + + RAY_FLAG shadowFlags = RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | RAY_FLAG_FORCE_OPAQUE; + + TraceRay(g_scene, + shadowFlags, + TraceRayParameters::InstanceMask, + TraceRayParameters::HitGroup::Offset[RayType::Shadow],//1 + TraceRayParameters::HitGroup::GeometryStride, + TraceRayParameters::MissShader::Offset[RayType::Shadow],//1 + rayDesc, rayPayload); + return rayPayload.hit; + } //*************************************************************************** //********************------ Ray gen shader -------************************** //*************************************************************************** -// TODO-3.1: Complete the ray generation shader. +// TDO-3.1: Complete the ray generation shader. // (1) Generate a ray using the function GenerateCameraRay() in RaytracingShaderHelper.hlsli // (2) Trace a radiance ray using the generated ray to obtain a color // (3) Write that color to the render target [shader("raygeneration")] void MyRaygenShader() { - + Ray cameraRay = GenerateCameraRay(uint2(DispatchRaysIndex().xy), g_sceneCB.cameraPosition, g_sceneCB.projectionToWorld); + float4 radianceResult = TraceRadianceRay(cameraRay, 0); + // Write the color to the render target - g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f); + g_renderTarget[DispatchRaysIndex().xy] = radianceResult; } //*************************************************************************** @@ -209,6 +257,9 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is. // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color. // When t is big, we want the background color to be more pronounced. + //TODO: apply visibility falloff + float t = RayTCurrent(); + color = lerp(color, BackgroundColor, 1.0 - exp(-0.0000001*t*t*t)); rayPayload.color = color; } @@ -227,27 +278,57 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle [shader("closesthit")] void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr) { + float3 geoNormal = attr.normal; + float3 hitPosition = HitWorldPosition(); + + Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) }; + bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth); + + // Reflected component ray. + float4 reflectedColor = float4(0, 0, 0, 0); + if (l_materialCB.reflectanceCoef > 0.001 ) + { + // Trace a reflection ray from the intersection points using Snell's law. The reflect() HLSL built-in function does this for you! + // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions + Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), geoNormal) }; + float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth); + + float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), geoNormal, l_materialCB.albedo.xyz); + reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor; + } + // Calculate final color. + float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, geoNormal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower); + float4 color = (phongColor + reflectedColor); + + + //TODO: apply visibility falloff + float t = RayTCurrent(); + color = lerp(color, BackgroundColor, 1.0 - exp(-0.0000001*t*t)); + + rayPayload.color = color; } //*************************************************************************** //**********************------ Miss shaders -------************************** //*************************************************************************** -// TODO-3.3: Complete the Radiance ray miss shader. What color should you output if you hit no geometry? +// TDO-3.3: Complete the Radiance ray miss shader. What color should you output if you hit no geometry? // Make sure you edit the rayPayload so your color gets passed down to other shaders. // NOTE: whether we missed a Triangle or a Procedural Geometry does not matter. The miss output should be the same! [shader("miss")] void MyMissShader(inout RayPayload rayPayload) { + rayPayload.color = float4(BackgroundColor); } -// TODO-3.3: Complete the Shadow ray miss shader. Is this ray a shadow ray if it hit nothing? +// TDO-3.3: Complete the Shadow ray miss shader. Is this ray a shadow ray if it hit nothing? [shader("miss")] void MyMissShader_ShadowRay(inout ShadowRayPayload rayPayload) { - + rayPayload.hit = false; + return; } //*************************************************************************** @@ -299,6 +380,22 @@ void MyIntersectionShader_AnalyticPrimitive() [shader("intersection")] void MyIntersectionShader_VolumetricPrimitive() { + Ray localRay = GetRayInAABBPrimitiveLocalSpace(); + float thit; + ProceduralPrimitiveAttributes attr; + + + if (RayMetaballsIntersectionTest(localRay, thit, attr, g_sceneCB.elapsedTime)) + { + PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex]; + + // Make sure the normals are stored in BLAS space and not the local space + attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS); + attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal)); + + // thit is invariant to the space transformation + ReportHit(thit, /*hitKind*/ 0, attr); + } } #endif // RAYTRACING_HLSL \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli index 94bf5cc..9de8616 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli @@ -68,7 +68,12 @@ bool is_a_valid_hit(in Ray ray, in float thit, in float3 hitSurfaceNormal) // (3) Call the hlsl built-in function smoothstep() on this interpolant to smooth it out so it doesn't change abruptly. float CalculateAnimationInterpolant(in float elapsedTime, in float cycleDuration) { - return smoothstep(0, 1, 0); + float ratio = elapsedTime / cycleDuration; + float cyclePoint = ratio - (int)ratio; + if (cyclePoint <= 0.5f) + return smoothstep(0, 1, 2.0f * cyclePoint); + else + return smoothstep(0, 1, 2.0f * (1.0f - cyclePoint)); } // Load three 2-byte indices from a ByteAddressBuffer. @@ -119,7 +124,7 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]); } -// TODO-3.1: Generate a ray in world space for a camera pixel corresponding to a dispatch index (analogous to a thread index in CUDA). +// TDO-3.1: Generate a ray in world space for a camera pixel corresponding to a dispatch index (analogous to a thread index in CUDA). // Check out https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-12-raytracing-hlsl-system-value-intrinsics to see interesting // intrinsic HLSL raytracing functions you may use. // Remember that you are given the pixel coordinates from index. You need to convert this to normalized-device coordinates first. @@ -130,8 +135,20 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld) { Ray ray; - ray.origin = float3(0.0f, 0.0f, 0.0f); - ray.direction = normalize(float3(0.0f, 0.0f, 0.0f)); + uint3 whd = DispatchRaysDimensions(); + float2 normIndex = { index.x / (1.0f * whd.x), index.y / (1.0f * whd.y) };//top left: (0,0), bottom right: (1.0, 1.0) + normIndex.y = 1.0 - normIndex.y;//bottom left: (0, 0), top right: (1, 1) + normIndex = float2( (normIndex.x - 0.5f) * 2.0f, (normIndex.y - 0.5f) * 2.0f );//bottom left: (-1, -1), top right: (1, 1) + + ray.origin = cameraPosition; + ray.direction = float3(normIndex.x, normIndex.y, 1.0f); //not sure about the 1.0 + + float4 rayDirectionHomogenous = float4(ray.direction, 1.0f); + float4 rayDirectionTransformed = mul(rayDirectionHomogenous, projectionToWorld); + //float4 rayDirectionTransformed = mul(projectionToWorld, rayDirectionHomogenous); + ray.direction = float3(rayDirectionTransformed.x, rayDirectionTransformed.y, rayDirectionTransformed.z); + ray.direction = normalize(ray.direction); + return ray; } @@ -141,7 +158,9 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 // f0 is usually the albedo of the material assuming the outside environment is air. float3 FresnelReflectanceSchlick(in float3 I, in float3 N, in float3 f0) { - return f0; + float costheta = min(max(dot(I, N), 0.0f), 1.0f); + float factor = pow(1.0f - costheta, 5); + return factor * f0; } #endif // RAYTRACINGSHADERHELPER_H \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 31a9444..a4f5c23 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -6,6 +6,10 @@ #include "RaytracingShaderHelper.hlsli" +#define NUMSTEPS 128 +#define THRESH 0.15f +#define CYCLETIME 5.0f + // LOOKAT-1.9.4: Shockingly, a metaball is just a sphere! struct Metaball { @@ -13,7 +17,7 @@ struct Metaball float radius; }; -// TODO-3.4.2: Calculate a magnitude of an influence from a Metaball charge. +// TDO-3.4.2: Calculate a magnitude of an influence from a Metaball charge. // This function should return a metaball potential, which is a float in range [0,1]. // 1) If the point is at the center, the potential is maximum = 1. // 2) If it is at the radius or beyond, the potential is 0. @@ -22,7 +26,14 @@ struct Metaball // of the distance from the center to the radius. float CalculateMetaballPotential(in float3 position, in Metaball blob) { - return 0.0f; + float3 distvec = blob.center - position; + float dist = sqrt(distvec.x * distvec.x + distvec.y * distvec.y + distvec.z * distvec.z); + if (dist > blob.radius) + { + return 0.0f; + } + float r = 1.0 - (dist / blob.radius); + return 6*r*r*r*r*r - 15*r*r*r*r + 10*r*r*r; } // LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials. @@ -77,15 +88,23 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps } } -// TODO-3.4.2: Find the entry and exit points for all metaball bounding spheres combined. +// TDO-3.4.2: Find the entry and exit points for all metaball bounding spheres combined. // Remember that a metaball is just a solid sphere. Didn't we already do this somewhere else? void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout Metaball blobs[N_METABALLS]) { tmin = INFINITY; tmax = -INFINITY; -} -// TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field. + for (UINT i = 0; i < N_METABALLS; i++) + { + float localtmin, localtmax; + SolveRaySphereIntersectionEquation(ray, localtmin, localtmax, blobs[i].center, blobs[i].radius); + if (localtmin < tmin) tmin = localtmin; + if (localtmax > tmax) tmax = localtmax; + + } +} +// TDO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field. // The test sphere traces through the metaball field until it hits a threshold isosurface. // Returns true if we found a point. False otherwise. // 1) Initialize a metaball array. See InitializeAnimatedMetaballs() @@ -100,9 +119,31 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout // If this condition fails, keep raymarching! bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime) { - thit = 0.0f; - attr.normal = float3(0.0f, 0.0f, 0.0f); - return false; + Metaball blobs[N_METABALLS]; + InitializeAnimatedMetaballs(blobs, elapsedTime, CYCLETIME); + float tmin, tmax; + TestMetaballsIntersection(ray, tmin, tmax, blobs); + if (tmin == INFINITY || tmax == -INFINITY) return false;//did not hit any metaballs + float tdiff = tmax - tmin; + float tdelta = tdiff / NUMSTEPS; + + bool didHit = false; + float curT = tmin; + for (int i = 0; i < NUMSTEPS; i++) + { + curT += tdelta; + float potential = CalculateMetaballsPotential(ray.origin + ray.direction * curT, blobs); + if (potential > THRESH) + {//taking the first hit on our raymarch that breaks the potential + didHit = true; + + break; + } + } + if (!didHit) return false; + thit = curT; + attr.normal = CalculateMetaballsNormal(ray.origin + ray.direction * thit, blobs); + return is_a_valid_hit(ray, thit, attr.normal); } #endif // VOLUMETRICPRIMITIVESLIBRARY_H \ No newline at end of file diff --git a/tools/testthefuckinggrid.py b/tools/testthefuckinggrid.py new file mode 100644 index 0000000..8a6aa0b --- /dev/null +++ b/tools/testthefuckinggrid.py @@ -0,0 +1,43 @@ +import sys + + +c_aabbWidth = 2 +c_aabbDistance = 2 + +aabbGrid = [3, 1, 3] + +def getBasePosition(): + result = (\ + -(aabbGrid[0] * c_aabbWidth + (aabbGrid[0] - 1) * c_aabbDistance) / 2.0, + -(aabbGrid[1] * c_aabbWidth + (aabbGrid[1] - 1) * c_aabbDistance) / 2.0, + -(aabbGrid[2] * c_aabbWidth + (aabbGrid[2] - 1) * c_aabbDistance) / 2.0) + return result + +def centerForCoord(xcoord, ycoord, zcoord, basePos): + cx = basePos[0] + xcoord * (c_aabbWidth + c_aabbDistance) + c_aabbWidth / 2.0 + cy = basePos[1] + ycoord * (c_aabbWidth + c_aabbDistance) + c_aabbWidth / 2.0 + cz = basePos[2] + zcoord * (c_aabbWidth + c_aabbDistance) + c_aabbWidth / 2.0 + + return (cx, cy, cz) + +def getCenters(): + result = [] + basePos = getBasePosition() + for i in range(aabbGrid[0]): + for j in range(aabbGrid[1]): + for k in range(aabbGrid[2]): + result.append(centerForCoord(i, j, k, basePos)) + + + return result + + + +def main(): + print("Base position:") + print(getBasePosition()) + print("Centers:") + print(getCenters()) + +if __name__ == "__main__": + main()