diff --git a/.gitignore b/.gitignore
index 19590e8..b32318c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ Packages
UpgradeLog.*
*/UpgradeLog.*
FallbackLayer_Build/*
-.FallbackLayer_Build/*
\ No newline at end of file
+.FallbackLayer_Build/*
+%USERPROFILE
diff --git a/README.md b/README.md
index b0189d0..db48508 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,129 @@
**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)
+Peyman Norouzi
+* [LinkedIn](https://www.linkedin.com/in/peymannorouzi)
+* Tested on: Windows 10, Xeon E5 @ 3.70GHz 32GB, GTX 1070 8192MB (SIG LAB Second Computer from Left)
+
+## Ray Tracing:
+
+
+
+
+
+In computer graphics, ray tracing is a rendering technique for generating photorealistic images. In this approach, we trace paths of light as they leave from a camera as pixels in an image plane and simulating the effects of them encountering with virtual objects. In my one of previous projects, I did implement Path tracer on CUDA which is a subset of the ray-tracing method. You can look at the previous project [here](https://github.com/pnorouzi/Project3-CUDA-Path-Tracer).
+
+
+## Table of Contents:
+
+- [DirectX Procedural Ray Tracing](#directx-procedural-ray-tracing)
+ * [DXR Implementation](#dxr-implementation)
+- [Perfomance of the Implementation](#perfomance-of-the-implementation)
+- [Cool Renders](#cool-renders)
+- [Bloopers](#bloopers)
+- [Sources](#sources)
+
+
+## DirectX Procedural Ray Tracing:
+
+As explained earlier, Ray Tracing is a process in which we can produce/render photorealistic images by firing rays through an imaginary camera and then following the illumination of objects in the scene with a set of pre-defined rules. The process is similar to path tracing except for the fact that it is deterministic and the tracing process only needs a single pass over the scene. The basic idea of the implementation can be seen below:
+
+
+
+
+
+
+### DXR Implementation:
+
+I used the DirectX 12 Raytracing (DXR) API for the implementation. The basic idea and pipeline of the implementation can be understood using the following diagram:
+
+
+
+
+
+In this project, we use a *minimum depth of 3*, which means we will be calling `TraceRay()` roughly 3 times. We will be tracing the following:
+
+1. a **primary (radiance) ray** generated from the camera
+2. a **shadow ray** just in case the ray hits a geometry on its way to the light source
+3. a **reflection** ray in case the material of the object is reflective
+
+Therefore, the lifecycle of a single ray can be thought of as follows:
+
+1. generate a ray, see if it hits something
+2. if it hits something, then attempt to *light/color* it
+ * attempting to color that hit point is equivalent to **tracing that ray towards the light source**.
+ * if that ray hits *another* object on its way to the light, then the region is effectively shadowed
+ * if not, then we successfully colored that point
+3. if at any point we hit a reflective material, then trace another ray in the reflected direction and repeat the process
+
+To be able to implement such an algorithm on the GPU (to make it fast and performant) we need to make all of the following data available in the GPU before any tracing happens:
+
+ * all geometries must be positioned within an acceleration structure (KD-Tree, Bounding Volume Hierarchy, or whatever your choice is..)
+ * the camera must be set up
+ * the light sources must be configured
+ * the shading logic must be configured
+ * and the output buffer must be ready
+
+In essence, the entire *ray tracing pipeline* must be ready on the GPU. In rasterization, this does not need to hold: you can render shadows *after* you render diffuse colors for example. So a good chunk of DXR is spent setting that up from the CPU. Once the GPU knows about all the details on the pipeline, it can execute the ray tracing algorithm. **This is where the DXR API would be very helpful since it would allow us to set up all of these things easier**.
+
+## Perfomance of the Implementation:
+
+One of the more important parameters of the implementation is the maximum depth of ray recursion. All of the renders in the repo are made using 3 as the maximum depth of recursion. By increasing the depth further than 3 we can see the performance takes a hit which makes sense. You can see the effects of recursion in the following plots:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Cool Renders:
+
+Here are some cool renders that shows how powerfull DXR ray tracing can be. For all of the renders bellow, the maximum Recurssion Depth is 3.
+
+In the following the light source is dynamically rotating slowly across the scene creating realistic shadows of the objects in the scene:
+
+
+
+
+
+
+In addition to moving the light source, in the following render, the geometries are dynamically moving across the scene:
+
+
+
+
+
+
+
+
+## Bloopers:
+
+Here are some bloopers showing that everyone can and will make mistakes along the way. Especially in this project, there can be so many places that you can make a mistake. :)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Sources:
+
+[DXR Documentation](https://docs.microsoft.com/en-us/windows/win32/directx)
+
+[Ray Tracing wiki](https://en.wikipedia.org/wiki/Ray_tracing_(graphics))
-### (TODO: Your README)
-Include screenshots, analysis, etc. (Remember, this is public, so don't put
-anything here that you don't want to share with the world.)
diff --git a/images/bloop1.gif b/images/bloop1.gif
new file mode 100644
index 0000000..66000b7
Binary files /dev/null and b/images/bloop1.gif differ
diff --git a/images/bloop2.gif b/images/bloop2.gif
new file mode 100644
index 0000000..603078a
Binary files /dev/null and b/images/bloop2.gif differ
diff --git a/images/bloop3.gif b/images/bloop3.gif
new file mode 100644
index 0000000..e83ff24
Binary files /dev/null and b/images/bloop3.gif differ
diff --git a/images/dr.png b/images/dr.png
new file mode 100644
index 0000000..ffcb1b6
Binary files /dev/null and b/images/dr.png differ
diff --git a/images/fps.png b/images/fps.png
new file mode 100644
index 0000000..c9cedd9
Binary files /dev/null and b/images/fps.png differ
diff --git a/images/light.gif b/images/light.gif
new file mode 100644
index 0000000..4e16c8e
Binary files /dev/null and b/images/light.gif differ
diff --git a/images/move.gif b/images/move.gif
new file mode 100644
index 0000000..7c8ed51
Binary files /dev/null and b/images/move.gif differ
diff --git a/images/mpr.png b/images/mpr.png
new file mode 100644
index 0000000..ddf1451
Binary files /dev/null and b/images/mpr.png differ
diff --git a/images/top.gif b/images/top.gif
new file mode 100644
index 0000000..fe8b37b
Binary files /dev/null and b/images/top.gif differ
diff --git a/images/top1.gif b/images/top1.gif
new file mode 100644
index 0000000..5aca02c
Binary files /dev/null and b/images/top1.gif differ
diff --git a/images/top2.gif b/images/top2.gif
new file mode 100644
index 0000000..9003662
Binary files /dev/null and b/images/top2.gif differ
diff --git a/plots.xlsx b/plots.xlsx
new file mode 100644
index 0000000..561ce57
Binary files /dev/null and b/plots.xlsx differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..bfb48e4 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -168,16 +168,44 @@ bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out Procedur
// Define the spheres in local space (within the aabb)
float3 center = float3(-0.2, 0, -0.2);
float radius = 0.7f;
+ float3 center1 = float3(0.5, 0.5, -0.5);
+ float radius1 = 0.4f;
+ float3 center2 = float3(0.1, 0.5, 0.1);
+ float radius2 = 0.2f;
thit = RayTCurrent();
- float tmax;
+ ProceduralPrimitiveAttributes temp_attr;
+
+ bool intersected = false;
+
+ float tmax, temp_thit;
+
if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
{
- return true;
+ intersected = true;
}
- return false;
+ if (RaySphereIntersectionTest(ray, temp_thit, tmax, temp_attr, center1, radius1))
+ {
+ if ((intersected && (thit > temp_thit)) || !intersected) {
+ attr = temp_attr;
+ thit = temp_thit;
+ }
+ intersected = true;
+ }
+
+ if (RaySphereIntersectionTest(ray, temp_thit, tmax, temp_attr, center2, radius2))
+ {
+ if ((intersected && (thit > temp_thit)) || !intersected) {
+ attr = temp_attr;
+ thit = temp_thit;
+ }
+ intersected = true;
+ }
+
+
+ return intersected;
}
#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..bc410a8 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -34,6 +34,16 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress();
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ //
+ geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index);
+ geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex);
+
}
{
@@ -52,6 +62,13 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + (AnalyticPrimitive::AABB) * sizeof(D3D12_RAYTRACING_AABB);
+ //geometryDescs[BottomLevelASType::AABB][1].AABBs.AABBs.StartAddress = m_aabbBuffer.resource->GetGPUVirtualAddress() + (AnalyticPrimitive::Spheres) * sizeof(D3D12_RAYTRACING_AABB);
+ //geometryDescs[BottomLevelASType::AABB][2].AABBs.AABBs.StartAddress = m_aabbBuffer.resource->GetGPUVirtualAddress() + (VolumetricPrimitive::Metaballs) * sizeof(D3D12_RAYTRACING_AABB);
+
+ for (auto primitiveType = 0; primitiveType < IntersectionShaderType::TotalPrimitiveCount; primitiveType++) {
+ (geometryDescs[BottomLevelASType::AABB][primitiveType]).AABBs.AABBs.StartAddress = m_aabbBuffer.resource->GetGPUVirtualAddress() + primitiveType * aabbDescTemplate.AABBs.AABBs.StrideInBytes;
+ }
}
}
@@ -71,6 +88,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// 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.NumDescs = geometryDescs.size();
+ bottomLevelInputs.pGeometryDescs = geometryDescs.data();
+ bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
+ bottomLevelInputs.Flags = buildFlags;
// Query the driver for resource requirements to build an acceleration structure. We've done this for you.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
@@ -111,6 +133,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// 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 +154,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.
@@ -182,6 +207,36 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev
// * Make each instance hover above the ground by ~ half its width
{
+ // Make the plane a little larger than the actual number of primitives in each dimension.
+ const XMUINT3 NUM_AABB = XMUINT3(700, 1, 700);
+ const XMFLOAT3 fWidth = XMFLOAT3(
+ NUM_AABB.x * c_aabbWidth + (NUM_AABB.x - 1) * c_aabbDistance,
+ NUM_AABB.y * c_aabbWidth + (NUM_AABB.y - 1) * c_aabbDistance,
+ NUM_AABB.z * c_aabbWidth + (NUM_AABB.z - 1) * c_aabbDistance);
+ const XMVECTOR vWidth = XMLoadFloat3(&fWidth);
+
+ auto& instanceDesc = instanceDescs[BottomLevelASType::AABB];
+ instanceDesc = {};
+ instanceDesc.InstanceMask = 1;
+ instanceDesc.InstanceContributionToHitGroupIndex = 2;
+ instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB];
+
+ // Calculate transformation matrix.
+ // We multiply the width by -0.5 in the x,z plane because we want the middle of the plane
+ // (which is currently expanded in the positive x,z plane) to be centered.
+
+ //////////////////////////
+ //const XMVECTOR vBasePosition = vWidth * XMLoadFloat3(&XMFLOAT3(0.0f, 0.5f, 0.0f));
+
+ // Scale in XZ dimensions.
+ //XMMATRIX mScale = XMMatrixScaling(fWidth.x, fWidth.y, fWidth.z);
+ //XMMATRIX mTranslation = XMMatrixTranslationFromVector(vBasePosition);
+ //XMMATRIX mTransform = mScale * mTranslation;
+
+ XMMATRIX mTransform = XMMatrixTranslationFromVector(XMLoadFloat3(&XMFLOAT3(0.0f, c_aabbWidth * 0.5f, 0.0f)));
+
+ // Store the transform in the instanceDesc.
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -204,7 +259,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.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
+ topLevelInputs.Flags = buildFlags;
+ topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ topLevelInputs.NumDescs = BottomLevelASType::Count;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -218,7 +277,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0);
// TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data.
-
+ AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource");
+
// Allocate space for the top-level AS.
{
@@ -233,6 +293,8 @@ 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 +323,9 @@ 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 +337,8 @@ 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,9 +348,13 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
m_fallbackTopLevelAccelerationStructurePointer = CreateFallbackWrappedPointer(topLevelAS.Get(), numBufferElements);
}
- // TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.
+ // TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESCD3D12_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 +373,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 +389,16 @@ 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.
@@ -338,7 +411,8 @@ 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();
@@ -350,4 +424,9 @@ void DXProceduralProject::BuildAccelerationStructures()
// 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..a1bc89e 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
@@ -22,7 +22,9 @@ 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 +51,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 +62,20 @@ 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..1c7a8f7 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -112,6 +112,11 @@ void DXProceduralProject::CreateConstantBuffers()
void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers()
{
+ auto device = m_deviceResources->GetD3DDevice();
+ auto frameCount = m_deviceResources->GetBackBufferCount();
+ auto framesize = m_aabbs.size();
+ m_aabbPrimitiveAttributeBuffer.Create(device, framesize, frameCount, L"Primitive AABB Buffer");
+
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -164,6 +169,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;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..94e3f34 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -87,6 +87,14 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
auto InitializeAABB = [&](auto& offsetIndex, auto& size)
{
D3D12_RAYTRACING_AABB aabb{};
+ aabb.MinX = offsetIndex.x*stride.x + basePosition.x;
+ aabb.MinY = offsetIndex.y*stride.y + basePosition.y;
+ aabb.MinZ = offsetIndex.z*stride.z + basePosition.z;
+
+ aabb.MaxX = aabb.MinX + size.x;
+ aabb.MaxY = aabb.MinY + size.y;
+ aabb.MaxZ = aabb.MinZ + size.z;
+
return aabb;
};
m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount);
@@ -110,12 +118,16 @@ 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(), IntersectionShaderType::TotalPrimitiveCount * sizeof(D3D12_RAYTRACING_AABB), &m_aabbBuffer.resource);
+ 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()
{
-
+ DXProceduralProject::BuildPlaneGeometry();
+ DXProceduralProject::BuildProceduralGeometryAABBs();
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
index 33899bd..9ab0b8d 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
@@ -29,7 +29,23 @@ 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 primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) {
+ for (UINT rayType = 0; rayType < RayType::Count; rayType++)
+ {
+ 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 +70,16 @@ 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();
+
+ // This is the triangle local root signature you already filled in before.
+ 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..f87969e 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
@@ -21,7 +21,7 @@ void DXProceduralProject::CreateRootSignatures()
// TODO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data.
// This will effectively put the indices at register 1, and the vertices at register 2.
-
+ ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 1);
// TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots.
// * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are.
@@ -39,7 +39,11 @@ void DXProceduralProject::CreateRootSignatures()
// t registers --> SRV
// b registers --> CBV
CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
-
+ rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]);
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0);
+ 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);
SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature);
@@ -67,7 +71,14 @@ void DXProceduralProject::CreateRootSignatures()
// to register 1, this overlap is allowed since we are talking about *local* root signatures
// --> the values they hold will depend on the shader function the local signature is bound to!
{
-
+ namespace RootSignatureSlots = LocalRootSignature::AABB::Slot;
+ CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count];
+ rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1);
+ rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 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..b06ad04 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
@@ -29,12 +29,20 @@ void DXProceduralProject::BuildShaderTables()
rayGenShaderID = stateObjectProperties->GetShaderIdentifier(c_raygenShaderName);
shaderIdToStringMap[rayGenShaderID] = c_raygenShaderName;
+
+
// 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;
+ for (UINT i = 0; i < RayType::Count; i++) {
+ missShaderIDs[i] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[i]);
+ shaderIdToStringMap[missShaderIDs[i]] = c_missShaderNames[i]; //?
+ }
+
+
// 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 +51,14 @@ void DXProceduralProject::BuildShaderTables()
}
// TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB.
+
+ for (UINT i = 0; i < IntersectionShaderType::Count; i++) {
+ for (UINT j = 0; j < RayType::Count; j++)
+ {
+ hitGroupShaderIDs_AABBGeometry[i][j] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[i][j]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[i][j]] = c_hitGroupNames_AABBGeometry[i][j];
+ }
+ }
};
@@ -94,8 +110,25 @@ 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
+
+ // The RayGen shader table contains a single ShaderRecord: the one single raygen shader!
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
+
+ // Push back the shader record, which does not need any root signatures.
+ for (UINT i = 0; i < RayType::Count; i++) {
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[i], 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..dce14fe 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,11 @@ 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 Ray_reflected = normalize(reflect(incidentLightRay, normal));
+ float3 reverseRayDirection = normalize(-WorldRayDirection());
+
+ return pow(saturate(dot(Ray_reflected, reverseRayDirection)) , specularPower);
}
// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
@@ -76,7 +80,26 @@ 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;
+ float3 hit_pos = HitWorldPosition();
+ float3 incident_ray = normalize(hit_pos - g_sceneCB.lightPosition.xyz);
+
+ float dif_coef = CalculateDiffuseCoefficient(incident_ray, normal);
+ float4 spec_coef = CalculateSpecularCoefficient(incident_ray, normal, specularPower);
+
+
+
+ float4 diffuse_color = dif_coef* diffuseCoef * albedo * g_sceneCB.lightDiffuseColor;
+ float4 specular_color = spec_coef * specularCoef;
+
+ if (isInShadow) {
+
+ diffuse_color *= InShadowRadiance;
+ specular_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
+
+ }
+
+
+ return diffuse_color + specular_color + ambientColor;
}
//***************************************************************************
@@ -135,7 +158,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 true;
+ }
+
+ // 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 | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, rayPayload);
+
+ return rayPayload.hit;
}
//***************************************************************************
@@ -149,9 +196,10 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
-
+ Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
// Write the color to the render target
- g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ g_renderTarget[DispatchRaysIndex().xy] = TraceRadianceRay(ray, 0);
+
}
//***************************************************************************
@@ -210,6 +258,9 @@ 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() / 1000.0f;
+ color = lerp(color, BackgroundColor, 1 - exp(-1.0*pow(t, 3.0f)));
rayPayload.color = color;
}
@@ -228,6 +279,35 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
+ float3 hitPosition = HitWorldPosition();
+
+ float3 normal = attr.normal;
+
+ // 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) };
+ bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth);
+
+ 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(), normal) };
+ float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth);
+
+ float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), normal, l_materialCB.albedo.xyz);
+ reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor;
+ }
+
+ // Calculate final color.
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, normal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ float4 color = (phongColor + reflectedColor);
+
+ float t = RayTCurrent() / 1000.0f;
+ color = lerp(color, BackgroundColor, 1 - exp(-1.0*pow(t, 3.0f)));
+ rayPayload.color = color;
+
}
//***************************************************************************
@@ -240,14 +320,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;
}
//***************************************************************************
@@ -299,6 +379,24 @@ 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..f23afee 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
@@ -68,7 +68,11 @@ 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 interpolant = fmod(elapsedTime, cycleDuration) / cycleDuration;
+ /////////////////
+ float cur_cycle = interpolant <= 0.5f ? interpolant : 1 - interpolant;
+
+ return smoothstep(0, 1, cur_cycle);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -130,8 +134,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;
+
+ float x = 2.0 * (float(index.x) / DispatchRaysDimensions().x) - 1;
+ float y = 1- 2.0 * (float(index.y) / DispatchRaysDimensions().y);
+ float4 local_cor = float4(x, y, 1.0f, 1.0f);
+ float4 world_cor = mul(local_cor, projectionToWorld);
+ world_cor /= world_cor.w;
+
+ ray.direction = normalize(world_cor.xyz - cameraPosition);
return ray;
}
@@ -141,7 +152,12 @@ 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_the = saturate(dot(-I, N));
+ float3 output = f0 + (1 - f0)*pow(1 - cos_the, 5);
+
+ return output;
+
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..5c1acde 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,19 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float Distance = distance(position,blob.center);
+ float r = blob.radius;
+ float x = (r - Distance)/r;
+
+ if (Distance >= r)
+ return 0.0f;
+ else if (Distance == r)
+ return 1.0f;
+
+ //float d = r - distance;
+ //float x = d / r;
+
+ return (6 * pow(x, 5)) - (15 * pow(x, 4)) + (10 * pow(x, 3));
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -83,6 +95,18 @@ 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 tmin_temp, tmax_temp;
+ if (RaySolidSphereIntersectionTest(ray, tmin_temp, tmax_temp, blobs[i].center, blobs[i].radius)) {
+ tmin = min(tmin, tmin_temp);
+ tmax = max(tmax, tmax_temp);
+ }
+ }
+
+ ///////////////////
+ //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 +124,37 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
// If this condition fails, keep raymarching!
bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime)
{
- thit = 0.0f;
- attr.normal = float3(0.0f, 0.0f, 0.0f);
- return false;
+
+ Metaball blobs[N_METABALLS];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 8.0f);
+
+ float tmin, tmax;
+
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+
+ uint steps = 128;
+ float threshold = 0.5f;
+ float t_point = tmin;
+ float stride = (tmax - tmin) / (steps * 1.0f);
+
+ while (t_point <= tmax) {
+ float3 position = ray.origin + (t_point * ray.direction);
+ float potential = CalculateMetaballsPotential(position, blobs);
+ float3 normal = CalculateMetaballsNormal(position, blobs);
+ if (potential > threshold && is_a_valid_hit(ray, t_point, normal))
+ {
+
+ thit = t_point;
+ attr.normal = normal;
+ return true;
+
+ }
+
+ t_point += stride;
+ }
+
+ return false;
+
}
#endif // VOLUMETRICPRIMITIVESLIBRARY_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/src - Shortcut.lnk b/src/D3D12RaytracingProceduralGeometry/src - Shortcut.lnk
new file mode 100644
index 0000000..9d1fe58
Binary files /dev/null and b/src/D3D12RaytracingProceduralGeometry/src - Shortcut.lnk differ