diff --git a/README.md b/README.md
index b0189d0..dbc4a95 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,70 @@
**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)
+* Disha Jindal: [Linkedin](https://www.linkedin.com/in/disha-jindal/)
+* Tested on: Windows 10, Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 16GB, GTX 1080 (SIGLAB)
-### (TODO: Your README)
+## DirectX Procedural Raytracing
+

+
+## Introduction
+The aim of this project is to raytrace procedural geometries using the DXR rendering pipeline. DirectX Raytracing (DXR) is basically a DirectX API added for real-time raytracing.
-Include screenshots, analysis, etc. (Remember, this is public, so don't put
-anything here that you don't want to share with the world.)
+**Ray Tracing**
+
+Ray tracing is a process similar to path tracing, except that it is deterministic without any probabilities and that we only do a single pass over the entire scene, unlike multiple iterations in path tracing. There are three types of rays: Radiance Ray (Rays generated from camera), Shadow Ray (To check if the ray from the intersection point to the light source hits some object on the way or not) and Reflection Ray (It is generated if the material is reflective). This image summarizes what goes on in ray tracing:
+
+
+
+**DXR Pipeline**
+
+The DXR execution pipeline mimics all the interactions depicted below. Since raytracing is an expensive process, the most prevalent way to boost performance is to skip unnecessary geometry that the ray will never intersect with. DXR has a built in support for acceleration structures. This acceleration structures are created around the geometry to be rendered and it defines a set of rules to be followed by the ray. This diagram summarizes the DXR execution pipeline:
+
+
+
+## Animations
+
+The gif on the top of the page has three animations in it: geometry, light and camera. Without any animation, the scene looks like below:
+
+
+
+Following are the images with three different animations on the above image. The first one shows geometric animation where the objects are moving. The one is the middle has fixed camera and objects but the light position is moving. Finally, in the last one object and lights are fixed but the camera viewpoint is moving.
+
+
+Geometric Animation | Light Animation | Camera Animation
+:-------------------------:|:-------------------------:|:-------------------------:
+ |  | 
+
+## Implementation
+There are two main implementation components: the first one is to set up the graphics pipeline in CPU and the second one is GPU side code for raytracing. The first part of pipeline setup included creating the scene data which included a camera, lights, colors for objects, as well as transforms for the objects to render and then, mapping this data onto the CPU in a region that the GPU can read from. The next part was to create the global and local [root signatures](https://docs.microsoft.com/en-us/windows/win32/direct3d12/root-signatures). Then, we created the hitgroups which consists of a `Closest Hit Shader`, an `Intersection Shader` and an optional `Any Hit Shader`. Then, we glued everything created so far into a Ray Tracing Pipeline State Object and then we do the most important part creating Top Level and Bottom Level `Acceleratiion Structures`. Finally, we conclude the pipeline creation by fill in the shader tables and the command lists.
+
+After the pipeline creation, the next part to raytrace in the GPU code. The first part to generate rays (radiance rays) and then write functions to trace radiance and shadow rays. Then we implement the miss shaders which are required to handle the case when a ray hits absolutely nothing in the acceleration structure and then the intersection shaders for spheres, boxes, and metaballs to compute the hit point and the normal. Then, we implement various lighting effects: [Phong Lighting Model Shading](https://en.wikipedia.org/wiki/Phong_reflection_model), [Schlick's Approximation](https://en.wikipedia.org/wiki/Schlick%27s_approximation). Then, we implement the closest hit shader to select the winning object out of all intersecting ones. Following is the illustration of some of these effects:
+
+Without Closed Hit Shaders | With Closed Hit Shaders
+:-------------------------:|:-------------------------:
+ | 
+
+Without Distance falloff function | With Distance falloff function
+:---------------------------------:|:---------------------------------:
+ | 
+
+## Performance Analysis
+Following plot shows the performance of ray tracing with the ray depth. The x-axis denotes the maximum ray depth which goes from 3-10 and the y-axis shows the frames per second. The results are pretty straightforward as the performance is dropping as we increase the ray depth.
+
+
+## Bloopers
+Following bloopers are a result of some of the bugs introduced while working on this:
+
+**Upside Down**
+This is the result of my incorrect ray generation logic. Initially in pixel coordinates, (0,0) is the top left corner of the canvas. I normalized it to bring it to bring every pixel from (-1,1) range but missed that for DXR (-1,-1) should be bottom left not top left.
+
+
+
+**Visibility Falloff For AABB**
+Incorrect configuration for visibility falloff for AABB's, led to this:
+
+
+**Background Not Far Enough For Triangles**
+We implemented distance falloff function inside the triangle closest hit shader so that faraway triangles are slightly blurred. By not properly configuring the far away function, the background overshadowed the ground.
+
+
diff --git a/images/before_3.6.PNG b/images/before_3.6.PNG
new file mode 100644
index 0000000..e54124d
Binary files /dev/null and b/images/before_3.6.PNG differ
diff --git a/images/blooper1_axis.PNG b/images/blooper1_axis.PNG
new file mode 100644
index 0000000..8ed1f72
Binary files /dev/null and b/images/blooper1_axis.PNG differ
diff --git a/images/blooper2_background_not_far_enough.PNG b/images/blooper2_background_not_far_enough.PNG
new file mode 100644
index 0000000..447ed5d
Binary files /dev/null and b/images/blooper2_background_not_far_enough.PNG differ
diff --git a/images/blooper3_visibility_falloff.PNG b/images/blooper3_visibility_falloff.PNG
new file mode 100644
index 0000000..f637c50
Binary files /dev/null and b/images/blooper3_visibility_falloff.PNG differ
diff --git a/images/camera_anime.gif b/images/camera_anime.gif
new file mode 100644
index 0000000..abd53a7
Binary files /dev/null and b/images/camera_anime.gif differ
diff --git a/images/camera_geo_light_anime.gif b/images/camera_geo_light_anime.gif
new file mode 100644
index 0000000..624296f
Binary files /dev/null and b/images/camera_geo_light_anime.gif differ
diff --git a/images/fps_ray_depth.png b/images/fps_ray_depth.png
new file mode 100644
index 0000000..8372a73
Binary files /dev/null and b/images/fps_ray_depth.png differ
diff --git a/images/geo_anime.gif b/images/geo_anime.gif
new file mode 100644
index 0000000..55342f7
Binary files /dev/null and b/images/geo_anime.gif differ
diff --git a/images/light_anime.gif b/images/light_anime.gif
new file mode 100644
index 0000000..dbe2474
Binary files /dev/null and b/images/light_anime.gif differ
diff --git a/images/no_anime.PNG b/images/no_anime.PNG
new file mode 100644
index 0000000..0ea138a
Binary files /dev/null and b/images/no_anime.PNG differ
diff --git a/images/onlyambientcolor.PNG b/images/onlyambientcolor.PNG
new file mode 100644
index 0000000..fb6c078
Binary files /dev/null and b/images/onlyambientcolor.PNG differ
diff --git a/images/without_fog.PNG b/images/without_fog.PNG
new file mode 100644
index 0000000..e046a57
Binary files /dev/null and b/images/without_fog.PNG differ
diff --git a/images/without_fog_after3.6.PNG b/images/without_fog_after3.6.PNG
new file mode 100644
index 0000000..9a1e286
Binary files /dev/null and b/images/without_fog_after3.6.PNG differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..e30a799 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -103,7 +103,7 @@ float3 CalculateNormalForARaySphereHit(in Ray ray, in float thit, float3 center)
}
// Test if a ray with RayFlags and segment intersects a hollow sphere.
-bool RaySphereIntersectionTest(in Ray ray, out float thit, out float tmax, in ProceduralPrimitiveAttributes attr, in float3 center = float3(0, 0, 0), in float radius = 1)
+bool RaySphereIntersectionTest(in Ray ray, out float thit, out float tmax, out ProceduralPrimitiveAttributes attr, in float3 center = float3(0, 0, 0), in float radius = 1)
{
float t0, t1; // solutions for t if the ray intersects
@@ -165,19 +165,36 @@ 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 = 3;
// Define the spheres in local space (within the aabb)
- float3 center = float3(-0.2, 0, -0.2);
- float radius = 0.7f;
+ float3 centers[N] =
+ {
+ float3(-0.2, 0, -0.2),
+ float3(0.6, 0.1, 0.6),
+ float3(0.2, 0.35, 0.2)
+ };
+
+ float radii[N] = { 0.4f, 0.2f, 0.1f };
+ bool hitFound = false;
thit = RayTCurrent();
+ float temp_thit;
float tmax;
- if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
+ ProceduralPrimitiveAttributes temp_attr;
+ for (int i = 0; i < N; i++)
{
- return true;
+ if (RaySphereIntersectionTest(ray, temp_thit, tmax, temp_attr, centers[i], radii[i]))
+ {
+ if (temp_thit < thit)
+ {
+ thit = temp_thit;
+ attr = temp_attr;
+ hitFound = true;
+ }
+ }
}
-
- return false;
+ return hitFound;
}
#endif // ANALYTICPRIMITIVES_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 905341d..3464682 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -20,7 +20,6 @@ using namespace DX;
void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(array, BottomLevelASType::Count>& geometryDescs)
{
D3D12_RAYTRACING_GEOMETRY_FLAGS geometryFlags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE;
-
{
// Triangle bottom-level AS contains a single plane geometry.
geometryDescs[BottomLevelASType::Triangle].resize(1); // only 1 triangle-type geometry: the plane
@@ -32,8 +31,17 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width / sizeof(Index);
+ geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex);
+
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer = { m_vertexBuffer.resource->GetGPUVirtualAddress(), 2 * sizeof(XMFLOAT3)};
}
{
@@ -51,7 +59,10 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + primitiveType * aabbDescTemplate.AABBs.AABBs.StrideInBytes;
+ }
}
}
@@ -70,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.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
+ bottomLevelInputs.Flags = buildFlags;
+ bottomLevelInputs.NumDescs = geometryDescs.size();
+ bottomLevelInputs.pGeometryDescs = geometryDescs.data();
// Query the driver for resource requirements to build an acceleration structure. We've done this for you.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
@@ -110,7 +125,8 @@ 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)
@@ -129,7 +145,7 @@ 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{};
+ return AccelerationStructureBuffers{scratch, bottomLevelAS, nullptr, bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes};
}
// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before.
@@ -181,7 +197,15 @@ 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;
+ instanceDesc.InstanceContributionToHitGroupIndex = 2;
+ instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB];
+
+ const XMVECTOR hoverAboveGround = XMLoadFloat3(&XMFLOAT3(0.0f, c_aabbWidth * 0.5f, 0.0f));
+ XMMATRIX mTransform = XMMatrixTranslationFromVector(hoverAboveGround);
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -204,7 +228,11 @@ 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.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
+ topLevelInputs.Flags = buildFlags;
+ topLevelInputs.NumDescs = BottomLevelASType::Count;
+ // CHECK and ADD Instance desc
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -218,7 +246,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.
{
@@ -233,6 +261,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");
}
@@ -261,7 +290,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
else // DirectX Raytracing
{
@@ -273,7 +302,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.
@@ -285,7 +314,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.
-
+ topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress();
+ topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress();
// Build acceleration structure.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -304,7 +335,7 @@ 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{};
+ return AccelerationStructureBuffers{ scratch, topLevelAS, instanceDescsResource, topLevelPrebuildInfo.ResultDataMaxSizeInBytes};
}
// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene.
@@ -320,12 +351,14 @@ 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 (int bottomLevel = 0; bottomLevel < BottomLevelASType::Count; bottomLevel++) {
+ bottomLevelAS[bottomLevel] = BuildBottomLevelAS(geometryDescs[bottomLevel]);
+ }
// Batch all resource barriers for bottom-level AS builds.
// This will Notifies the driver that it needs to synchronize multiple accesses to resources.
@@ -338,7 +371,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();
@@ -349,5 +382,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 (int bottomLevel = 0; bottomLevel < BottomLevelASType::Count; bottomLevel++) {
+ m_bottomLevelAS[bottomLevel] = bottomLevelAS[bottomLevel].accelerationStructure;
+ }
+ m_topLevelAS = topLevelAS.accelerationStructure;
+ //CHECK: WRAPPED_GPU_POINTER m_fallbackTopLevelAccelerationStructurePointer for FALLBACK
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
index 03a8c58..38c3475 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,13 @@ 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 = { m_hitGroupShaderTable->GetGPUVirtualAddress(), m_hitGroupShaderTable->GetDesc().Width, m_hitGroupShaderTableStrideInBytes};
// TODO-2.8: now fill in dispatchDesc->MissShaderTable
-
+ dispatchDesc->MissShaderTable = { m_missShaderTable->GetGPUVirtualAddress(), m_missShaderTable->GetDesc().Width, m_missShaderTableStrideInBytes};
// TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord
-
+ dispatchDesc->RayGenerationShaderRecord = { m_rayGenShaderTable->GetGPUVirtualAddress(), m_rayGenShaderTable->GetDesc().Width};
// We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA!
dispatchDesc->Width = m_width;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
index e3ff63c..296ca81 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -111,7 +111,10 @@ void DXProceduralProject::CreateConstantBuffers()
// structured buffers are for structs that have dynamic data (e.g lights in a scene, or AABBs in this case)
void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers()
{
-
+ auto device = m_deviceResources->GetD3DDevice();
+ auto numElements = m_aabbs.size();
+ auto frameCount = m_deviceResources->GetBackBufferCount();
+ m_aabbPrimitiveAttributeBuffer.Create(device, numElements, frameCount, L"AABB Primitive Attribute Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -164,6 +167,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.
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS =
+ XMMatrixMultiply(XMMatrixMultiply(mScale, mRotation), mTranslation);
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace =
+ XMMatrixInverse(nullptr, m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS);
};
UINT offset = 0;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..e118ecd 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);
@@ -110,12 +117,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..5e84714 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
@@ -29,7 +29,22 @@ 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.
{
+ for (UINT rayType = 0; rayType < RayType::Count; rayType++)
+ {
+ for (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) {
+ auto hitGroup = raytracingPipeline->CreateSubobject();
+ if (rayType == RayType::Radiance)
+ {
+ // We import the closest hit shader name
+ hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]);
+ }
+ // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type
+ hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primitiveType]);
+ hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primitiveType][rayType]);
+ hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE);
+ }
+ }
}
}
@@ -54,6 +69,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 (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) {
+ rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primitiveType]);
+ }
}
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
index 2dff8b5..873a330 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
@@ -7,67 +7,81 @@
// TODO-2.2: Create parts of the shader root signature.
void DXProceduralProject::CreateRootSignatures()
{
- auto device = m_deviceResources->GetD3DDevice();
+ auto device = m_deviceResources->GetD3DDevice();
- // Global Root Signature
- // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call.
- // Think of them as global function parameters.
- {
- // A descriptor range is like an array of descriptors of the same type (UAV, SRV, CBV).
- // The range has the following data: type of resource accessed, the number of descriptors, and the base register the shader
- // should look at to access these resources.
- CD3DX12_DESCRIPTOR_RANGE ranges[2];
- ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture
+ // Global Root Signature
+ // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call.
+ // Think of them as global function parameters.
+ {
+ // A descriptor range is like an array of descriptors of the same type (UAV, SRV, CBV).
+ // The range has the following data: type of resource accessed, the number of descriptors, and the base register the shader
+ // should look at to access these resources.
+ CD3DX12_DESCRIPTOR_RANGE ranges[2];
+ ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture
- // TODO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data.
+ // 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.
- // - The OutputView should correspond to the UAV range descriptor above (descriptor table), bound to register 0 of the UAV registers.
+ // TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots.
+ // * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are.
+ // - The OutputView should correspond to the UAV range descriptor above (descriptor table), bound to register 0 of the UAV registers.
// - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers.
// Note that since we initialize these as a range of size 2, then you should bind the entire range to register 1.
// This will automatically fill in registers 1 and 2.
- // - The AccelerationStructure should be init as SRV bound to register 0 of the SRV registers.
+ // - The AccelerationStructure should be init as SRV bound to register 0 of the SRV registers.
// - The SceneConstant should be init as a ConstantBufferView (CBV) bound to register 0 of the CBV registers.
// - The AABBAttributeBuffer should be init as SRV bound to register 3 of the SRV registers.
- // - Look up InitAsDescriptorTable(), InitAsShaderResourceView(), and InitAsConstantBuffer() in the DirectX documentation
- // to understand what to do.
+ // - 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
- CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
- // 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);
- }
+ CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
+ rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]);
+ rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]);
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0);
+ rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0);
+ rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3);
- // Local Root Signature
- // This is a root signature that enables a shader to have unique arguments that come from shader tables.
- // Think of them as local function parameters.
- {
- // Triangle geometry
- {
- namespace RootSignatureSlots = LocalRootSignature::Triangle::Slot;
- CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count];
- rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1);
- CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters);
- localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE;
- SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::Triangle]);
- }
+ // 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);
+ }
- // TODO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature
- // - Remember that the AABB holds 1 slot for Material Constants, and another 1 for the geometry instance.
- // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means.
- // - Use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps
+ // Local Root Signature
+ // This is a root signature that enables a shader to have unique arguments that come from shader tables.
+ // Think of them as local function parameters.
+ {
+ // Triangle geometry
+ {
+ namespace RootSignatureSlots = LocalRootSignature::Triangle::Slot;
+ CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count];
+ rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1);
+
+ CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters);
+ localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE;
+ SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::Triangle]);
+ }
+
+ // TODO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature
+ // - Remember that the AABB holds 1 slot for Material Constants, and another 1 for the geometry instance.
+ // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means.
+ // - Use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps
// to register 1, this overlap is allowed since we are talking about *local* root signatures
- // --> the values they hold will depend on the shader function the local signature is bound to!
- {
-
- }
- }
-}
+ // --> 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]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
index 150e92d..e8d5395 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]);
+ missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]);
+ shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0];
+ 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,6 +45,13 @@ void DXProceduralProject::BuildShaderTables()
}
// TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB.
+ for (UINT j = 0; j < IntersectionShaderType::Count; j++) {
+ for (UINT i = 0; i < RayType::Count; i++)
+ {
+ hitGroupShaderIDs_AABBGeometry[j][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[j][i]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[j][i]] = c_hitGroupNames_AABBGeometry[j][i];
+ }
+ }
};
@@ -95,7 +104,19 @@ 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 = 2;
+ UINT shaderRecordSize = shaderIDSize; // No root arguments
+
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
+
+ // Push back the shader record, which does not need any root signatures.
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[0], shaderRecordSize, nullptr, 0));
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[1], 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_missShaderTable = missShaderTable.GetResource();
+ m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize();
}
// Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature.
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..619512f 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);
+ float3 reflectedRay = normalize(reflect(incidentLightRay, normal)); // Normalized reflected ray
+ float3 reverseRayDirection = normalize(-WorldRayDirection()); // Normalized reverse ray direction
+ float clampedDotProduct = saturate(dot(reflectedRay, reverseRayDirection)); // Clamped dot product
+ return pow(clampedDotProduct, specularPower); // Is it float or float4?
}
// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
@@ -66,17 +69,32 @@ float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal
// InShadowRadiance from RaytracingHlslCompat.h tp dim down the diffuse color. The specular color should
// be completely black if the ray is a shadow ray.
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)
+ in float diffuseCoef = 1.0, in float specularCoef = 1.0, in float specularPower = 50)
{
- // Ambient component
- // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit
- float4 ambientColor = g_sceneCB.lightAmbientColor;
- float4 ambientColorMin = g_sceneCB.lightAmbientColor - 0.1;
- float4 ambientColorMax = g_sceneCB.lightAmbientColor;
- float a = 1 - saturate(dot(normal, float3(0, -1, 0)));
- ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a);
-
- return ambientColor;
+ // Ambient component
+ // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit
+ float4 ambientColor = g_sceneCB.lightAmbientColor;
+ float4 ambientColorMin = g_sceneCB.lightAmbientColor - 0.1;
+ float4 ambientColorMax = g_sceneCB.lightAmbientColor;
+ float a = 1 - saturate(dot(normal, float3(0, -1, 0)));
+ ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a);
+
+ // Diffuse
+ float3 incidentLightRay = normalize(HitWorldPosition() - g_sceneCB.lightPosition.xyz);
+ float lambCoeff = CalculateDiffuseCoefficient(incidentLightRay, normal);
+ float shadowColor = isInShadow ? InShadowRadiance : 1.0;
+ float4 diffuseColor = diffuseCoef * lambCoeff * g_sceneCB.lightDiffuseColor * albedo * shadowColor;
+
+ // Specular
+ // Black if Shadow
+ float4 specularColor = float4(0, 0, 0, 0);
+ if (!isInShadow)
+ {
+ float4 specCoeff = CalculateSpecularCoefficient(incidentLightRay, normal, specularPower);
+ specularColor = specularCoef * specCoeff * float4(1, 1, 1, 1);
+ }
+
+ return ambientColor + diffuseColor + specularColor;
}
//***************************************************************************
@@ -103,21 +121,21 @@ float4 TraceRadianceRay(in Ray ray, in UINT currentRayRecursionDepth)
RayPayload rayPayload = { float4(0, 0, 0, 0), currentRayRecursionDepth + 1 };
- // TraceRay() is a built-in DXR function. Lookup its documentation to see what it does.
- // To understand how a ray "finds out" what type of object it hit and therefore call the correct shader hitgroup, it indexes into the shader
- // table as follows:
- // hitgroup to choose = ptr to shader table + size of a shader record + (ray contribution + (geometry index * geometry stride) + instance contribution)
- // * `ray contribution` and `geometry stride` are given here.
- // * `ptr to shader table` + `size of a shader record` are given in BuildShaderTables() in DXR-ShaderTable.cpp
- // * `geometry index` is implicitly given when you build your bottom-level AS in BuildGeometryDescsForBottomLevelAS() in DXR-AccelerationStructure.cpp
- // * `instance contribution` is given in BuildBottomLevelASInstanceDescs() in DXR-AccelerationStructure.cpp
- // Essentially, when a ray hits a Geometry in a Bottom-Level AS Instance contained within a Top Level AS, it does the following:
- // (1) Identify which Instance of a BLAS it hit (Triangle or AABB) --> this gives it the `instance contribution`
- // (2) Identify which Geometry *inside* this Instance it hit
- // (If hit Triangle instance, then Triangle geom!, if hit AABB instance, then might be Sphere, Metaball, Fractal, etc..)
- // --> this gives it the `geometry index`
- // (3) Identify what type of Ray it is --> this is given right here, say a Radiance ray
- // (4) Combines all of these inputs to index into the correct shader into the hitgroup shader table.
+ // TraceRay() is a built-in DXR function. Lookup its documentation to see what it does.
+ // To understand how a ray "finds out" what type of object it hit and therefore call the correct shader hitgroup, it indexes into the shader
+ // table as follows:
+ // hitgroup to choose = ptr to shader table + size of a shader record + (ray contribution + (geometry index * geometry stride) + instance contribution)
+ // * `ray contribution` and `geometry stride` are given here.
+ // * `ptr to shader table` + `size of a shader record` are given in BuildShaderTables() in DXR-ShaderTable.cpp
+ // * `geometry index` is implicitly given when you build your bottom-level AS in BuildGeometryDescsForBottomLevelAS() in DXR-AccelerationStructure.cpp
+ // * `instance contribution` is given in BuildBottomLevelASInstanceDescs() in DXR-AccelerationStructure.cpp
+ // Essentially, when a ray hits a Geometry in a Bottom-Level AS Instance contained within a Top Level AS, it does the following:
+ // (1) Identify which Instance of a BLAS it hit (Triangle or AABB) --> this gives it the `instance contribution`
+ // (2) Identify which Geometry *inside* this Instance it hit
+ // (If hit Triangle instance, then Triangle geom!, if hit AABB instance, then might be Sphere, Metaball, Fractal, etc..)
+ // --> this gives it the `geometry index`
+ // (3) Identify what type of Ray it is --> this is given right here, say a Radiance ray
+ // (4) Combines all of these inputs to index into the correct shader into the hitgroup shader table.
TraceRay(g_scene,
RAY_FLAG_CULL_BACK_FACING_TRIANGLES,
TraceRayParameters::InstanceMask,
@@ -135,7 +153,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 rayPayload = { true };
+
+ TraceRay(g_scene,
+ RAY_FLAG_CULL_BACK_FACING_TRIANGLES,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, rayPayload);
+
+ return rayPayload.hit;
}
//***************************************************************************
@@ -149,9 +191,11 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
-
- // Write the color to the render target
- g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ uint2 index = uint2(DispatchRaysIndex().xy);
+ Ray ray = GenerateCameraRay(index, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
+ float4 reflectionColor = TraceRadianceRay(ray, 0);
+ // Write the color to the render target
+ g_renderTarget[DispatchRaysIndex().xy] = reflectionColor;
}
//***************************************************************************
@@ -170,7 +214,7 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
uint indicesPerTriangle = 3;
uint triangleIndexStride = indicesPerTriangle * indexSizeInBytes;
- // PrimitiveIndex() is the triangle index within the mesh. For Procedural primitives, this is the index into the AABB array defining the geometry.
+ // PrimitiveIndex() is the triangle index within the mesh. For Procedural primitives, this is the index into the AABB array defining the geometry.
uint baseIndex = PrimitiveIndex() * triangleIndexStride;
// Load up three 16 bit indices for the triangle.
@@ -179,8 +223,8 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
// Retrieve corresponding vertex normals for the triangle vertices.
float3 triangleNormal = g_vertices[indices[0]].normal;
- // This is the intersection point on the triangle.
- float3 hitPosition = HitWorldPosition();
+ // This is the intersection point on the triangle.
+ float3 hitPosition = HitWorldPosition();
// Trace a ray from the hit position towards the single light source we have. If on our way to the light we hit something, then we have a shadow!
Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) };
@@ -188,10 +232,10 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
// Reflected component ray.
float4 reflectedColor = float4(0, 0, 0, 0);
- if (l_materialCB.reflectanceCoef > 0.001 )
+ if (l_materialCB.reflectanceCoef > 0.001)
{
// Trace a reflection ray from the intersection points using Snell's law. The reflect() HLSL built-in function does this for you!
- // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions
+ // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions
Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), triangleNormal) };
float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth);
@@ -203,13 +247,14 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, triangleNormal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
float4 color = (phongColor + reflectedColor);
- // TODO: Apply a visibility falloff.
- // If the ray is very very very far away, tends to sample the background color rather than the color you computed.
- // This is to mimic some form of distance fog where farther objects appear to blend with the background.
- // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is.
- // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color.
- // When t is big, we want the background color to be more pronounced.
-
+ // TODO: Apply a visibility falloff.
+ // If the ray is very very very far away, tends to sample the background color rather than the color you computed.
+ // This is to mimic some form of distance fog where farther objects appear to blend with the background.
+ // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is.
+ // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color.
+ // When t is big, we want the background color to be more pronounced.
+ float t = RayTCurrent();
+ color = lerp(color, BackgroundColor, (1 - exp(-0.000001*pow(t, 3.0f))));
rayPayload.color = color;
}
@@ -227,9 +272,38 @@ 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 ray.
+ float4 reflectedColor = float4(0, 0, 0, 0);
+ if (l_materialCB.reflectanceCoef > 0.001)
+ {
+ // Trace a reflection ray from the intersection points using Snell's law. The reflect() HLSL built-in function does this for you!
+ // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions
+ Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), 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);
+ // TODO: Apply a visibility falloff.
+ // If the ray is very very very far away, tends to sample the background color rather than the color you computed.
+ // This is to mimic some form of distance fog where farther objects appear to blend with the background.
+ // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is.
+ // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color.
+ // When t is big, we want the background color to be more pronounced.
+ float t = RayTCurrent();
+ color = lerp(color, BackgroundColor, (1 - exp(-0.000001*pow(t, 3.0f))));
+ rayPayload.color = color;
}
+
//***************************************************************************
//**********************------ Miss shaders -------**************************
//***************************************************************************
@@ -240,14 +314,14 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive
[shader("miss")]
void MyMissShader(inout RayPayload rayPayload)
{
-
+ 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;
}
//***************************************************************************
@@ -275,20 +349,20 @@ void MyIntersectionShader_AnalyticPrimitive()
Ray localRay = GetRayInAABBPrimitiveLocalSpace();
AnalyticPrimitive::Enum primitiveType = (AnalyticPrimitive::Enum) l_aabbCB.primitiveType;
- // The point of the intersection shader is to:
- // (1) find out what is the t at which the ray hits the procedural
- // (2) pass on some attributes used by the closest hit shader to do some shading (e.g: normal vector)
+ // The point of the intersection shader is to:
+ // (1) find out what is the t at which the ray hits the procedural
+ // (2) pass on some attributes used by the closest hit shader to do some shading (e.g: normal vector)
float thit;
ProceduralPrimitiveAttributes attr;
if (RayAnalyticGeometryIntersectionTest(localRay, primitiveType, thit, attr))
{
PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex];
- // Make sure the normals are stored in BLAS space and not the local space
+ // 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
+ // thit is invariant to the space transformation
ReportHit(thit, /*hitKind*/ 0, attr);
}
}
@@ -299,6 +373,25 @@ void MyIntersectionShader_AnalyticPrimitive()
[shader("intersection")]
void MyIntersectionShader_VolumetricPrimitive()
{
+ Ray localRay = GetRayInAABBPrimitiveLocalSpace();
+ VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType;
+
+ // The point of the intersection shader is to:
+ // (1) find out what is the t at which the ray hits the procedural
+ // (2) pass on some attributes used by the closest hit shader to do some shading (e.g: normal vector)
+ float thit;
+ ProceduralPrimitiveAttributes attr;
+ if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime))
+ {
+ PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex];
+
+ // Make sure the normals are stored in BLAS space and not the local space
+ attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS);
+ attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal));
+
+ // thit is invariant to the space transformation
+ ReportHit(thit, /*hitKind*/ 0, attr);
+ }
}
#endif // RAYTRACING_HLSL
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
index 94bf5cc..b41129e 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 farInCycleTime = fmod(elapsedTime, cycleDuration) / cycleDuration;
+ farInCycleTime = (farInCycleTime <= 0.5f) ? 2 * farInCycleTime : 1 - 2 * (farInCycleTime - 0.5f);
+ return smoothstep(0, 1, farInCycleTime);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -130,9 +132,15 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics)
inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld)
{
Ray ray;
- ray.origin = float3(0.0f, 0.0f, 0.0f);
- ray.direction = normalize(float3(0.0f, 0.0f, 0.0f));
-
+ ray.origin = cameraPosition;
+ // Pixel width, height and depth
+ uint3 whd = DispatchRaysDimensions();
+ // Pixel to NDC
+ float3 dc = { ((2.0f * index.x) / whd.x) - 1.0f, 1.0f - ((2.0f * index.y) / whd.y) , 1.0f};
+ float4 ndc = normalize(float4(dc.x, dc.y, 1.0f, 1.0f));
+ // Projected Point
+ float4 pos = mul(ndc, projectionToWorld);
+ ray.direction = normalize(float3(pos.x, pos.y, pos.z));
return ray;
}
@@ -141,7 +149,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 cos_theta = saturate(dot(-I, N));
+ return f0 + (1 - f0)*pow(1 - cos_theta, 5);
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..daa6936 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,14 @@ 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)
+ return 0.0f;
+
+ float r = blob.radius;
+ float d = r - distance;
+ float x = d / r;
+ return 6 * (x*x*x*x*x) - 15 * (x*x*x*x) + 10 * (x*x*x);
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -60,9 +67,9 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps
{
float3 keyFrameCenters[N_METABALLS][2] =
{
- { float3(-0.3, -0.3, -0.4),float3(0.3,-0.3,-0.0) }, // begin center --> end center
+ { 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) }
};
// Metaball field radii of max influence
@@ -83,6 +90,16 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
{
tmin = INFINITY;
tmax = -INFINITY;
+ for (UINT i = 0; i < N_METABALLS; i++) {
+ float temp_min, temp_max;
+ if (RaySolidSphereIntersectionTest(ray, temp_min, temp_max, blobs[i].center, blobs[i].radius))
+ {
+ tmin = min(temp_min, tmin);
+ tmax = max(temp_max, 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,9 +117,32 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
// If this condition fails, keep raymarching!
bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime)
{
- thit = 0.0f;
- attr.normal = float3(0.0f, 0.0f, 0.0f);
- return false;
+ // Initialize a metaball array
+ Metaball blobs[N_METABALLS];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 11.0f);
+
+ float tmin, tmax;
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+
+ UINT MAX_STEPS = 128;
+ const float Threshold = 0.2f;
+ float step_size = (tmax - tmin) / 128;
+ float t = tmin;
+ while (t < tmax) {
+ float3 pos = ray.origin + t * ray.direction;
+ float potential = CalculateMetaballsPotential(pos, blobs);
+ if (potential > Threshold) {
+ float3 sur_normal = CalculateMetaballsNormal(pos, blobs);
+ if (is_a_valid_hit(ray, t, sur_normal))
+ {
+ thit = t;
+ attr.normal = sur_normal;
+ return true;
+ }
+ }
+ t += step_size;
+ }
+ return false;
}
#endif // VOLUMETRICPRIMITIVESLIBRARY_H
\ No newline at end of file