diff --git a/README.md b/README.md
index b0189d0..9247bea 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,48 @@
+# DirectX Procedural Raytracing
**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)
+* Zheyuan Xie
+* Tested on: Windows 10 Pro, i7-7700HQ @ 2.80GHz, 16GB, GTX 1050 2GB (Dell XPS 15 9560)
-### (TODO: Your README)
+## Introduction
-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 implemented ray tracing for procedural geometry using Direct X 12 Raytracing API.
+
+Keyboard Shortcut:
+
+ - `C` - enable/disable camera animation.
+ - `G` - enable/disable geometry animation.
+ - `L` - enable/disable light animation.
+
+## Features
+
+### Supported Geometry
+ 1. Triangles
+
+ Triangles are supported directly in DXR.
+
+ 2. Axis-aligned Bounding Box
+
+ Axis-aligned Bounding Boxs (AABBs) are the simplest type of analytic geometry primitive. The shape renders into boxes that takes all the volume of a procedural AABB. One thing to note is the box has to be axis-aligned (rotation not supported).
+
+ 3. Spheres
+
+ Spheres are another very simple analytic geometry primitive defined by center and radius. The project supports multiple spheres in a single procedural AABB.
+
+ 4. Metaballs
+ Metaballs (a.k.a blobs) are special spheres that have this potential that causes adjacent spheres to merge together. This process is rendered with a special technique using potential thresholding.
+
+
+## Performance Analysis
+The chart below shows how FPS changes with maximum ray depth. As maximum ray depth increases, the FPS drops slightly influenced by reflective materials in the scene.
+
+
+
+## Reference
+ - [Microsoft Direct3D 12 programming guide](https://docs.microsoft.com/en-us/windows/win32/direct3d12/directx-12-programming-guide)
+ - [3D Game Engine Programming - Learning DirectX 12](https://www.3dgep.com/learning-directx-12-2/#Introduction)
+ - [Microsoft DirectX-Graphics-Samples](https://github.com/microsoft/DirectX-Graphics-Samples)
+ - Recitation Slides
diff --git a/cq.md b/cq.md
new file mode 100644
index 0000000..3123f29
--- /dev/null
+++ b/cq.md
@@ -0,0 +1,45 @@
+# Conceptual Questions
+## Question 1: Generate Ray from Pixels
+In ray tracing, a ray is generated for each pixel P'raster represented in raster space. The original and direction of the ray can be calculated by the following steps:
+
+ 1. Convert from raster space to NDC space. Note that the y coordinate is inverted since the origin of NDC space is located on the lower left corner.
+
+
+ > P'NDC.x = (P'raster.x + 0.5) / PixelWidth
+
+ > P'NDC.y = 1 - (P'raster.y + 0.5) / PixelHeight
+
+ 2. Convert from 2D NDC space to 2D screen space.
+
+ > P'screen.x = (1 - 2 * P'NDC.x) * screenWidth
+
+ > P'screen.y = (1 - 2 * P'NDC.y) * screenHeight
+
+ 3. Convert 2D screen space coordinate to 3D camera space gives the ray origin.
+
+ > RayOrigin.x = P'screen.x
+
+ > RayOrigin.y = P'screen.y
+
+ > RayOrigin.z = - CameraToScreenDistance
+
+ 4. Calculate the direction of the ray. The camera orgin is (0,0,0) in the camera space.
+
+ > RayDirection = normalize(RayOrigin - CameraOrigin)
+
+## Question 2: Rendering Procedual Geometry
+
+The procedural geometry is defined by:
+ - An Axis-Aligned Bounding Box (AABB).
+ - Geometry type.
+ - Geometry equation.
+
+The ray is first checked against the AABB. If there's an intersection (the ray enters the AABB at P0), it is then checked against the geomtry defined by its associated equation. We can use ray marching to advance the ray after it enters the AABB.
+
+Calculate the samllest distance from P0 to all procedure shapes inside the AABB (using the equation), say it's d. If it is negative (or smaller than some threshold), we've found the intersection of the ray with the geometry. If not we advanced the ray by distance d to P1, calculate the smallest distance from P1 to the all procedure shapes in AABB, check if the distance is smaller than the threshold. We repeat this process until we hit or some maximum number of steps is reached.
+
+
+## Question 3: DXR Acceleratin Structures
+We can build 1 TLAS and 4 BLASs for the given scene:
+
+
diff --git a/images/accelstructs.png b/images/accelstructs.png
new file mode 100644
index 0000000..d9c83ca
Binary files /dev/null and b/images/accelstructs.png differ
diff --git a/images/dxr.gif b/images/dxr.gif
new file mode 100644
index 0000000..6800bf7
Binary files /dev/null and b/images/dxr.gif differ
diff --git a/images/performance.png b/images/performance.png
new file mode 100644
index 0000000..e5d6bf7
Binary files /dev/null and b/images/performance.png differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..7a75ac3 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -165,19 +165,37 @@ bool RaySolidSphereIntersectionTest(in Ray ray, out float thit, out float tmax,
// You can hardcode the local centers/radii of the spheres, just try to maintain them between 1 and -1 (and > 0 for the radii).
bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr)
{
+ const int N_SPHERES = 3;
// Define the spheres in local space (within the aabb)
- float3 center = float3(-0.2, 0, -0.2);
- float radius = 0.7f;
-
- thit = RayTCurrent();
-
- float tmax;
- if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
- {
- return true;
- }
+ float3 center[N_SPHERES] = {
+ float3(-0.2, 0, -0.2),
+ float3(-0.8, 0.5, -0.8),
+ float3(0.5, 0, 0.5),
+ };
+ float radius[N_SPHERES] = {
+ 0.7f,
+ 0.1f,
+ 0.3f,
+ };
+
+ thit = RayTCurrent();
+ bool hit = false;
+
+ for (int idx = 0; idx < N_SPHERES; idx++) {
+ float _thit;
+ float _tmax;
+ ProceduralPrimitiveAttributes _attr;
+ if (RaySphereIntersectionTest(ray, _thit, _tmax, _attr, center[idx], radius[idx]))
+ {
+ if (_thit < thit) {
+ thit = _thit;
+ attr = _attr;
+ hit = true;
+ }
+ }
+ }
- return false;
+ return hit;
}
#endif // ANALYTICPRIMITIVES_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 084077a..8404938 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -31,7 +31,15 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width)
auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0];
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,11 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + i * sizeof(D3D12_RAYTRACING_AABB);
+ }
}
}
@@ -68,7 +80,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 = {};
@@ -87,7 +103,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// Allocate resources for acceleration structures as a UAV --> this will prepare bottomLevelAS for us.
// Acceleration structures can only be placed in resources that are created in the default heap (or custom heap equivalent).
- // Default heap is OK since the application doesn’t need CPU read/write access to them.
+ // Default heap is OK since the application doesn�t need CPU read/write access to them.
// The resources that will contain acceleration structures must be created in the state D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
// and must have resource flag D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS. The ALLOW_UNORDERED_ACCESS requirement simply acknowledges both:
// - the system will be doing this type of access in its implementation of acceleration structure builds behind the scenes.
@@ -108,7 +124,10 @@ 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 +146,11 @@ 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)));
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTranslation);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -202,6 +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 = {};
@@ -216,7 +253,7 @@ 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,11 @@ 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();
+ topLevelBuildDesc.Inputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress();
+ topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ }
// Build acceleration structure.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -302,7 +343,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 +364,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,7 +385,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.
m_deviceResources->ExecuteCommandList();
@@ -347,5 +396,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..5547e00 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,10 @@ 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 +61,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..25f1d89 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -38,6 +38,12 @@ void DXProceduralProject::InitializeScene()
purple.y * ChromiumReflectance.y,
purple.z * ChromiumReflectance.z,
1.0f);
+ XMFLOAT4 lime_green = XMFLOAT4(0.1961, 0.8039, 0.1961, 1);
+ XMFLOAT4 dark_green = XMFLOAT4(0.0000, 0.4444, 0.0000, 1);
+ XMFLOAT4 dodger_blue = XMFLOAT4(0.1176, 0.5647, 1.0000, 1);
+ XMFLOAT4 crimson = XMFLOAT4(0.8627, 0.0784, 0.2353, 1);
+ XMFLOAT4 cyan = XMFLOAT4(0.0000, 0.9999, 0.8793, 1);
+ XMFLOAT4 orange = XMFLOAT4(0.9999, 0.2705, 0.1000, 1);
UINT offset = 0;
// Analytic primitives.
@@ -45,6 +51,13 @@ void DXProceduralProject::InitializeScene()
using namespace AnalyticPrimitive;
SetAttributes(offset + AABB, yellow, 0.3f);
SetAttributes(offset + Spheres, chromium_purple, 0.8f);
+ SetAttributes(offset + Cube1, lime_green, 0.3f);
+ SetAttributes(offset + Cube2, crimson, 0.2f);
+ SetAttributes(offset + Cube3, dodger_blue, 0.5f);
+ SetAttributes(offset + Cube4, dark_green, 0.4f);
+ SetAttributes(offset + Cube5, cyan, 0.2f);
+ SetAttributes(offset + Cube6, dodger_blue, 0.0f);
+ SetAttributes(offset + Cube7, orange, 0.8f);
offset += AnalyticPrimitive::Count;
}
@@ -111,7 +124,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();
+ UINT primitiveCount = IntersectionShaderType::TotalPrimitiveCount;
+
+ m_aabbPrimitiveAttributeBuffer.Create(device, primitiveCount, frameCount, L"AABB Primitive Attribute Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -146,6 +163,7 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime)
XMMATRIX mScale15y = XMMatrixScaling(1, 1.5, 1);
XMMATRIX mScale15 = XMMatrixScaling(1.5, 1.5, 1.5);
XMMATRIX mScale2 = XMMatrixScaling(2, 2, 2);
+ XMMATRIX mScale025 = XMMatrixScaling(0.25, 0.25, 0.25);
// Rotation matrix that changes over time
XMMATRIX mRotation = XMMatrixRotationY(-2 * animationTime);
@@ -164,6 +182,9 @@ 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 mTransform = mScale * mRotation * mTranslation;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = mTransform;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, mTransform);
};
UINT offset = 0;
@@ -172,6 +193,13 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime)
using namespace AnalyticPrimitive;
SetTransformForAABB(offset + AABB, mScale15y, mIdentity);
SetTransformForAABB(offset + Spheres, mScale15, mRotation);
+ SetTransformForAABB(offset + Cube1, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube2, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube3, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube4, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube5, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube6, mScale025, mIdentity);
+ SetTransformForAABB(offset + Cube7, mScale025, mIdentity);
offset += AnalyticPrimitive::Count;
}
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..c732dbb 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -86,7 +86,14 @@ 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);
@@ -97,6 +104,13 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
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 + Cube1] = InitializeAABB(XMFLOAT3(-0.25f, 0.0f, 0.0f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube2] = InitializeAABB(XMFLOAT3(0.0f, 0.0f, 0.14f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube3] = InitializeAABB(XMFLOAT3(0.25f, 0.0f, 0.75f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube4] = InitializeAABB(XMFLOAT3(-0.35f, 0.0f, 0.55f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube5] = InitializeAABB(XMFLOAT3(-0.45f, 0.0f, -0.25f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube6] = InitializeAABB(XMFLOAT3(-0.55f, 0.0f, 0.0f), XMFLOAT3(0.5f, 0.5f, 0.5f));
+ m_aabbs[offset + Cube7] = InitializeAABB(XMFLOAT3(-0.65f, 0.0f, 1.0f), XMFLOAT3(0.5f, 0.5f, 0.5f));
offset += AnalyticPrimitive::Count;
}
@@ -110,12 +124,13 @@ 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);
}
}
// 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..c2270e6 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
@@ -21,6 +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); // 2 static index and vertex buffers.
// TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots.
@@ -35,10 +36,15 @@ void DXProceduralProject::CreateRootSignatures()
// - Look up InitAsDescriptorTable(), InitAsShaderResourceView(), and InitAsConstantBuffer() in the DirectX documentation
// to understand what to do.
// - If you're ever unsure if the register mapping is correct, look at the top of Raytracing.hlsl.
- // u registers --> UAV
- // t registers --> SRV
- // b registers --> CBV
+ // u registers --> UAV (Unordered Access View)
+ // t registers --> SRV (Shader Resource View)
+ // b registers --> CBV (Constant Buffer View)
CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
+ rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]);
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0);
+ rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0);
+ rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3);
+ rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]);
// 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);
@@ -67,7 +73,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..de6b408 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++)
@@ -43,7 +45,14 @@ void DXProceduralProject::BuildShaderTables()
}
// TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB.
-
+ for (UINT r = 0; r < IntersectionShaderType::Count; r++)
+ {
+ for (UINT c = 0; c < RayType::Count; c++)
+ {
+ hitGroupShaderIDs_AABBGeometry[r][c] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[r][c]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[r][c]] = c_hitGroupNames_AABBGeometry[r][c];
+ }
+ }
};
// Get shader identifiers using the lambda function defined above.
@@ -95,7 +104,17 @@ 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;
+
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
+ for (UINT i = 0; i < RayType::Count; i++)
+ {
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[i], shaderIDSize, nullptr, 0));
+ }
+ 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/ProceduralPrimitivesLibrary.hlsli b/src/D3D12RaytracingProceduralGeometry/ProceduralPrimitivesLibrary.hlsli
index dfeb4e0..bcc13ea 100644
--- a/src/D3D12RaytracingProceduralGeometry/ProceduralPrimitivesLibrary.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/ProceduralPrimitivesLibrary.hlsli
@@ -22,9 +22,18 @@ bool RayAnalyticGeometryIntersectionTest(in Ray ray, in AnalyticPrimitive::Enum
switch (analyticPrimitive)
{
- case AnalyticPrimitive::AABB: return RayAABBIntersectionTest(ray, aabb, thit, attr);
- case AnalyticPrimitive::Spheres: return RayMultipleSpheresIntersectionTest(ray, thit, attr);
- default: return false;
+ case AnalyticPrimitive::AABB:
+ case AnalyticPrimitive::Cube1:
+ case AnalyticPrimitive::Cube2:
+ case AnalyticPrimitive::Cube3:
+ case AnalyticPrimitive::Cube4:
+ case AnalyticPrimitive::Cube5:
+ case AnalyticPrimitive::Cube6:
+ case AnalyticPrimitive::Cube7:
+ return RayAABBIntersectionTest(ray, aabb, thit, attr);
+ case AnalyticPrimitive::Spheres:
+ return RayMultipleSpheresIntersectionTest(ray, thit, attr);
+ default: return false;
}
}
diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
index d066933..f2665c3 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 coefficient = dot(normalize(-incidentLightRay), normalize(normal));
+ coefficient = clamp(coefficient, 0, 1);
+ return coefficient;
}
// TODO-3.6: Phong lighting specular component.
@@ -51,7 +53,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);
+ float3 reflectedLightRay = normalize(reflect(incidentLightRay, normal));
+ float coefficient = pow(dot(reflectedLightRay, normalize(-WorldRayDirection())), specularPower);
+ coefficient = clamp(coefficient, 0, 1);
+ return coefficient;
}
// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
@@ -68,6 +73,25 @@ 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 lightDirection = normalize(HitWorldPosition() - g_sceneCB.lightPosition);
+
+ // Diffuse component
+ float4 lightDiffuseColor = g_sceneCB.lightDiffuseColor;
+ float Kd = CalculateDiffuseCoefficient(lightDirection, normal);
+ float4 diffuseColor = (isInShadow ? InShadowRadiance : 1.0) * diffuseCoef * Kd * lightDiffuseColor * albedo;
+
+ // Specular component
+ float4 specularColor;
+ if (!isInShadow)
+ {
+ float4 lightSpecularColor = float4(1, 1, 1, 1);
+ float4 Ks = CalculateSpecularCoefficient(lightDirection, normal, specularPower);
+ specularColor = specularCoef * Ks * lightSpecularColor;
+ }
+ else {
+ specularColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+
// Ambient component
// Fake AO: Darken faces with normal facing downwards/away from the sky a little bit
float4 ambientColor = g_sceneCB.lightAmbientColor;
@@ -76,7 +100,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 +159,31 @@ 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;
+ }
+
+ // Set the ray's extents.
+ RayDesc rayDesc;
+ rayDesc.Origin = ray.origin;
+ rayDesc.Direction = ray.direction;
+ // Set TMin to a zero value to avoid aliasing artifacts along contact areas.
+ // Note: make sure to enable face culling so as to avoid surface face fighting.
+ rayDesc.TMin = 0;
+ rayDesc.TMax = 10000;
+
+ ShadowRayPayload shadowRayPayload = { true };
+
+ TraceRay(g_scene,
+ RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_FORCE_OPAQUE | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, shadowRayPayload);
+
+ return shadowRayPayload.hit;
}
//***************************************************************************
@@ -149,9 +197,14 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
+ Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
+
+ UINT currentRecursionDepth = 0;
+ float4 reflectionColor = TraceRadianceRay(ray, currentRecursionDepth);
// Write the color to the render target
- g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ // g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ g_renderTarget[DispatchRaysIndex().xy] = reflectionColor;
}
//***************************************************************************
@@ -210,6 +263,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 lerp_coeff = exp(-0.0001 * t * t * t);
+ color = lerp(BackgroundColor, color, lerp_coeff);
+
rayPayload.color = color;
}
@@ -227,7 +284,35 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
[shader("closesthit")]
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
+ float3 hitPosition = HitWorldPosition();
+ float3 surfaceNormal = attr.normal;
+ // (1) Trace a shadow ray.
+ Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) };
+ bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth);
+
+ // (2) Trace a reflectance ray.
+ float4 reflectedColor = float4(0, 0, 0, 0);
+ if (l_materialCB.reflectanceCoef > 0.001)
+ {
+ Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), surfaceNormal) };
+ float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth);
+
+ float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), surfaceNormal, l_materialCB.albedo.xyz);
+ reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor;
+ }
+
+ // (3) (4) Calculate final color.
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, surfaceNormal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ float4 color = (phongColor + reflectedColor);
+
+ // (5) TODO: apply visibility falloff.
+ float t = RayTCurrent();
+ float lerp_coeff = exp(-0.0001 * t * t * t);
+ color = lerp(BackgroundColor, color, lerp_coeff);
+
+ // (6) Fill the payload color.
+ rayPayload.color = color;
}
//***************************************************************************
@@ -240,14 +325,16 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive
[shader("miss")]
void MyMissShader(inout RayPayload rayPayload)
{
-
+ // simply return background color.
+ 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)
{
-
+ // no hit.
+ rayPayload.hit = false;
}
//***************************************************************************
@@ -299,6 +386,20 @@ 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/RaytracingHlslCompat.h b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
index 6e10f0d..9daf1f6 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h
@@ -100,7 +100,14 @@ namespace AnalyticPrimitive {
enum Enum {
AABB = 0,
Spheres,
- Count
+ Cube1,
+ Cube2,
+ Cube3,
+ Cube4,
+ Cube5,
+ Cube6,
+ Cube7,
+ Count,
};
}
diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
index 94bf5cc..93b78e2 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; // 0 -> 1
+ float lerp_coeff = (t > 0.5) ? 1 - 2 * (t - 0.5) : 2 * t; // 0 -> 1 -> 0;
+ return smoothstep(0, 1, lerp_coeff);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -129,9 +131,20 @@ 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)
{
- Ray ray;
- ray.origin = float3(0.0f, 0.0f, 0.0f);
- ray.direction = normalize(float3(0.0f, 0.0f, 0.0f));
+ // Ray ray;
+ // ray.origin = float3(0.0f, 0.0f, 0.0f);
+ // ray.direction = normalize(float3(0.0f, 0.0f, 0.0f));
+
+ float2 xy = index + 0.5;
+ float2 screenPos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
+ screenPos.y = - screenPos.y;
+
+ float4 unprojected = mul(float4(screenPos, 0, 1), projectionToWorld);
+ float3 world = unprojected.xyz / unprojected.w;
+
+ Ray ray;
+ ray.origin = cameraPosition;
+ ray.direction = normalize(world - cameraPosition);
return ray;
}
@@ -141,7 +154,7 @@ 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;
+ return f0 + (1 - f0) * pow(1 - clamp(dot(-I, N), 0, 1), 5);
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..4623760 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,16 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float dist = distance(position, blob.center);
+ float ratio = dist / blob.radius;
+
+ float potential;
+ if (dist > blob.radius) {
+ potential = 0.0f;
+ } else {
+ potential = 1 - (6 * pow(ratio, 5) - 15 * pow(ratio, 4) + 10 * pow(ratio, 3));
+ }
+ return potential;
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -80,9 +89,20 @@ 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.
// 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;
+
+ float _thit, _tmax;
+ for (int i = 0; i < N_METABALLS; i++) {
+ if (RaySolidSphereIntersectionTest(ray, _thit, _tmax, blobs[i].center, blobs[i].radius)) {
+ tmin = min(_thit, tmin);
+ tmax = max(_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,6 +120,30 @@ 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)
{
+ Metaball blobs[3];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 10.0f);
+
+ float tmin, tmax;
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+
+ float t = tmin;
+ float dt = (tmax - tmin) / 128;
+ for (int step = 0; step < 128; step++) {
+ float3 position = ray.origin + t * ray.direction;
+ float potential = CalculateMetaballsPotential(position, blobs);
+
+ if (potential > 0.5) {
+ float3 normal = CalculateMetaballsNormal(position, blobs);
+ if (is_a_valid_hit(ray, t, normal)) {
+ thit = t;
+ attr.normal = normal;
+ return true;
+ }
+ }
+
+ t += dt;
+ }
+
thit = 0.0f;
attr.normal = float3(0.0f, 0.0f, 0.0f);
return false;