diff --git a/README.md b/README.md
index b0189d0..986521c 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,43 @@
**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)
+* Tabatha Hickman
+ * LinkedIn: https://www.linkedin.com/in/tabatha-hickman-335987140/
+* Tested on: SIGLAB computer
-### (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.)
+## DirectX Procedural Raytracing
+
+This project utilizes Microsoft's DirectX to perform raytracing of both triangle meshes and procedural geometries on the GPU.
+
+Raytracing involves shooting rays from the camera to each pixel on the screen, then deterministically calculating intersections, reflections, and shadows. Each ray is traced through the scene only once, and terminates either if it misses all geometry or if we have reached a defined maximum recursion depth. The rays generated from the camera are called radiance rays and the rays shot from each intersection to the light source in order to determine coloring are called shadow rays.
+
+In order to have all this computation occur on the GPU, we need to set up several things on the CPU side.
+
+To send data from the CPU to the GPU, we allocate and upload buffers. For all the scene information (camera, lights, material colors, and transforms), we put this all in one struct and use a Constant Buffer, a DXR GpuUploadBuffer that holds only one struct, to upload it. The only other information we need to send to the GPU are the transformations from local geometry space to BLAS space and vice versa (information on BLASes to come). For this we use a StructuredBuffer, which can hold multiple structs, so we can send transformations for each of the instances of geometry.
+
+We also want to set up global and local parameters in the GPU shaders, which are called root signatures. This is accomplished by creating either constant values or resources on the CPU and creating descriptors to those resources. The descriptors can be of type UniformAccessView, ConstantBufferView, or ShaderResourceView. Each descriptor type has its own shader registry and we set up the descriptors so that each parameter has its own shader registered counting up from 0 for the type of descriptor it is. In other words we can have a UAV descriptor at register 0 and a SRV descriptor at register 0 because they are in different registrys as different types, but if we add another SRV descriptor, it goes in register 1.
+
+Now for the raytracing specifics. Each object needs a hitgroup to define how intersections, reflection, and shadows will be calculated for it. The hitgroup includes a closest hit shader, used for radiance rays, at least one intersection shader, and an optional any hit shader. We import shaders (by filename) for some set of these types for each combination of geometry and ray type. We also connect any relevant local root signatures to these shaders. Then, we create a shader table, one for each type of shader (raygen, miss, or hitgroup). These basically organize all the shaders into a sort of buffer so that we know which shaders are needed for each ray type and primitive type.
+
+We also need to know some information about the geometry, procedural or not. For the triangle-based meshes, we need index and vertex buffers, and for the procedural geometries, we need to define their bounding boxes in the x, y, and z planes. (These buffers should already have corresponding root signatures declared for them.)
+
+To optimize the raytracing, we can use an acceleration structure. DXR has a built in acceleration structure that involves top-level structures (TLAS) which hold instances of bottom-level structures (BLAS) which hold descriptors of geometry. We just need to create, set up, and pass geometry to these structures, and DXR will do the rest. For this project, our scene has a triangle mesh plane and three procedural geometries. Therefore, we define one TLAS with two instances of BLASes, one that holds triangles, and the other that holds AABBs, or bounding boxes with data about procedural geometry. All of these instances and descriptors can be created by making DXR defined structs and filling in their parameters.
+
+All of these tasks are performed by sending them as computations to a command list, which executes and calls DispatchRays to begin the actual raytracing.
+
+Now for the GPU side, where we define kernels for tracing rays, computing intersections, and coloring surfaces.
+
+We need to write a kernel which generates rays from the camera (the ray origin is its world space position) to each pixel in the screen. Using the id of any given ray, we get its location in pixel space using the dimensions of the screen. Then, we convert that into NDC space and then into world space so that we can get the direction of the ray. We also utilize the DXR function TraceRay() to call one of our intersection shaders based on what type of ray (radiance or shadow) we encounter. We must implement shaders to determine what happens to the ray based on what we hit. For example, a miss shader simply colors the pixel with the background color. Intersection shaders find all intersections of the ray with all geometry in the scene. Closest hit shaders concern themselves only with the intersection closest to the camera (with the minimum t value) and will shoot shadow rays and reflected rays.
+
+One of the more fun parts of this project was writing a function for intersection with metaballs (which would be used in intersection shaders). Given a set of balls, each of which having a center and a radius, we find all the intersection points of a given ray with these spheres and determine which one is the first (the entrance point, or minimum t value) and which one is the last (the exit point, or maximum t value). Then we divide the distance between these two points into incremental steps, and go through these steps one by one. At each point, we calculate a potential which is based on the point's distance from the center of any sphere. If the potential is greater than a threshhold we stop and use this as the intersection point, otherwise we go to the next step along the ray and try again. We also animated the metaballs in a cyclic pattern (using a simple sine function) so that we could see their unique deformations at work.
+
+To color our surfaces, we used a combination of diffuse and specular surface shading. Reflected colors were determined by summing ambient, diffuse (Lambertian), and specular (Phong) components. Then, using Schlick's approximation for Fresnel reflection, we determine a good ratio of the reflected color and the diffuse color and use this weighted combination as the surface color. Finally, we achieve a slight distance falloff toward the background by interpolating between the background color and the computed color.
+
+## Performance Testing
+
+Below is a chart of the minimum recorded FPS while running the raytracer vs the maximum recursion depth allowed. I'd like to note that the FPS followed a similar behavior over time (with all of camera, geometry, and lights being animated) for all of the tested recursion depths, starting at its minimum, increasing to 200-250 FPS, then decreasing back to about 30 FPS more than its minimum. As you can see, a recursion depth of 3 is significantly faster than depths of 4-10, but of the results from 4-10 there isn't much of a trend. One would expect to see some sort of steady decrease in FPS as recursion depth increases because more calculations must be done for each bounce, but I suspect that because there is so little geometry in the scene, most of the rays terminate due to misses after 2-3 bounces, so we don't see very much of a change.
+
+
diff --git a/images/chart.JPG b/images/chart.JPG
new file mode 100644
index 0000000..6b47694
Binary files /dev/null and b/images/chart.JPG differ
diff --git a/images/final.PNG b/images/final.PNG
new file mode 100644
index 0000000..b8a486d
Binary files /dev/null and b/images/final.PNG differ
diff --git a/images/lightAngle.PNG b/images/lightAngle.PNG
new file mode 100644
index 0000000..844575f
Binary files /dev/null and b/images/lightAngle.PNG differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..49bf5de 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -169,15 +169,40 @@ bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out Procedur
float3 center = float3(-0.2, 0, -0.2);
float radius = 0.7f;
- thit = RayTCurrent();
+ float3 center2 = float3(0.7, 0, 0.7);
+ float radius2 = 0.2f;
- float tmax;
- if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
+ float3 center3 = float3(0.3, 0.7, 0.3);
+ float radius3 = 0.4f;
+
+ thit = INFINITY;
+ float tmax, thitTemp;
+ ProceduralPrimitiveAttributes attrTemp;
+ bool hit = false;
+ if (RaySphereIntersectionTest(ray, thitTemp, tmax, attrTemp, center, radius) && thitTemp < thit)
+ {
+ thit = thitTemp;
+ attr = attrTemp;
+ hit = true;
+ }
+ if (RaySphereIntersectionTest(ray, thitTemp, tmax, attrTemp, center2, radius2) && thitTemp < thit)
+ {
+ thit = thitTemp;
+ attr = attrTemp;
+ hit = true;
+ }
+ if (RaySphereIntersectionTest(ray, thitTemp, tmax, attrTemp, center3, radius3) && thitTemp < thit)
{
- return true;
+ thit = thitTemp;
+ attr = attrTemp;
+ hit = true;
}
- return false;
+ if (!hit)
+ {
+ thit = RayTCurrent();
+ }
+ return hit;
}
#endif // ANALYTICPRIMITIVES_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
index 4a8b9ab..b9b60d9 100644
--- a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
+++ b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
@@ -50,6 +50,7 @@
true
+ C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64;$(LibraryPath)
false
@@ -60,6 +61,7 @@
true
+ C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64;$(LibraryPath)
@@ -78,6 +80,8 @@
true
d3d12.lib;dxgi.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
d3d12.dll
+
+
true
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 084077a..f69a067 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -26,12 +26,22 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress())
- // The number of elements of a D3D12 resource can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width)
+ // * Remember to use m_indexBuffer and m_vertexBuffer to get pointers to the data.
+ // * GPUVirtualAddresses can be accessed from a D3D12Resource using GetGPUVirtualAddress() (e.g m_vertexBuffer.resource->GetGPUVirtualAddress())
+ // * The *total size* of the buffer can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width)
+ // * We filled in the format of the buffers to avoid confusion.
auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0];
geometryDesc = {};
-
+ geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
+ geometryDesc.Flags = geometryFlags;
+ geometryDesc.Triangles.Transform3x4 = NULL; // assuming vertices given world space positions
+ geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT;
+ geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;
+ geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index);
+ geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex);
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex);
}
{
@@ -49,7 +59,11 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + i * sizeof(D3D12_RAYTRACING_AABB);
+ }
}
}
@@ -68,7 +82,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// Again, these tell the AS where the actual geometry data is and how it is laid out.
// TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout.
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs;
-
+ bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
+ bottomLevelInputs.Flags = buildFlags;
+ bottomLevelInputs.NumDescs = geometryDescs.size();
+ bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ bottomLevelInputs.pGeometryDescs = geometryDescs.data();
// Query the driver for resource requirements to build an acceleration structure. We've done this for you.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
@@ -87,7 +105,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// Allocate resources for acceleration structures as a UAV --> this will prepare bottomLevelAS for us.
// Acceleration structures can only be placed in resources that are created in the default heap (or custom heap equivalent).
- // Default heap is OK since the application doesn’t need CPU read/write access to them.
+ // Default heap is OK since the application doesn�t need CPU read/write access to them.
// The resources that will contain acceleration structures must be created in the state D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
// and must have resource flag D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS. The ALLOW_UNORDERED_ACCESS requirement simply acknowledges both:
// - the system will be doing this type of access in its implementation of acceleration structure builds behind the scenes.
@@ -108,7 +126,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc.
// Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.
// This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls.
-
+ bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress();
+ //bottomLevelBuildDesc.SourceAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress();
+ bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
// Fill up the command list with a command that tells the GPU how to build the bottom-level AS.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -127,7 +147,13 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// the AccelerationStructureBuffers struct so the top-level AS can use it!
// Don't forget that this is the return value.
// Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h
- return AccelerationStructureBuffers{};
+ AccelerationStructureBuffers asBuffer = {};
+ asBuffer.accelerationStructure = bottomLevelAS;
+ asBuffer.instanceDesc = nullptr; //not top level AS
+ asBuffer.ResultDataMaxSizeInBytes = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+ asBuffer.scratch = scratch;
+
+ return asBuffer;
}
// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before.
@@ -179,7 +205,16 @@ 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 hoverPos = XMLoadFloat3(&XMFLOAT3(0.0f, c_aabbWidth * 0.5f, 0.0f));
+ XMMATRIX mTranslation = XMMatrixTranslationFromVector(hoverPos);
+ // Store the transform in the instanceDesc.
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTranslation);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -202,7 +237,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.NumDescs = 2;
+ topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ //topLevelInputs.InstanceDescs = ; DO DOWN BELOW
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -216,7 +255,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0);
// TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data.
-
+ AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource");
// Allocate space for the top-level AS.
{
@@ -231,7 +270,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
}
// TODO-2.6: Allocate a UAV buffer for the actual top-level AS.
-
+ AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &topLevelAS, initialResourceState, L"TopLevelAccelerationStructure");
}
// Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer:
@@ -259,7 +298,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
else // DirectX Raytracing
{
@@ -271,7 +310,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
// Create a wrapped pointer to the acceleration structure.
@@ -283,7 +322,10 @@ 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.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress();
+ topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ //topLevelBuildDesc.SourceAccelerationStructureData = topLevelAS->GetGPUVirtualAddress();
// Build acceleration structure.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -302,7 +344,13 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// Very similar to how you did this in BuildBottomLevelAS() except now you have to worry about topLevelASBuffers.instanceDesc.
// Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h.
// Make sure to return the topLevelASBuffers before you exit the function.
- return AccelerationStructureBuffers{};
+ AccelerationStructureBuffers asBuffer = {};
+ asBuffer.accelerationStructure = topLevelAS;
+ asBuffer.instanceDesc = instanceDescsResource;
+ asBuffer.ResultDataMaxSizeInBytes = topLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+ asBuffer.scratch = scratch;
+
+ return asBuffer;
}
// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene.
@@ -318,12 +366,13 @@ 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];
-
+ bottomLevelAS[0] = BuildBottomLevelAS(geometryDescs[0]);
+ bottomLevelAS[1] = BuildBottomLevelAS(geometryDescs[1]);
// Batch all resource barriers for bottom-level AS builds.
// This will Notifies the driver that it needs to synchronize multiple accesses to resources.
@@ -336,7 +385,7 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-2.6: Build top-level AS. Hint, you already made a function that does this.
AccelerationStructureBuffers topLevelAS;
-
+ topLevelAS = BuildTopLevelAS(bottomLevelAS);
// Kick off acceleration structure construction.
m_deviceResources->ExecuteCommandList();
@@ -347,5 +396,7 @@ 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.
-
+ m_bottomLevelAS[0] = bottomLevelAS[0].accelerationStructure;
+ m_bottomLevelAS[1] = bottomLevelAS[1].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..6108ad2 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,24 @@ 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
-
+ auto& hitgroup = dispatchDesc->HitGroupTable;
+ hitgroup = {};
+ hitgroup.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress();
+ hitgroup.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width;
+ hitgroup.StrideInBytes = m_hitGroupShaderTableStrideInBytes;
// TODO-2.8: now fill in dispatchDesc->MissShaderTable
-
+ auto& miss = dispatchDesc->MissShaderTable;
+ miss = {};
+ miss.StartAddress = m_missShaderTable->GetGPUVirtualAddress();
+ miss.SizeInBytes = m_missShaderTable->GetDesc().Width;
+ miss.StrideInBytes = m_missShaderTableStrideInBytes;
// TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord
-
+ auto& raygen = dispatchDesc->RayGenerationShaderRecord;
+ raygen = {};
+ raygen.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress();
+ raygen.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..0ea5355 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -111,7 +111,11 @@ void DXProceduralProject::CreateConstantBuffers()
// structured buffers are for structs that have dynamic data (e.g lights in a scene, or AABBs in this case)
void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers()
{
+ auto device = m_deviceResources->GetD3DDevice();
+ auto frameCount = m_deviceResources->GetBackBufferCount();
+ // second param is num_Elements, the number of aabbs stored in the buffer
+ m_aabbPrimitiveAttributeBuffer.Create(device, 3, frameCount, L"AABB Primitive Attribute Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -164,6 +168,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 locToBLAS = mScale * mRotation * mTranslation;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = locToBLAS;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, locToBLAS);
};
UINT offset = 0;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..656ae43 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -87,6 +87,12 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
auto InitializeAABB = [&](auto& offsetIndex, auto& size)
{
D3D12_RAYTRACING_AABB aabb{};
+ aabb.MinX = basePosition.x + offsetIndex.x * stride.x;
+ aabb.MinY = basePosition.y + offsetIndex.y * stride.y;
+ aabb.MinZ = basePosition.z + offsetIndex.z * stride.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 +116,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), &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..6979606 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 primType = 0; primType < IntersectionShaderType::Count; primType++)
+ {
+ 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]);
+ }
+ hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primType]);
+ // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type
+ hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primType][rayType]);
+ hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE);
+ }
+ }
}
}
@@ -54,6 +70,17 @@ 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());
+
+ for (UINT primType = 0; primType < IntersectionShaderType::Count; primType++)
+ {
+ // Shader association
+ auto rootSignatureAssociation = raytracingPipeline->CreateSubobject();
+ rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature);
+ rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primType]);
+ }
}
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp
index c9501ac..d25be1c 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Pipeline.cpp
@@ -2,7 +2,7 @@
#include "DXProceduralProject.h"
#include "CompiledShaders\Raytracing.hlsl.h"
-// LOOKAT-2.4, TODO-2.4: Fill in the Raytracing Pipeline State Object (RTPSO).
+// LOOKAT-2.4: Fill in the Raytracing Pipeline State Object (RTPSO).
// An RTPSO represents a full set of shaders reachable by a DispatchRays() call, with all configuration options resolved,
// such as local signatures and other state.
void DXProceduralProject::CreateRaytracingPipelineStateObject()
@@ -19,7 +19,7 @@ void DXProceduralProject::CreateRaytracingPipelineStateObject()
// DXIL library
CreateDxilLibrarySubobject(&raytracingPipeline);
- // TODO-2.4: Hit groups. Call the function you filled in in DXR-HitGroup.cpp.
+ // LOOKAT-2.4: Hit groups. Call the function you filled in in DXR-HitGroup.cpp.
CreateHitGroupSubobjects(&raytracingPipeline);
// Shader config: defines the maximum sizes in bytes for the ray rayPayload and attribute structure.
@@ -28,7 +28,7 @@ void DXProceduralProject::CreateRaytracingPipelineStateObject()
UINT attributeSize = sizeof(struct ProceduralPrimitiveAttributes);
shaderConfig->Config(payloadSize, attributeSize);
- // TODO-2.4: Local root signature and shader association. Call the other function you did in in DXR-HitGroup.cpp.
+ // LOOKAT-2.4: Local root signature and shader association. Call the other function you did in in DXR-HitGroup.cpp.
CreateLocalRootSignatureSubobjects(&raytracingPipeline);
// Global root signature
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
index 2dff8b5..a37c582 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.
@@ -40,6 +40,12 @@ void DXProceduralProject::CreateRootSignatures()
// 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 +73,14 @@ void DXProceduralProject::CreateRootSignatures()
// to register 1, this overlap is allowed since we are talking about *local* root signatures
// --> the values they hold will depend on the shader function the local signature is bound to!
{
-
+ namespace RootSignatureSlots = LocalRootSignature::AABB::Slot;
+ CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count];
+ rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1);
+ rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 2);
+
+ CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters);
+ localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE;
+ SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB]);
}
}
}
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
index 150e92d..57897d9 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[RayType::Radiance]);
+ shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[RayType::Radiance];
+ missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[RayType::Shadow]);
+ shaderIdToStringMap[missShaderIDs[1]] = c_missShaderNames[RayType::Shadow];
// Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray.
for (UINT i = 0; i < RayType::Count; i++)
@@ -43,7 +45,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];
+ }
+ }
};
// Get shader identifiers using the lambda function defined above.
@@ -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/NuGet.Config b/src/D3D12RaytracingProceduralGeometry/NuGet.Config
similarity index 100%
rename from src/NuGet.Config
rename to src/D3D12RaytracingProceduralGeometry/NuGet.Config
diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
index d066933..bfaea32 100644
--- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
+++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
@@ -36,25 +36,26 @@ ConstantBuffer l_aabbCB: register(b2); // other
//*********************------ Utilities. -------*****************************
//***************************************************************************
-// TODO-3.6: Diffuse lighting calculation. This is just a Lambert shading term.
+// TODO-3.5: Diffuse lighting calculation. This is just a Lambert shading term.
// HINT: See https://en.wikipedia.org/wiki/Lambertian_reflectance
// Remember to clamp the dot product term!
float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
{
- return 0.0f;
+ return abs(dot(normalize(incidentLightRay), normalize(normal)));
}
-// TODO-3.6: Phong lighting specular component.
+// TODO-3.5: Phong lighting specular component.
// The equation should be coefficient = (reflectedRay . reverseRayDirection) ^ (specularPower).
// HINT: Consider using built-in DirectX functions to find the reflected ray. Remember that a reflected ray is reflected
// with respect to the normal of the hit position.
// Remember to normalize the reflected ray, and to clamp the dot product term
float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower)
{
- return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float3 reflectedRay = normalize(reflect(normalize(incidentLightRay), normalize(normal)));
+ return pow(abs(dot(normalize(WorldRayDirection()), reflectedRay)), specularPower);
}
-// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
+// TODO-3.5: Phong lighting model = ambient + diffuse + specular components.
// See https://en.wikipedia.org/wiki/Phong_reflection_model for the full simple equation.
// We have filled in the ambient color for you.
// HINT 1: remember that you can get the world position of the hitpoint using HitWorldPosition()
@@ -76,7 +77,22 @@ 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 lightRay = normalize(g_sceneCB.lightPosition.xyz - HitWorldPosition());
+ // Diffuse component
+ float4 diffColor = g_sceneCB.lightDiffuseColor;
+ float4 diffuseColor = albedo * diffColor * diffuseCoef * CalculateDiffuseCoefficient(lightRay, normal);
+
+ // Specular component
+ float4 specularColor = float4(1.0f, 1.0f, 1.0f, 1.0f) * specularCoef * CalculateSpecularCoefficient(lightRay, normal, specularPower);
+
+ // Changes if in shadow
+ if (isInShadow)
+ {
+ diffuseColor *= InShadowRadiance;
+ specularColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+
+ return ambientColor + diffuseColor + specularColor;
}
//***************************************************************************
@@ -135,7 +151,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 | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | RAY_FLAG_FORCE_OPAQUE,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, rayPayload);
+
+ return rayPayload.hit;
}
//***************************************************************************
@@ -149,9 +189,11 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
+ Ray camRay = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
+ float4 col = TraceRadianceRay(camRay, 0);
// Write the color to the render target
- g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ g_renderTarget[DispatchRaysIndex().xy] = col;
}
//***************************************************************************
@@ -209,8 +251,10 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
// Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is.
// Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color.
// When t is big, we want the background color to be more pronounced.
+ float t = 1.0f - min(RayTCurrent()/500.0f, 1.0f);
+ float4 falloffColor = lerp(BackgroundColor, color, t);
- rayPayload.color = color;
+ rayPayload.color = falloffColor;
}
// TODO: Write the closest hit shader for a procedural geometry.
@@ -227,7 +271,32 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
[shader("closesthit")]
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
+ // This is the intersection point on the triangle.
+ float3 hitPosition = HitWorldPosition();
+
+ // Trace a shadow ray to determine if this ray is a shadow ray
+ 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)
+ {
+ 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 with phong lighting
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, attr.normal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ float4 color = (phongColor + reflectedColor);
+
+ float t = 1.0f - min(RayTCurrent() / 500.0f, 1.0f);
+ float4 falloffColor = lerp(BackgroundColor, color, t);
+ rayPayload.color = falloffColor;
}
//***************************************************************************
@@ -240,14 +309,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 +368,21 @@ void MyIntersectionShader_AnalyticPrimitive()
[shader("intersection")]
void MyIntersectionShader_VolumetricPrimitive()
{
+ Ray localRay = GetRayInAABBPrimitiveLocalSpace();
+ VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType;
+
+ float thit;
+ ProceduralPrimitiveAttributes attr;
+ if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime))
+ {
+ PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex];
+
+ // 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, 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..cbca841 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
@@ -68,7 +68,10 @@ 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;
+ interpolant = sin(3.1415 * interpolant);
+
+ return smoothstep(0, 1, interpolant);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -130,18 +133,25 @@ 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 ndcx = (float(index.x + 0.5f) / DispatchRaysDimensions().x) * 2.0f - 1.0f;
+ float ndcy = 1.0f - (float(index.y + 0.5f) / DispatchRaysDimensions().y) * 2.0f;
+ float4 castPos = mul(float4(ndcx, ndcy, 1.0, 1.0), projectionToWorld);
+ castPos /= castPos.w;
+ ray.direction = normalize(castPos.xyz - cameraPosition);
return ray;
}
-// TODO-3.6: Fresnel reflectance - schlick approximation.
+// TODO-3.5: Fresnel reflectance - schlick approximation.
// See https://en.wikipedia.org/wiki/Schlick%27s_approximation for formula.
// 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;
+ float3 cosTheta = abs(dot(N, I));
+ float3 approx = f0 + (1 - f0) * pow((1 - cosTheta), 5);
+
+ return approx;
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..3b0ebbc 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,11 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float dist = distance(position, blob.center);
+ if (dist > blob.radius) { return 0.0f; }
+
+ float ratio = (blob.radius - dist) / blob.radius;
+ return 6.0f* pow(ratio, 5.0f) - 15.0f * pow(ratio, 4.0f) + 10.0f * pow(ratio, 3.0f);
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -83,6 +87,16 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
{
tmin = INFINITY;
tmax = -INFINITY;
+
+ for (int b = 0; b < N_METABALLS; b++)
+ {
+ float tempMin, tempMax;
+ if (RaySolidSphereIntersectionTest(ray, tempMin, tempMax, blobs[b].center, blobs[b].radius))
+ {
+ tmin = min(tmin, tempMin);
+ tmax = max(tmax, tempMax);
+ }
+ }
}
// TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field.
@@ -102,6 +116,34 @@ bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrim
{
thit = 0.0f;
attr.normal = float3(0.0f, 0.0f, 0.0f);
+
+ Metaball blobs[N_METABALLS];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 20.0f);
+
+ float tmin, tmax;
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+ if (tmax < -10000) { return false; } // We didn't hit any of the metaballs
+
+ float inc = (tmax - tmin) / 127.0f;
+ float tcurr = tmin;
+ for (int i = 0; i < 128; i++)
+ {
+ float3 pos = ray.origin + ray.direction * tcurr;
+ float currPot = CalculateMetaballsPotential(pos, blobs);
+ if (currPot > 0.2f)
+ {
+ float3 nor = CalculateMetaballsNormal(pos, blobs);
+ if (is_a_valid_hit(ray, tcurr, nor))
+ {
+ thit = tcurr;
+ attr.normal = nor;
+ return true;
+ }
+ }
+
+ tcurr += inc;
+ }
+
return false;
}