diff --git a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj
index 0d72eb0..4e36582 100644
--- a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj
+++ b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj
@@ -28,7 +28,7 @@
StaticLibrary
Unicode
- v141
+ v142
diff --git a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
index cc2db39..52aef00 100644
--- a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
+++ b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
@@ -201,6 +201,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index b0189d0..2e7c1b1 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,98 @@
**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)
+* Gangzheng Tong
+ * www.gtong.me
+* Tested on: Windows 10, i7-8750H @ 2.20GHz 16GB, RTX 2070 8GB (personal laptop)
-### (TODO: Your README)
+### Overview
-Include screenshots, analysis, etc. (Remember, this is public, so don't put
-anything here that you don't want to share with the world.)
+This project implements a raytracing based scene renderer and demonstrates the usage of DirectX 12 Raytracing API. It utilizes the built-in acceleration structure, analytic and procedural geometry construction and shadow ray testing.
+
+
+
+
+
+
+
+### Contents
+
+- Ray Tracing Concept
+- Acceleration Structure
+- Output
+- Performance Analysis
+- Bloopers
+
+
+
+### Ray Tracing Concept
+
+
+
+
+
+
+
+
+
+Unlike path tracer, ray tracing based renderer simples the the rendering and calculation significantly by only keeping a few rays for each pixel and shading point rather than spawning multiply rays based on BSDF. In this project, we only consider:
+
+- **Primary ray**: the ray shot from camera to each pixel. We need to apply the project_to_world transformation in order to bring the ray to the world coordinate, for further intersection testing with the scene.
+
+- **Shadow ray**: from the closest_hit point, spawn a shadow ray to the light source to find out if this point is in shadow. It's still cheap for scenes with only a few light sources.
+
+- **Reflected ray**: calculating the reflected ray based on the incident ray direction and normal of hit point. Perform another trace like the primary ray but with the depth incremented.
+
+
+
+### Acceleration Structure
+
+##### Diagram of DXR Top-Level/Bottom-Level Acceleration Structures
+
+
+
+
+
+
+
+### Output
+
+
+
+- Static light and static camera, 4 metaballs and 3 spheres
+
+
+
+
+
+- Animating light source
+
+
+
+
+
+- Animating camera
+
+
+
+### Performance Analysis
+
+
+
+- With the max recursion depth increased from 3 to 10, initially there is a drop in the FPS from 3 to 5, but it quickly stables at around 280 FPS.
+- It makes sense since the scene doesn't have too many reflected materials and most of rays terminated after 5 bounces.
+
+
+
+### Bloopers
+
+
+
+When I used **pow(x, 5)** to calculate **x * x * x * x * x**
+
+
+
+### References
+
+- https://github.com/Microsoft/DirectX-Graphics-Samples
+- https://docs.microsoft.com/en-us/windows/win32/direct3d12/directx-12-programming-guide
\ No newline at end of file
diff --git a/img/as.png b/img/as.png
new file mode 100644
index 0000000..2e6f254
Binary files /dev/null and b/img/as.png differ
diff --git a/img/blooper.PNG b/img/blooper.PNG
new file mode 100644
index 0000000..4ba4eb1
Binary files /dev/null and b/img/blooper.PNG differ
diff --git a/img/camera_space.png b/img/camera_space.png
new file mode 100644
index 0000000..309138a
Binary files /dev/null and b/img/camera_space.png differ
diff --git a/img/main.gif b/img/main.gif
new file mode 100644
index 0000000..a26da8c
Binary files /dev/null and b/img/main.gif differ
diff --git a/img/ndc_1.png b/img/ndc_1.png
new file mode 100644
index 0000000..259b6fb
Binary files /dev/null and b/img/ndc_1.png differ
diff --git a/img/output1.gif b/img/output1.gif
new file mode 100644
index 0000000..be8735f
Binary files /dev/null and b/img/output1.gif differ
diff --git a/img/output_camera.gif b/img/output_camera.gif
new file mode 100644
index 0000000..3e2e109
Binary files /dev/null and b/img/output_camera.gif differ
diff --git a/img/output_light.gif b/img/output_light.gif
new file mode 100644
index 0000000..aeef2c8
Binary files /dev/null and b/img/output_light.gif differ
diff --git a/img/performance.png b/img/performance.png
new file mode 100644
index 0000000..d747a39
Binary files /dev/null and b/img/performance.png differ
diff --git a/img/pipeline.png b/img/pipeline.png
new file mode 100644
index 0000000..0802502
Binary files /dev/null and b/img/pipeline.png differ
diff --git a/img/raytrace.jpg b/img/raytrace.jpg
new file mode 100644
index 0000000..6c0fe18
Binary files /dev/null and b/img/raytrace.jpg differ
diff --git a/img/screen_space.png b/img/screen_space.png
new file mode 100644
index 0000000..6f12e1f
Binary files /dev/null and b/img/screen_space.png differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..03a80ba 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -169,6 +169,12 @@ bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out Procedur
float3 center = float3(-0.2, 0, -0.2);
float radius = 0.7f;
+ float3 center2 = float3(-0.3, 0.5, -0.2);
+ float radius2 = 0.5f;
+
+ float3 center3 = float3(-0.1, -0.5, 0.2);
+ float radius3 = 0.4f;
+
thit = RayTCurrent();
float tmax;
@@ -176,6 +182,14 @@ bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out Procedur
{
return true;
}
+ if (RaySphereIntersectionTest(ray, thit, tmax, attr, center2, radius2))
+ {
+ return true;
+ }
+ if (RaySphereIntersectionTest(ray, thit, tmax, attr, center3, radius3))
+ {
+ return true;
+ }
return false;
}
diff --git a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
index 4a8b9ab..f59a973 100644
--- a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
+++ b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
@@ -21,13 +21,13 @@
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 084077a..94a2412 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -30,8 +30,16 @@ 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 = { };
+ geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.IndexCount = static_cast(m_indexBuffer.resource->GetDesc().Width) / sizeof(Index);
+ geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT;
+ geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;
+ geometryDesc.Triangles.VertexCount = static_cast(m_vertexBuffer.resource->GetDesc().Width) / sizeof(Vertex);
+ geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex);
+ geometryDesc.Flags = geometryFlags;
}
{
@@ -49,7 +57,12 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + i * sizeof(D3D12_RAYTRACING_AABB);
+ }
}
}
@@ -68,7 +81,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// 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.
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs;
-
+ bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
+ bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ bottomLevelInputs.Flags = buildFlags;
+ bottomLevelInputs.NumDescs = static_cast(geometryDescs.size());
+ bottomLevelInputs.pGeometryDescs = geometryDescs.data();
// Query the driver for resource requirements to build an acceleration structure. We've done this for you.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
@@ -108,8 +125,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// TODO-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.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
{
@@ -127,7 +145,12 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// 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{};
+ AccelerationStructureBuffers bottomLevelASBuffers;
+ bottomLevelASBuffers.accelerationStructure = bottomLevelAS;
+ bottomLevelASBuffers.scratch = scratch;
+ bottomLevelASBuffers.ResultDataMaxSizeInBytes = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+
+ return bottomLevelASBuffers;
}
// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before.
@@ -179,7 +202,17 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev
// Where do you think procedural shader records would start then? Hint: right after.
// * Make each instance hover above the ground by ~ half its width
{
+ auto& instanceDesc = instanceDescs[BottomLevelASType::AABB];
+ instanceDesc = {};
+ instanceDesc.InstanceMask = 1;
+
+ // Set hit group offset to beyond the shader records for the triangle AABB.
+ instanceDesc.InstanceContributionToHitGroupIndex = BottomLevelASType::AABB * RayType::Count;
+ instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB];
+ // Move all AABBS above the ground plane.
+ XMMATRIX mTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&XMFLOAT3(0, c_aabbWidth / 2.0, 0)));
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTranslation);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -202,7 +235,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// TODO-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.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ topLevelInputs.Flags = buildFlags;
+ topLevelInputs.NumDescs = NUM_BLAS;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -216,7 +252,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0);
// TODO-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.
{
@@ -231,7 +268,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
}
// TODO-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:
@@ -259,7 +296,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
else // DirectX Raytracing
{
@@ -271,7 +308,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
// Create a wrapped pointer to the acceleration structure.
@@ -283,7 +320,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// TODO-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.
-
+ topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress();
+ topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress();
+ topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
// Build acceleration structure.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -302,7 +341,12 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// 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{};
+ AccelerationStructureBuffers topLevelASBuffers;
+ topLevelASBuffers.accelerationStructure = topLevelAS;
+ topLevelASBuffers.instanceDesc = instanceDescsResource;
+ topLevelASBuffers.scratch = scratch;
+ topLevelASBuffers.ResultDataMaxSizeInBytes = topLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+ return topLevelASBuffers;
}
// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene.
@@ -318,12 +362,15 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-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.
// Hint: you filled in a function that does this.
AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count];
-
+ for (UINT i = 0; i < BottomLevelASType::Count; i++)
+ {
+ bottomLevelAS[i] = BuildBottomLevelAS(geometryDescs[i]);
+ }
// Batch all resource barriers for bottom-level AS builds.
// This will Notifies the driver that it needs to synchronize multiple accesses to resources.
@@ -336,6 +383,7 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-2.6: Build top-level AS. Hint, you already made a function that does this.
AccelerationStructureBuffers topLevelAS;
+ topLevelAS = BuildTopLevelAS(bottomLevelAS);
// Kick off acceleration structure construction.
@@ -347,5 +395,9 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-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 (UINT i = 0; i < BottomLevelASType::Count; i++)
+ {
+ m_bottomLevelAS[i] = bottomLevelAS[i].accelerationStructure;
+ }
+ m_topLevelAS = topLevelAS.accelerationStructure;
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
index 03a8c58..21a9798 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
@@ -22,7 +22,8 @@ void DXProceduralProject::DoRaytracing()
commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex));
// TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer
-
+ m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex);
+ commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex));
// Bind the descriptor heaps.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -49,10 +50,11 @@ void DXProceduralProject::DoRaytracing()
// 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.
-
+ 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
@@ -60,13 +62,18 @@ void DXProceduralProject::DoRaytracing()
{
// 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
-
+ dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress();
+ dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width;
+ dispatchDesc->HitGroupTable.StrideInBytes = m_hitGroupShaderTableStrideInBytes;
// TODO-2.8: now fill in dispatchDesc->MissShaderTable
-
+ dispatchDesc->MissShaderTable.StartAddress = m_missShaderTable->GetGPUVirtualAddress();
+ dispatchDesc->MissShaderTable.SizeInBytes = m_missShaderTable->GetDesc().Width;
+ dispatchDesc->MissShaderTable.StrideInBytes = m_missShaderTableStrideInBytes;
// TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord
-
+ dispatchDesc->RayGenerationShaderRecord.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress();
+ dispatchDesc->RayGenerationShaderRecord.SizeInBytes = m_rayGenShaderTable->GetDesc().Width;
// We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA!
dispatchDesc->Width = m_width;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
index e3ff63c..4c3146e 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -111,7 +111,11 @@ 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 frameCount = m_deviceResources->GetBackBufferCount();
+ // TONG TODO
+ int numElements = AnalyticPrimitive::Count + VolumetricPrimitive::Count;
+ m_aabbPrimitiveAttributeBuffer.Create(device, numElements, frameCount, L"Scene AABB Primitive Attributes Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -164,6 +168,10 @@ 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.
+ XMMATRIX transformMat = mScale * mRotation * mTranslation;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = transformMat;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, transformMat);
+
};
UINT offset = 0;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..d3f8de7 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -86,7 +86,13 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// This should take into account the basePosition and the stride defined above.
auto InitializeAABB = [&](auto& offsetIndex, auto& size)
{
- D3D12_RAYTRACING_AABB aabb{};
+ D3D12_RAYTRACING_AABB aabb{
+ basePosition.x + offsetIndex.x * stride.x,
+ basePosition.y + offsetIndex.y * stride.y,
+ basePosition.z + offsetIndex.z * stride.z,
+ basePosition.x + offsetIndex.x * stride.x + size.x,
+ basePosition.y + offsetIndex.y * stride.y + size.y,
+ basePosition.z + offsetIndex.z * stride.z + size.z, };
return aabb;
};
m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount);
@@ -110,6 +116,9 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// TODO-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);
+
}
}
@@ -117,5 +126,6 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// TODO-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..549a23d 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
@@ -29,7 +29,19 @@ 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.
{
-
+ // Create hit groups for each intersection shader.
+ for (UINT t = 0; t < IntersectionShaderType::Count; t++)
+ for (UINT rayType = 0; rayType < RayType::Count; rayType++)
+ {
+ auto hitGroup = raytracingPipeline->CreateSubobject();
+ hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[t]);
+ if (rayType == RayType::Radiance)
+ {
+ hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]);
+ }
+ hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[t][rayType]);
+ hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE);
+ }
}
}
@@ -54,6 +66,14 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT
// TODO-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());
+ // Shader association
+ auto rootSignatureAssociation = raytracingPipeline->CreateSubobject();
+ rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature);
+ for (auto& hitGroupsForIntersectionShaderType : c_hitGroupNames_AABBGeometry)
+ {
+ rootSignatureAssociation->AddExports(hitGroupsForIntersectionShaderType);
+ }
}
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
index 2dff8b5..0f77f3b 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
@@ -21,7 +21,7 @@ void DXProceduralProject::CreateRootSignatures()
// TODO-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.
// * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are.
@@ -39,8 +39,12 @@ void DXProceduralProject::CreateRootSignatures()
// t registers --> SRV
// b registers --> CBV
CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
+ rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]);
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0); // occupy slot 0
+ rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0);
+ rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3); // occupy slot 3
+ rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]); // occupy slot 1 and 2
- // 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);
SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature);
}
@@ -67,7 +71,14 @@ void DXProceduralProject::CreateRootSignatures()
// 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..dde2865 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
@@ -32,8 +32,8 @@ 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[RayType::Radiance] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[RayType::Radiance]);
+ missShaderIDs[RayType::Shadow] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[RayType::Shadow]);
// 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++)
@@ -43,7 +43,13 @@ void DXProceduralProject::BuildShaderTables()
}
// TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB.
-
+ for (UINT i = 0; i < RayType::Count; i++)
+ {
+ for (UINT t = 0; t < IntersectionShaderType::Count; t++){
+ hitGroupShaderIDs_AABBGeometry[t][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[t][i]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[t][i]] = c_hitGroupNames_AABBGeometry[t][i];
+ }
+ }
};
// Get shader identifiers using the lambda function defined above.
@@ -95,7 +101,20 @@ void DXProceduralProject::BuildShaderTables()
// TODO-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 = RayType::Count;
+ UINT shaderRecordSize = shaderIDSize; // No root arguments
+
+ // The miss shader table contains 2 ShaderRecords
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
+
+ // Push back the shader record, which does not need any root signatures.
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[RayType::Radiance], shaderRecordSize, nullptr, 0));
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[RayType::Shadow], shaderRecordSize, nullptr, 0));
+
+ // Save the uploaded resource (remember that the uploaded resource is created when we call Allocate() on a GpuUploadBuffer
+ missShaderTable.DebugPrint(shaderIdToStringMap);
+ m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize();
+ m_missShaderTable = missShaderTable.GetResource();
}
// Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature.
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..b7e3a21 100644
--- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
+++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
@@ -41,7 +41,7 @@ ConstantBuffer l_aabbCB: register(b2); // other
// Remember to clamp the dot product term!
float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
{
- return 0.0f;
+ return saturate(dot(-incidentLightRay, normal));
}
// TODO-3.6: Phong lighting specular component.
@@ -51,7 +51,10 @@ float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
// Remember to normalize the reflected ray, and to clamp the dot product term
float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower)
{
- return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ // DONE
+ float3 reflectedLightRay = normalize(reflect(incidentLightRay, normal));
+ float clmaped = saturate( dot(reflectedLightRay, normalize(-WorldRayDirection())) );
+ return pow(clmaped, specularPower );
}
// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
@@ -68,6 +71,26 @@ 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)
{
+ float3 hitPosition = HitWorldPosition();
+ float3 lightPosition = g_sceneCB.lightPosition.xyz;
+ float shadowCoef = isInShadow ? InShadowRadiance : 1.0;
+ float3 incidentLightRay = normalize(hitPosition - lightPosition);
+
+ // Diffuse component.
+ float4 lightDiffuseColor = g_sceneCB.lightDiffuseColor;
+ float Kd = CalculateDiffuseCoefficient(incidentLightRay, normal);
+ float4 diffuseColor = shadowCoef * diffuseCoef * Kd * lightDiffuseColor * albedo;
+
+ // Specular component.
+ float4 specularColor = float4(0, 0, 0, 0);
+ if (!isInShadow)
+ {
+ float4 lightSpecularColor = float4(1, 1, 1, 1);
+ float4 Ks = CalculateSpecularCoefficient(incidentLightRay, normal, specularPower);
+ specularColor = specularCoef * Ks * lightSpecularColor;
+ }
+ // DONE
+
// Ambient component
// Fake AO: Darken faces with normal facing downwards/away from the sky a little bit
float4 ambientColor = g_sceneCB.lightAmbientColor;
@@ -76,7 +99,7 @@ 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;
+ return ambientColor + diffuseColor + specularColor;
}
//***************************************************************************
@@ -135,7 +158,38 @@ 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;
+ }
+ // TODO
+
+ // Set the ray's extents.
+ RayDesc rayDesc;
+ rayDesc.Origin = ray.origin;
+ rayDesc.Direction = ray.direction;
+ // Set TMin to a zero value to avoid aliasing artifcats along contact areas.
+ // Note: make sure to enable back-face culling so as to avoid surface face fighting.
+ rayDesc.TMin = 0;
+ rayDesc.TMax = 10000;
+
+ // Initialize shadow ray payload.
+ // Set the initial value to true since closest and any hit shaders are skipped.
+ // Shadow miss shader, if called, will set it to false.
+ ShadowRayPayload shadowPayload = { true };
+ TraceRay(g_scene,
+ RAY_FLAG_CULL_BACK_FACING_TRIANGLES
+ | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH
+ | RAY_FLAG_FORCE_OPAQUE // skip any hit shaders
+ | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER, // skip closest hit shaders,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, shadowPayload);
+
+ // DONE
+ return shadowPayload.hit;
}
//***************************************************************************
@@ -149,9 +203,14 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
+ // Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid.
+ Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
- // Write the color to the render target
- g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ // Cast a ray into the scene and retrieve a shaded color.
+ float4 color = TraceRadianceRay(ray, 0);
+
+ // Write the raytraced color to the output texture.
+ g_renderTarget[DispatchRaysIndex().xy] = color;
}
//***************************************************************************
@@ -210,6 +269,10 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
// 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.
+ float t = RayTCurrent();
+ float val = exp(-0.0001 * pow(t, 3.0));
+ color = lerp(color, BackgroundColor, 1.0 -val);
+
rayPayload.color = color;
}
@@ -227,7 +290,32 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
[shader("closesthit")]
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
-
+ float3 hitPosition = HitWorldPosition();
+ Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) };
+ bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth);
+
+ // Reflected component.
+ float4 reflectedColor = float4(0, 0, 0, 0);
+ if (l_materialCB.reflectanceCoef > 0.001)
+ {
+ // Trace a reflection ray.
+ Ray reflectionRay = { HitWorldPosition(), reflect(WorldRayDirection(), attr.normal) };
+ float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth);
+
+ float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, l_materialCB.albedo.xyz);
+ reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor;
+ }
+
+ // Calculate final color.
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, attr.normal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ float4 color = phongColor + reflectedColor;
+
+ // Apply visibility falloff.
+ float t = RayTCurrent();
+ float val = exp(-0.0001 * pow(t, 3.0));
+ color = lerp(color, BackgroundColor, 1.0 - val);
+
+ rayPayload.color = color;
}
//***************************************************************************
@@ -240,14 +328,15 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive
[shader("miss")]
void MyMissShader(inout RayPayload rayPayload)
{
-
+ float4 backgroundColor = float4(BackgroundColor);
+ rayPayload.color = backgroundColor;
}
// TODO-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;
}
//***************************************************************************
@@ -299,6 +388,18 @@ void MyIntersectionShader_AnalyticPrimitive()
[shader("intersection")]
void MyIntersectionShader_VolumetricPrimitive()
{
+ Ray localRay = GetRayInAABBPrimitiveLocalSpace();
+ VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType;
+
+ float thit;
+ ProceduralPrimitiveAttributes attr;
+ if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime))
+ {
+ PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex];
+ attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS);
+ attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal));
+ ReportHit(thit, /*hitKind*/ 0, attr);
+ }
}
#endif // RAYTRACING_HLSL
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
index 6e10f0d..4c26fb3 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
@@ -24,7 +24,7 @@ typedef UINT16 Index;
#endif
// Number of metaballs to use within an AABB. Limit to 3 unless you are attempting the dynamic looping extra-credit.
-#define N_METABALLS 3
+#define N_METABALLS 4
#define N_FRACTAL_ITERATIONS 5 // = <1,...>
diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
index 94bf5cc..20b073f 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
@@ -68,7 +68,9 @@ 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 t = fmod(elapsedTime, cycleDuration) / cycleDuration;
+ t = (t <= 0.5f) ? 2 * t : 1 - 2 * (t - 0.5f);
+ return smoothstep(0, 1, t);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -129,9 +131,21 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics)
// as long as the direction of the ray is correct then the depth does not matter.
inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld)
{
+ float2 xy = index + 0.5f; // center in the middle of the pixel.
+ float2 screenPos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
+
+ // Invert Y for DirectX-style coordinates.
+ screenPos.y = -screenPos.y;
+
+ // Apply projectionToWorld to bring the point to world space
+ float4 world = mul(float4(screenPos, 0, 1), projectionToWorld);
+ // Normalize
+ world.xyz /= world.w;
+
+ // Create a ray with the origin and dir above
Ray ray;
- ray.origin = float3(0.0f, 0.0f, 0.0f);
- ray.direction = normalize(float3(0.0f, 0.0f, 0.0f));
+ ray.origin = cameraPosition;
+ ray.direction = normalize(world.xyz - ray.origin);
return ray;
}
@@ -141,7 +155,8 @@ 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 cosi = saturate(dot(-I, N));
+ return f0 + (1 - f0) * pow(1 - cosi, 5);
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..4a357ee 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,20 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float distance = length(position - blob.center);
+
+ if (distance <= blob.radius)
+ {
+ float d = blob.radius - distance;
+ float r = blob.radius;
+ float x = d / r;
+
+ // Note: pow has limited precision!
+ return 6 * x * x * x * x * x
+ - 15 * x * x * x * x
+ + 10 * x * x * x;
+ }
+ return 0;
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -62,11 +75,12 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps
{
{ float3(-0.3, -0.3, -0.4),float3(0.3,-0.3,-0.0) }, // begin center --> end center
{ float3(0.0, -0.2, 0.5), float3(0.0, 0.4, 0.5) },
- { float3(0.4,0.4, 0.4), float3(-0.4, 0.2, -0.4) }
+ { float3(0.4,0.4, 0.4), float3(-0.4, 0.2, -0.4) },
+ { float3(0.4,-0.4, 0.4), float3(-0.4, 0.2, -0.4) }
};
// Metaball field radii of max influence
- float radii[N_METABALLS] = { 0.45, 0.55, 0.45 };
+ float radii[N_METABALLS] = { 0.45, 0.55, 0.45, 0.2 };
// Calculate animated metaball center positions.
float tAnimate = CalculateAnimationInterpolant(elapsedTime, cycleDuration);
@@ -82,7 +96,19 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps
void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout Metaball blobs[N_METABALLS])
{
tmin = INFINITY;
- tmax = -INFINITY;
+ tmax = -INFINITY;
+
+ for (UINT i = 0; i < N_METABALLS; i++)
+ {
+ float cur_thit, cur_tmax;
+ if (RaySolidSphereIntersectionTest(ray, cur_thit, cur_tmax, blobs[i].center, blobs[i].radius))
+ {
+ tmin = min(cur_thit, tmin);
+ tmax = max(cur_tmax, tmax);
+ }
+ }
+ tmin = max(tmin, RayTMin());
+ tmax = min(tmax, RayTCurrent());
}
// TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field.
@@ -100,8 +126,46 @@ 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);
+ Metaball blobs[N_METABALLS];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 10.0f);
+
+ float tmin, tmax;
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+
+ UINT MAX_STEPS = 128;
+ float t = tmin;
+ float minTStep = (tmax - tmin) / MAX_STEPS;
+ UINT i = 0;
+
+ while (i < MAX_STEPS)
+ {
+ float3 pos = ray.origin + t * ray.direction;
+ float potentials[N_METABALLS]; // All metaballs potentials
+ float totalPotential = 0; // Sum of all metaball field potentials.
+
+ for (UINT j = 0; j < N_METABALLS; j++)
+ {
+ potentials[j] = CalculateMetaballPotential(pos, blobs[j]);
+ totalPotential += potentials[j];
+ }
+
+ float Threshold = 0.55f;
+
+ // continue if totalPotential not cross the threshold
+ // found a shading point otherwise
+ if (totalPotential >= Threshold)
+ {
+ float3 normal = CalculateMetaballsNormal(pos, blobs);
+ if (is_a_valid_hit(ray, t, normal))
+ {
+ thit = t;
+ attr.normal = normal;
+ return true;
+ }
+ }
+ t += minTStep;
+ i++;
+ }
return false;
}