diff --git a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
index cc2db39..5f46935 100644
--- a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
+++ b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters
@@ -201,6 +201,9 @@
+
+
+
diff --git a/README.md b/README.md
index b0189d0..338e9dd 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,98 @@
**University of Pennsylvania, CIS 565: GPU Programming and Architecture,
Project 5 - DirectX Procedural Raytracing**
-* (TODO) YOUR NAME HERE
- * (TODO) [LinkedIn](), [personal website](), [twitter](), etc.
-* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
+* Alexis Ward
+ * [LinkedIn](https://www.linkedin.com/in/alexis-ward47/), [personal website](https://www.alexis-ward.tech/)
+* Tested on: Windows 10, i7-8750H CPU @ 2.20GHz 16GB, GTX 1050 Ti
-### (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.)
+# Ray Tracing Background (Conceptual Questions)
+
+### How To Ray Trace
+
+Ray tracing begins by firing off "rays" from the camera's perspective, with 1 ray corresponding to 1 pixel. Each ray is defined by an `Origin` and a `Direction`, such that `Ray = Origin + t * Direction`, with `t` being a distance along the ray. You can convert each pixel in the display to a ray by:
+
+1. Setting `Origin` equal to the camera's "eye," or position.
+2. Given pixel coordinates `index`, we get it's Screen Space counterpart by:
+ * `screenX = (index.x / ScreenXDimension) * 2.f - 1.f` and
+ * `screenY = 1.f - (index.y / ScreenYDimension) * 2.f`
+3. Since we don't have a `z` or `w` value, we estimate the furthest possible point by using the camera's far-clip distance: `farPoint = vec4(screenX * -farClip, screenY * -farClip, farClip, farClip)`. This puts us into Unhomogenized Screen Space.
+4. We now multiply this vector by the inverse of the camera's view and projection matrices: `inverse(u_View) * inverse(u_Project) * farPoint`
+5. Set the `Direction` to the normalized difference between the above result and the `Origin`.
+
+This is essentially converting a pervieved point in Pixel Space back to World Space, and then finding the vector from the camera to said point. We can now test for the ray's intersections with geometry.
+
+DirectX Update: Multiplication is swapped by virtue of DirectX's wonkiness.
+
+### Locating Procedural Geometry
+
+
+
+Every procedural geometry in this project is defined using 3 things: its `Axis-Aligned Bounding Box` (shortened to AABB, it is a tight bounds that encases the whole shape), its `Type` or shape, and the `Equation` determined by its `Type` and transformation. This `Equation` will most likely be a signed distance function, which tells us how far we are from the closest point on the geometry (but not necessarily the closest point along a ray).
+
+Update: No SDFs in this project, but the process is similar nonetheless.
+
+To render procedural geometry, we check each pixel's corresponding ray for intersections with the scene. To simplify this, we can first see if the current ray passes through an `AABB` stored within our Bottom Level Acceleration Structure (BLAS). This tells us that the geometry encased within can potentially influence this pixel. We then use a Closest Hit Shader determined by the object's `Type` to trace our ray with the influence of this geometry's `Equation`. This equation should give us a `t` value, as shown in the aforementioned ray representation: `Ray = Origin + t * Direction`. We displace our ray by this value and test again. This part happens recursively until the `Equation` returns a zero (or a value within some epsilon factor), or if the ray leaves the `AABB`.
+
+This method greatly decreases runtime because, by working with only the intersected AABBs, we avoid having to recursively compare the ray to every geometry equation equation in the scene: we only perform this for the relevant equations. The AABB intersection check is a simple one because of how they are defined by their min and max [x, y, z] positions.
+
+### DXR Top-Level / Bottom-Level Example
+
+Say our scene looks like this:
+
+
+
+
+
+This is a diagram of the resulting acceleration structure:
+
+
+
+
+# The Project
+
+## The CPU Side
+This project would not run without a complete CPU implementation, so unfortunately I have no visuals to display here. The general setup process goes as follows:
+
+ - Allocating and uploadng **scene data** to the GPU, which includes a camera, lights, colors for objects, as well as transforms for the objects to render. I use `GpuUploadBuffers` to accomplish this.
+ - Set up **root signatures** as a way to programmatically read/write to various data from the GPU. You first create a slot for the present resource and a descriptor that tells the GPU how to read said resource. The two are linked later in the process.
+ - Create the **HitGroup** subobjects that will be built into the pipeline. A hit group, in this project, is composed of a `Closest Hit Shader` and at least one `Intersection Shader`.
+ - Allocating and uploading the **geometry data**. This is done in two different ways: by triangle data and by procedural geometry data (AABBs).
+ - Creating an **acceleration structure** to boost performance. The scene is divided into `Top Level Acceleration Structures` (TLAS) that hold multiple instances of `Bottom Level Acceleration Structures` (BLAS0, as visualized in the example above)
+ - DXR's **Shader Tables**. Again, `GpuUploadBuffers`, and this version holds shaders of the same types.
+ - Finally, we "glue" all this data together using the many `Set...()` functions available. Then `DispatchRays()` can be calleld for each pixel, like a CUDA kernel.
+
+## The GPU Side
+
+First we have to generate the rays as described in the **How To Ray Trace** section above. This will show a dark image like the one below, that only depicts ambient lighting.
+
+
+
+
+
+### Trace Ray
+
+DXR's TraceRay function, as the name implies, traces a ray through the scene. One implementation occurs for the radiance ray and another for the shadow ray. This is what the scene looks like with and without the shadow processing:
+
+
+
+### Shaders
+
+Miss Shaders and Intersection Shaders pull the scene together. They determine what happens when a ray hits or misses geometry and calculates essential information like the hit points and normals. In this project, I allow for intersections with spheres and metaballs (left and right below).
+
+### Shading
+This project uses the Phone Lighting Model for all objects. There is also a distance falloff that "blurs" geometry the further away they are - though, this is closer to atmospheric perspective. View the before and aafter below:
+
+
+
+## Interesting bugs
+I sadly have some visual artifacts when it comes to the box AABB. This includes extra geometry and spotty shading. I hope to fix this in the future.
+
+I noticed that the calls to DirectX's `TraceRay()` function cuts my frame rate down by two thirds. I wasn't yet sure if that's just what happens with `TraceRay()`, or if it's somehow related to the aforementioned artifacts or improper buffer handling. To be determined!
+
+## Performance Analysis
+
+
+
+As expected, the frame rate goes down with increased ray depth (with few exceptions).
\ No newline at end of file
diff --git a/images/balls.png b/images/balls.png
new file mode 100644
index 0000000..7c34af0
Binary files /dev/null and b/images/balls.png differ
diff --git a/images/diagram.png b/images/diagram.png
new file mode 100644
index 0000000..1cd6a2a
Binary files /dev/null and b/images/diagram.png differ
diff --git a/images/falloff.png b/images/falloff.png
new file mode 100644
index 0000000..64c17e5
Binary files /dev/null and b/images/falloff.png differ
diff --git a/images/graph.png b/images/graph.png
new file mode 100644
index 0000000..c7589c9
Binary files /dev/null and b/images/graph.png differ
diff --git a/images/oof.gif b/images/oof.gif
new file mode 100644
index 0000000..668ebeb
Binary files /dev/null and b/images/oof.gif differ
diff --git a/images/polyGraph.png b/images/polyGraph.png
new file mode 100644
index 0000000..a6069f4
Binary files /dev/null and b/images/polyGraph.png differ
diff --git a/images/post-falloff.PNG b/images/post-falloff.PNG
new file mode 100644
index 0000000..ec4be3f
Binary files /dev/null and b/images/post-falloff.PNG differ
diff --git a/images/pre-falloff.PNG b/images/pre-falloff.PNG
new file mode 100644
index 0000000..c1ff07a
Binary files /dev/null and b/images/pre-falloff.PNG differ
diff --git a/images/refl.png b/images/refl.png
new file mode 100644
index 0000000..de5fb71
Binary files /dev/null and b/images/refl.png differ
diff --git a/images/sdf.png b/images/sdf.png
new file mode 100644
index 0000000..ef01322
Binary files /dev/null and b/images/sdf.png differ
diff --git a/images/shadows.png b/images/shadows.png
new file mode 100644
index 0000000..511c2a6
Binary files /dev/null and b/images/shadows.png differ
diff --git a/images/spheres.png b/images/spheres.png
new file mode 100644
index 0000000..0215169
Binary files /dev/null and b/images/spheres.png differ
diff --git a/images/wOShadow.png b/images/wOShadow.png
new file mode 100644
index 0000000..69f8013
Binary files /dev/null and b/images/wOShadow.png differ
diff --git a/images/withShadow.png b/images/withShadow.png
new file mode 100644
index 0000000..0be72de
Binary files /dev/null and b/images/withShadow.png differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..71c010f 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -166,13 +166,29 @@ bool RaySolidSphereIntersectionTest(in Ray ray, out float thit, out float tmax,
bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr)
{
// Define the spheres in local space (within the aabb)
- float3 center = float3(-0.2, 0, -0.2);
- float radius = 0.7f;
+ float3 centerS1 = float3(-0.2, 0, -0.5);
+ float r1 = 0.7f;
+
+ // Sphere 2
+ float3 centerS2 = float3(0, 0.2, 0);
+ float r2 = 0.5f;
+
+ // Sphere3
+ float3 centerS3 = float3(-0.1, -0.7, -0.2);
+ float r3 = 0.3f;
thit = RayTCurrent();
float tmax;
- if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
+ if (RaySphereIntersectionTest(ray, thit, tmax, attr, centerS1, r1))
+ {
+ return true;
+ }
+ else if (RaySphereIntersectionTest(ray, thit, tmax, attr, centerS2, r2))
+ {
+ return true;
+ }
+ else if (RaySphereIntersectionTest(ray, thit, tmax, attr, centerS3, r3))
{
return true;
}
diff --git a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
index 4a8b9ab..5b4d504 100644
--- a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
+++ b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj
@@ -219,14 +219,14 @@ PrebuildCheck.bat
/Zpr %(AdditionalOptions)
-
-
-
{4be280a6-1066-41ca-acdd-6bb7e532508b}
+
+
+
diff --git a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj.filters b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj.filters
index ebe7438..a524448 100644
--- a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj.filters
+++ b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj.filters
@@ -132,12 +132,12 @@
Source Files
-
-
-
Assets\Shaders
+
+
+
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 905341d..d47e4ec 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -26,14 +26,21 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress())
- // * 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 = {};
+ // Geometry Desc variables (in D3D12_RAYTRACING_GEOMETRY_DESC)
+ geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
+ geometryDesc.Flags = geometryFlags;
+ // * 565 staff filled in the format of the buffers to avoid confusion.
geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT;
geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;
+ // * Use m_indexBuffer and m_vertexBuffer to get pointers to the following data:
+ // * The *total size* of the buffer can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width)
+ geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index);
+ geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex);
+ // * GPUVirtualAddresses can be accessed from a D3D12Resource using GetGPUVirtualAddress() (e.g m_vertexBuffer.resource->GetGPUVirtualAddress())
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer = { m_vertexBuffer.resource->GetGPUVirtualAddress(), sizeof(Vertex) };// Virtual Address and Stride
}
{
@@ -51,12 +58,24 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width / sizeof(D3D12_RAYTRACING_AABB); // total size over data size, like above
+ geometryDescs[BottomLevelASType::AABB][aabbGeom].AABBs.AABBs = {
+ m_aabbBuffer.resource->GetGPUVirtualAddress() + aabbGeom * sizeof(D3D12_RAYTRACING_AABB),
+ sizeof(D3D12_RAYTRACING_AABB)
+ };
+ }
}
}
// TODO-2.6: Given the geometry and the geometry descriptors, build the bottom-level acceleration structures.
-AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vector& geometryDescs, D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags)
+AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vector& geometryDescs,
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags)
{
auto device = m_deviceResources->GetD3DDevice();
auto commandList = m_deviceResources->GetCommandList();
@@ -70,7 +89,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.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
+ bottomLevelInputs.NumDescs = geometryDescs.size();
+ bottomLevelInputs.pGeometryDescs = geometryDescs.data();
// Query the driver for resource requirements to build an acceleration structure. We've done this for you.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
@@ -89,7 +112,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.
@@ -110,7 +133,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc.
// Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.
// This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls.
-
+ bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress();
// Fill up the command list with a command that tells the GPU how to build the bottom-level AS.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -127,9 +151,15 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// TODO-2.6: After we finished building the bottom-level AS, save all the info in
// the AccelerationStructureBuffers struct so the top-level AS can use it!
+ AccelerationStructureBuffers accelBuff{};
+ accelBuff.scratch = scratch;
+ accelBuff.accelerationStructure = bottomLevelAS;
+ accelBuff.instanceDesc = nullptr;
+ accelBuff.ResultDataMaxSizeInBytes = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+
// Don't forget that this is the return value.
// Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h
- return AccelerationStructureBuffers{};
+ return accelBuff;
}
// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before.
@@ -178,10 +208,26 @@ void DXProceduralProject::BuildBottomLevelASInstanceDescs(BLASPtrType *bottomLev
// TODO-2.6: Create instanced bottom-level AS with procedural geometry AABBs.
// * Make sure to set InstanceContributionToHitGroupIndex to beyond the shader records for the triangle AABB.
// For triangles, we have 1 shader record for radiance rays, and another for shadow rays.
- // Where do you think procedural shader records would start then? Hint: right after.
+ // Where do you think procedural shader records would start then? Hint: right after - so 2!
// * 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];
+
+ // Calculate transformation matrix.
+ // We multiply the width by 0.5 and translate up by that ammount for each object.
+ const XMVECTOR widthHover = XMLoadFloat3(&XMFLOAT3(0.f, c_aabbWidth * 0.5f , 0.f));
+
+ // Transform
+ // XMMATRIX mScale = XMMatrixScaling(fWidth.x, fWidth.y, fWidth.z); // Maybe by c_aabbWidth?
+ XMMATRIX mTranslation = XMMatrixTranslationFromVector(widthHover);
+ XMMATRIX mTransform = /*mScale */ mTranslation;
+ // 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 +250,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// TODO-2.6: fill in the topLevelInputs, read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS.
// Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as a DescsLayout since we are using an array of bottom-level AS.
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &topLevelInputs = topLevelBuildDesc.Inputs;
-
+ topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
+ topLevelInputs.Flags = buildFlags;
+ topLevelInputs.NumDescs = NUM_BLAS; // 2
+ topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -218,7 +267,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"Temp_top-level_AS_data"); // Need this line?? compiled without
// Allocate space for the top-level AS.
{
@@ -233,7 +283,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"Top-level_AS_data");
}
// Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer:
@@ -261,7 +312,8 @@ 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 +325,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.
@@ -285,7 +338,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.
// This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls.
-
+ topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress();
+ topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress();
// Build acceleration structure.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -303,8 +358,14 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// TODO-2.6: After we finished building the top-level AS, save all the info in the AccelerationStructureBuffers struct.
// 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.
+ AccelerationStructureBuffers accelBuff{};
+ accelBuff.scratch = scratch;
+ accelBuff.accelerationStructure = topLevelAS;
+ accelBuff.instanceDesc = instanceDescsResource;
+ accelBuff.ResultDataMaxSizeInBytes = topLevelPrebuildInfo.ResultDataMaxSizeInBytes;
+
// Make sure to return the topLevelASBuffers before you exit the function.
- return AccelerationStructureBuffers{};
+ return accelBuff;
}
// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene.
@@ -320,12 +381,14 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this.
array, BottomLevelASType::Count> geometryDescs;
-
+ BuildGeometryDescsForBottomLevelAS(geometryDescs);
// TODO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS.
// Hint: you filled in a function that does this.
AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count];
-
+ for (UINT bLObj = 0; bLObj < BottomLevelASType::Count; bLObj++) {
+ bottomLevelAS[bLObj] = BuildBottomLevelAS(geometryDescs[bLObj]);
+ }
// Batch all resource barriers for bottom-level AS builds.
// This will Notifies the driver that it needs to synchronize multiple accesses to resources.
@@ -337,8 +400,7 @@ void DXProceduralProject::BuildAccelerationStructures()
commandList->ResourceBarrier(BottomLevelASType::Count, resourceBarriers);
// TODO-2.6: Build top-level AS. Hint, you already made a function that does this.
- AccelerationStructureBuffers topLevelAS;
-
+ AccelerationStructureBuffers topLevelAS = BuildTopLevelAS(bottomLevelAS);
// Kick off acceleration structure construction.
m_deviceResources->ExecuteCommandList();
@@ -349,5 +411,8 @@ 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_topLevelAS = topLevelAS.accelerationStructure;
+ for (UINT blAS = 0; blAS < BottomLevelASType::Count; blAS++) {
+ m_bottomLevelAS[blAS] = bottomLevelAS[blAS].accelerationStructure;
+ }
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AppLifeCyclecpp.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AppLifeCyclecpp.cpp
index 527139a..7b9f0ea 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AppLifeCyclecpp.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AppLifeCyclecpp.cpp
@@ -22,6 +22,7 @@ void DXProceduralProject::OnInit()
m_deviceResources->SetWindow(Win32Application::GetHwnd(), m_width, m_height);
m_deviceResources->InitializeDXGIAdapter();
EnableDirectXRaytracing(m_deviceResources->GetAdapter());
+ SelectRaytracingAPI(RaytracingAPI::DirectXRaytracing);
m_deviceResources->CreateDeviceResources();
m_deviceResources->CreateWindowSizeDependentResources();
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
index 03a8c58..61e6513 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
@@ -22,7 +22,8 @@ void DXProceduralProject::DoRaytracing()
commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex));
// TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer
-
+ m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex);
+ commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex));
// Bind the descriptor heaps.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -49,10 +50,11 @@ void DXProceduralProject::DoRaytracing()
// This should be done by telling the commandList to SetComputeRoot*(). You just have to figure out what * is.
// Example: in the case of GlobalRootSignature::Slot::SceneConstant above, we used SetComputeRootConstantBufferView()
// Hint: look at CreateRootSignatures() in DXR-Pipeline.cpp.
-
+ commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::VertexBuffers, m_indexBuffer.gpuDescriptorHandle);
// TODO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer.
-
+ //commandList->SetComputeRootUnorderedAccessView(GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor.);
+ commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor);
// This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor
// This will set the hooks using the shader tables built before and call DispatchRays on the command list
@@ -60,13 +62,18 @@ void DXProceduralProject::DoRaytracing()
{
// You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc).
// TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE
-
-
- // TODO-2.8: now fill in dispatchDesc->MissShaderTable
-
-
- // TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord
-
+ dispatchDesc->HitGroupTable = { m_hitGroupShaderTable->GetGPUVirtualAddress(), // StartAddress
+ m_hitGroupShaderTable->GetDesc().Width, // SizeInBytes
+ m_hitGroupShaderTableStrideInBytes }; // StrideInBytes
+
+ // TODO-2.8: now fill in dispatchDesc->MissShaderTable, also D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE
+ dispatchDesc->MissShaderTable = { m_missShaderTable->GetGPUVirtualAddress(), // StartAddress
+ m_missShaderTable->GetDesc().Width, // SizeInBytes
+ m_missShaderTableStrideInBytes }; // StrideInBytes
+
+ // TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord, D3D12_GPU_VIRTUAL_ADDRESS_RANGE instead
+ dispatchDesc->RayGenerationShaderRecord = { m_rayGenShaderTable->GetGPUVirtualAddress(), // StartAddress
+ m_rayGenShaderTable->GetDesc().Width }; // SizeInBytes
// 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..787cfd5 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
@@ -67,9 +67,9 @@ void DXProceduralProject::InitializeScene()
m_up = XMVector3Normalize(XMVector3Cross(direction, right));
// Rotate camera around Y axis.
- XMMATRIX rotate = XMMatrixRotationY(XMConvertToRadians(45.0f));
- m_eye = XMVector3Transform(m_eye, rotate);
- m_up = XMVector3Transform(m_up, rotate);
+ XMMATRIX rotate = DirectX::XMMatrixRotationY(XMConvertToRadians(45.0f)); // Got errors out of nowhere,
+ m_eye = XMVector3Transform(m_eye, rotate); // so added DirectX:: to all
+ m_up = XMVector3Transform(m_up, rotate); // (classmate's advice)
UpdateCameraMatrices();
}
@@ -111,7 +111,13 @@ 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()
{
+ // Similar besides the "numElements" input to create - from m_aabbMaterialCB in the .h file
+ auto device = m_deviceResources->GetD3DDevice();
+ auto frameCount = m_deviceResources->GetBackBufferCount();
+ m_aabbPrimitiveAttributeBuffer.Create(device,
+ IntersectionShaderType::TotalPrimitiveCount,
+ frameCount, L"AABB Primitive Attribute Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -121,10 +127,10 @@ void DXProceduralProject::UpdateCameraMatrices()
m_sceneCB->cameraPosition = m_eye;
float fovAngleY = 45.0f;
- XMMATRIX view = XMMatrixLookAtLH(m_eye, m_at, m_up);
- XMMATRIX proj = XMMatrixPerspectiveFovLH(XMConvertToRadians(fovAngleY), m_aspectRatio, 0.01f, 125.0f);
+ XMMATRIX view = DirectX::XMMatrixLookAtLH(m_eye, m_at, m_up);
+ XMMATRIX proj = DirectX::XMMatrixPerspectiveFovLH(XMConvertToRadians(fovAngleY), m_aspectRatio, 0.01f, 125.0f);
XMMATRIX viewProj = view * proj;
- m_sceneCB->projectionToWorld = XMMatrixInverse(nullptr, viewProj);
+ m_sceneCB->projectionToWorld = DirectX::XMMatrixInverse(nullptr, viewProj);
}
// TODO-2.1: Update the PrimitiveInstancePerFrameBuffer for every AABB stored in m_aabbPrimitiveAttributeBuffer[].
@@ -140,15 +146,15 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime)
{
auto frameIndex = m_deviceResources->GetCurrentFrameIndex();
- XMMATRIX mIdentity = XMMatrixIdentity();
+ XMMATRIX mIdentity = DirectX::XMMatrixIdentity();
// Different scale matrices
- XMMATRIX mScale15y = XMMatrixScaling(1, 1.5, 1);
- XMMATRIX mScale15 = XMMatrixScaling(1.5, 1.5, 1.5);
- XMMATRIX mScale2 = XMMatrixScaling(2, 2, 2);
+ XMMATRIX mScale15y = DirectX::XMMatrixScaling(1, 1.5, 1);
+ XMMATRIX mScale15 = DirectX::XMMatrixScaling(1.5, 1.5, 1.5);
+ XMMATRIX mScale2 = DirectX::XMMatrixScaling(2, 2, 2);
// Rotation matrix that changes over time
- XMMATRIX mRotation = XMMatrixRotationY(-2 * animationTime);
+ XMMATRIX mRotation = DirectX::XMMatrixRotationY(-2 * animationTime);
auto SetTransformForAABB = [&](UINT primitiveIndex, XMMATRIX& mScale, XMMATRIX& mRotation)
@@ -156,7 +162,7 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime)
XMVECTOR vTranslation =
0.5f * (XMLoadFloat3(reinterpret_cast(&m_aabbs[primitiveIndex].MinX))
+ XMLoadFloat3(reinterpret_cast(&m_aabbs[primitiveIndex].MaxX))); // i.e middle of AABB.
- XMMATRIX mTranslation = XMMatrixTranslationFromVector(vTranslation);
+ XMMATRIX mTranslation = DirectX::XMMatrixTranslationFromVector(vTranslation);
// TODO-2.1: Fill in this lambda function.
// It should create a transform matrix that is equal to scale * rotation * translation.
@@ -164,6 +170,10 @@ void DXProceduralProject::UpdateAABBPrimitiveAttributes(float animationTime)
// You can infer what the bottom level AS space to local space transform should be.
// The intersection shader tests in this project work with local space, but the geometries are provided in bottom level
// AS space. So this data will be used to convert back and forth from these spaces.
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS =
+ mScale * mRotation * mTranslation;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace =
+ DirectX::XMMatrixInverse(nullptr, m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS);
};
UINT offset = 0;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..d5a2c79 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -86,7 +86,12 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// This should take into account the basePosition and the stride defined above.
auto InitializeAABB = [&](auto& offsetIndex, auto& size)
{
- D3D12_RAYTRACING_AABB aabb{};
+ D3D12_RAYTRACING_AABB aabb{ basePosition.x + offsetIndex.x * stride.x, // MinX
+ basePosition.y + offsetIndex.y * stride.y, // MinY
+ basePosition.z + offsetIndex.z * stride.z, // MinZ
+ basePosition.x + offsetIndex.x * stride.x + size.x, // MaxX
+ basePosition.y + offsetIndex.y * stride.y + size.y, // MaxY
+ basePosition.z + offsetIndex.z * stride.z + size.z }; // MaxZ
return aabb;
};
m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount);
@@ -110,12 +115,15 @@ 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).
-
+ //m_aabbBuffer.resource.A
+ AllocateUploadBuffer(device, m_aabbs.data(),
+ m_aabbs.size() * sizeof(D3D12_RAYTRACING_AABB), &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..3a14d12 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 rayType = 0; rayType < RayType::Count; rayType++)
+ {
+ for (UINT primitives = 0; primitives < IntersectionShaderType::Count; primitives++)
+ {
+ 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[primitives]);
+ // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type
+ hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primitives][rayType]);
+ hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE);
+ }
+ }
}
}
@@ -54,6 +70,18 @@ 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 AABB 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 each primitive type
+ for (UINT primitives = 0; primitives < IntersectionShaderType::Count; primitives++)
+ {
+ rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primitives]);
+ }
}
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
index 2dff8b5..73c1155 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
@@ -19,26 +19,32 @@ void DXProceduralProject::CreateRootSignatures()
CD3DX12_DESCRIPTOR_RANGE ranges[2];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture
- // TODO-2.2: In range index 1 (the second range), initialize 2 SRV resources at register 1: indices and vertices of triangle data.
+ // TODO-2.2: In range index 1 (the second range), initialize 2 SRV (ShaderResourceView) 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.
+ CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
// - The OutputView should correspond to the UAV range descriptor above (descriptor table), bound to register 0 of the UAV registers.
- // - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers.
+ rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]);
+ // - The Index/Vertex Buffer should correspond to the SRV range (descriptor table) above, bound to registers 1 and 2 of the SRV registers.
// Note that since we initialize these as a range of size 2, then you should bind the entire range to register 1.
// This will automatically fill in registers 1 and 2.
+ rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]);
// - The AccelerationStructure should be init as SRV bound to register 0 of the SRV registers.
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0);
// - The SceneConstant should be init as a ConstantBufferView (CBV) bound to register 0 of the CBV registers.
- // - The AABBAttributeBuffer should be init as SRV bound to register 3 of the SRV registers.
+ rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0);
+ // - The AABBAttributeBuffer should be init as SRV bound to register 3 of the SRV registers.
+ rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3);
// - Look up InitAsDescriptorTable(), InitAsShaderResourceView(), and InitAsConstantBuffer() in the DirectX documentation
// to understand what to do.
// - If you're ever unsure if the register mapping is correct, look at the top of Raytracing.hlsl.
// u registers --> UAV
// t registers --> SRV
// b registers --> CBV
- CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count];
+
// Finally, we bundle up all the descriptors you filled up and tell the device to create this global root signature!
CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters);
@@ -61,13 +67,21 @@ void DXProceduralProject::CreateRootSignatures()
}
// TODO-2.2: AABB geometry. Inspire yourself from the triangle local signature above to create an AABB local signature
- // - Remember that the AABB holds 1 slot for Material Constants, and another 1 for the geometry instance.
- // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means.
- // - Use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps
- // to register 1, this overlap is allowed since we are talking about *local* root signatures
- // --> the values they hold will depend on the shader function the local signature is bound to!
{
-
+ namespace RootSignatureSlots = LocalRootSignature::AABB::Slot;
+ CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count];
+ // - The AABB holds 1 slot for Material Constants, and another 1 for the geometry instance.
+ // - See the AABB Definition in RaytracingSceneDefines.h to understand what this means.
+ rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1);
+ rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveInstanceConstantBuffer), 2);
+ // - The above use registers 1 and 2 of the CBVs for the AABB. Yes, althought the triangle MaterialConstant *also* maps
+ // to register 1, this overlap is allowed since we are talking about *local* root signatures
+ // --> the values they hold will depend on the shader function the local signature is bound to!
+
+
+ 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..511768f 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp
@@ -32,8 +32,11 @@ 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;
+ for (UINT i = 0; i < RayType::Count; i++)
+ {
+ missShaderIDs[i] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[i]);
+ shaderIdToStringMap[missShaderIDs[i]] = c_missShaderNames[i]; // Radiance = 0, Shadow = 1 - from RayType
+ }
// 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 +46,14 @@ void DXProceduralProject::BuildShaderTables()
}
// TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB.
-
+ for (UINT i = 0; i < RayType::Count; i++)
+ {
+ for (UINT aabb = 0; aabb < IntersectionShaderType::Count; aabb++)
+ {
+ hitGroupShaderIDs_AABBGeometry[aabb][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[aabb][i]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[aabb][i]] = c_hitGroupNames_AABBGeometry[aabb][i];
+ }
+ }
};
// Get shader identifiers using the lambda function defined above.
@@ -95,7 +105,20 @@ void DXProceduralProject::BuildShaderTables()
// TODO-2.7: Miss shader table. Very similar to the RayGen table except now we push_back() 2 shader records
// 1 for the radiance ray, 1 for the shadow ray. Don't forget to call DebugPrint() on the table for your sanity!
{
-
+ UINT numShaderRecords = 2;
+ UINT shaderRecordSize = shaderIDSize; // No root arguments
+
+ // The miss shader table contains a two ShaderRecords: the radiance ray and the shadow ray!
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"Miss_Shader_Table");
+
+ // Push back the shader records, which do not need any root signatures.
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[0], shaderRecordSize, nullptr, 0));
+ missShaderTable.push_back(ShaderRecord(missShaderIDs[1], shaderRecordSize, nullptr, 0));
+
+ // Save the uploaded resource (remember that the uploaded resource is created when we call Allocate() on a GpuUploadBuffer
+ missShaderTable.DebugPrint(shaderIdToStringMap);
+ m_missShaderTable = missShaderTable.GetResource();
+ m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize();
}
// Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature.
diff --git a/src/D3D12RaytracingProceduralGeometry/Main.cpp b/src/D3D12RaytracingProceduralGeometry/Main.cpp
index 7f70bc6..6bbb443 100644
--- a/src/D3D12RaytracingProceduralGeometry/Main.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/Main.cpp
@@ -16,7 +16,7 @@
#include "stdafx.h"
#include "DXProceduralProject.h"
-#define CPU_CODE_COMPLETE 0
+#define CPU_CODE_COMPLETE 1
_Use_decl_annotations_
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
diff --git a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
index d066933..716e080 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 min(1.f, abs(dot(normalize(normal), normalize(incidentLightRay))));
}
// TODO-3.6: Phong lighting specular component.
@@ -51,20 +51,14 @@ float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
// Remember to normalize the reflected ray, and to clamp the dot product term
float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower)
{
- return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float3 reflectedRay = normalize(reflect(normalize(normal), normalize(incidentLightRay)));
+ float3 reverseRayDirection = normalize(-incidentLightRay); //?? normalize(WorldRayDirection()); //
+ float coefficient = pow(min(1.f, abs(dot(reflectedRay, reverseRayDirection))), specularPower);
+ return coefficient * float4(1.f, 1.f, 1.f, 1.f);
}
// TODO-3.6: 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()
-// from RaytracingShaderHelper.hlsli. The light position is somewhere in g_sceneCB.
-// HINT 1: first you need to figure out the diffuse coefficient using CalculateDiffuseCoefficient()
-// then you need to figure out the specular coefficient using CalculateSpecularCoefficient()
-// then you need to combine the diffuse, specular, and ambient colors into one color.
-// Remember that if the ray is a shadow ray, then the hit position should be very dim. Consider using
-// InShadowRadiance from RaytracingHlslCompat.h tp dim down the diffuse color. The specular color should
-// be completely black if the ray is a shadow ray.
float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInShadow,
in float diffuseCoef = 1.0, in float specularCoef = 1.0, in float specularPower = 50)
{
@@ -74,9 +68,28 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh
float4 ambientColorMin = g_sceneCB.lightAmbientColor - 0.1;
float4 ambientColorMax = g_sceneCB.lightAmbientColor;
float a = 1 - saturate(dot(normal, float3(0, -1, 0)));
+ // 565 staff has filled in the ambient color in advance.
ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a);
- return ambientColor;
+ // HINT 1: remember that you can get the world position of the hitpoint using HitWorldPosition()
+ // from RaytracingShaderHelper.hlsli. The light position is somewhere in g_sceneCB.
+ float3 incidentLightRay = normalize(g_sceneCB.lightPosition - HitWorldPosition());
+
+ // HINT 2: first you need to figure out the diffuse coefficient using CalculateDiffuseCoefficient()
+ diffuseCoef = CalculateDiffuseCoefficient(incidentLightRay, normal);
+ // then you need to figure out the specular coefficient using CalculateSpecularCoefficient()
+ specularCoef = CalculateSpecularCoefficient(incidentLightRay, normal, specularPower);
+
+ // Remember that if the ray is a shadow ray, then the hit position should be very dim. Consider using
+ // InShadowRadiance from RaytracingHlslCompat.h tp dim down the diffuse color. The specular color should
+ // be completely black if the ray is a shadow ray.
+ if (isInShadow) {
+ specularCoef = 0.f;
+ diffuseCoef *= InShadowRadiance; // CAUSES WEIRD SPOTS - fixed in Shadow Ray below
+ }
+
+ // then you need to combine the diffuse, specular, and ambient colors into one color.
+ return ambientColor + g_sceneCB.lightDiffuseColor * albedo * (diffuseCoef + specularCoef);
}
//***************************************************************************
@@ -135,7 +148,44 @@ 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;
+
+ // From RaytracingHlslCompat.h, to be passed into TraceRay
+ ShadowRayPayload rayPayload = { true };
+
+ // TraceRay() is a built-in DXR function. Bellow are comments from it's documentation
+ TraceRay(
+ // AccelerationStructure
+ g_scene,
+ // RayFlags: used to specify the behavior upon hitting a surface, found options in "Flags per ray" documentation
+ RAY_FLAG_CULL_BACK_FACING_TRIANGLES |
+ RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH |
+ RAY_FLAG_SKIP_CLOSEST_HIT_SHADER |
+ RAY_FLAG_FORCE_OPAQUE,
+ // InstanceInclusionMask: can mask out geometry
+ TraceRayParameters::InstanceMask,
+ // RayContributionToHitGroupIndex: since given objects can have multiple hit groups
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ // MultiplierForGeometryContributionToHitGroupIndex: orders shaders as in AS
+ TraceRayParameters::HitGroup::GeometryStride,
+ // MissShaderIndex: index to use in case several consecutive miss shaders are present
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ // Ray information to trace and write to
+ rayDesc, rayPayload);
+
+ return rayPayload.hit;
}
//***************************************************************************
@@ -149,9 +199,11 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
-
+ Ray ray = GenerateCameraRay(DispatchRaysIndex().xy,
+ g_sceneCB.cameraPosition,
+ 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);
}
//***************************************************************************
@@ -209,25 +261,46 @@ 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.
-
- rayPayload.color = color;
+
+ rayPayload.color = lerp(color, BackgroundColor, min(max(RayTCurrent() - 20.f, 0.f) / 100.f, 1.f));
}
// TODO: Write the closest hit shader for a procedural geometry.
// You suppose this is called after the ray successfully iterated over all geometries and determined it intersected with some AABB.
// The attributes of the AABB are in attr (basically the normal)
-// You need to:
-// (0) Realize that you do not need to load in indices or vertices because we're talking procedural geometry here (wohoo!)
-// (1) Trace a shadow ray to determine if this ray is a shadow ray.
-// (2) Trace a reflectance ray --> compute the reflected color.
-// (3) Use the fact that your ray is a shadow ray or not to compute the Phong lighting.
-// (4) Combine the reflect color and the phong color into one color.
-// (5) Apply visibility falloff to select some interpolation between the computed color or the background color
-// (6) Fill the payload color with whatever final color you computed
[shader("closesthit")]
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
+ // (0) Realize that you do not need to load in indices or vertices because we're talking procedural geometry here (wohoo!)
+ // This is the intersection point on the primitive. - Skip straight to this step
+ float3 hitPosition = HitWorldPosition();
+ // (1) Trace a shadow ray to determine if this ray is a shadow 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);
+
+ // (2) Trace a reflectance ray --> compute the reflected color.
+ float4 reflectedColor = float4(0, 0, 0, 0);
+ if (l_materialCB.reflectanceCoef > 0.001)
+ {
+ // Trace the ray from the intersection points using Snell's law with the built in reflect() func
+ 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;
+ }
+
+ // (3) Use the fact that your ray is a shadow ray or not to compute the Phong lighting.
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, attr.normal, shadowRayHit,
+ l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ // (4) Combine the reflect color and the phong color into one color.
+ float4 color = (phongColor + reflectedColor);
+
+ // (5) Apply visibility falloff to select some interpolation between the computed color or the background color
+ // And (6) Fill the payload color with whatever final color you computed
+ rayPayload.color = lerp(color, BackgroundColor, min(max(RayTCurrent() - 20.f, 0.f) / 100.f, 1.f));
}
//***************************************************************************
@@ -240,14 +313,15 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive
[shader("miss")]
void MyMissShader(inout RayPayload rayPayload)
{
-
+ // rayPayload.color = float4(145.f / 255.f, 83.f / 255.f, 181.f / 255.f);
+ 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;
}
//***************************************************************************
@@ -296,9 +370,28 @@ void MyIntersectionShader_AnalyticPrimitive()
// TODO-3.4.2: Volumetric primitive intersection shader. In our case, we only have Metaballs to take care of.
// The overall structure of this function is parallel to MyIntersectionShader_AnalyticPrimitive()
// except you have to call the appropriate intersection test function (see RayVolumetricGeometryIntersectionTest())
+// Even more similar than I expected!
[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 volumetric
+ // (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)) // Extra variable time
+ {
+ 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..8fef46d 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 step = fmod(elapsedTime, cycleDuration) / cycleDuration;
+ if (step > 0.5f) {
+ step = 1.f - step;
+ }
+ return smoothstep(0, 1, step);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -121,17 +125,30 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics)
// TODO-3.1: Generate a ray in world space for a camera pixel corresponding to a dispatch index (analogous to a thread index in CUDA).
// Check out https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-12-raytracing-hlsl-system-value-intrinsics to see interesting
-// intrinsic HLSL raytracing functions you may use.
+// intrinsic HLSL raytracing functions you may use. - This is where I got DispatchRaysDimensions!
// Remember that you are given the pixel coordinates from index. You need to convert this to normalized-device coordinates first.
// Want: bottom left corner = (-1,-1) and top right corner = (1,1).
// Keep in mind that the pixel space in DirectX is top left = (0,0) and bottom right = (width, height)
// Once you have the normalized-device coordinates, use the projectionToWorld matrix to find a 3D location for this pixel. The depth will be wrong but
// as long as the direction of the ray is correct then the depth does not matter.
+// Was having trouble getting this to work at first, so Grace told me to add that "+ 0.5" to the NDC
+// calculation, and Tabatha reminded me to swap the order of multiplication from what I oiginally imagined
inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld)
{
+ // Converting Pixel Coordinates to Screen Space Coordinates (actually NDC)
+ float screenX = (float(index.x + 0.5f) / float(DispatchRaysDimensions().x)) * 2.f - 1.f;
+ float screenY = 1.f - (float(index.y + 0.5f) / float(DispatchRaysDimensions().y)) * 2.f;
+
+ // Point estimate, assuming Unhomogenized Screen Space but without a proper perpective divide
+ float4 pEst = float4(screenX, screenY, 1.f, 1.f);
+
+ // Get 3D location (convert to world space)
+ float4 dir = mul(pEst, projectionToWorld);
+ dir /= dir.w;
+
Ray ray;
- ray.origin = float3(0.0f, 0.0f, 0.0f);
- ray.direction = normalize(float3(0.0f, 0.0f, 0.0f));
+ ray.origin = cameraPosition;
+ ray.direction = normalize(dir.xyz - cameraPosition);
return ray;
}
@@ -141,7 +158,8 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4
// f0 is usually the albedo of the material assuming the outside environment is air.
float3 FresnelReflectanceSchlick(in float3 I, in float3 N, in float3 f0)
{
- return f0;
+ float cosTheta = abs(dot(normalize(I), normalize(N)));
+ return f0 + (1.f - f0) * pow(1.f - cosTheta, 5.f);
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..52e971b 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,13 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float d = distance(blob.center, position);
+ float x = (d <= blob.radius ? (blob.radius - d) / blob.radius : 0.f);// 1.f - max(distance(blob.center, position), blob.radius) / blob.radius;
+ float x3 = x * x * x;
+ float x4 = x3 * x;
+ float x5 = x4 * x;
+
+ return 6.f * x5 - 15.f * x4 + 10 * x3;
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -83,25 +89,65 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
{
tmin = INFINITY;
tmax = -INFINITY;
+
+ for (int blob = 0; blob < N_METABALLS; blob++) {
+ // Variables to pass in
+ float t0 = INFINITY;
+ float t1 = INFINITY;
+ // Use this previously created function, since metaballs are solid spheres
+ if (!RaySolidSphereIntersectionTest(ray, t0, t1,
+ blobs[blob].center, blobs[blob].radius)) {
+ continue;
+ }
+
+ // Retain the minimum and t-values from all intersected blobs :)
+ tmin = min(t0, tmin);
+ tmax = max(t1, tmax); // Clamped by RayTMin() and RayTCurrent() in RSSIntersectionTest
+ }
}
// TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field.
// The test sphere traces through the metaball field until it hits a threshold isosurface.
// Returns true if we found a point. False otherwise.
-// 1) Initialize a metaball array. See InitializeAnimatedMetaballs()
-// 2) Test intersections on the metaballs to find the minimum t and the maximum t to raymarch between.
-// 3) Use some number of steps (~128 is a good number for raymarching) to do the following:
-// a) Compute the total metaball potential over this point by summing ALL potentials of each metaball.
-// See CalculateMetaballsPotential().
-// b) If the total potential crosses an isosurface threshold (defined on (0,1]), then we will potentially
-// render this point:
-// i) We compute the normal at this point (see CalculateMetaballsNormal())
-// ii) Only render this point if it is valid hit. See is_a_valid_hit().
-// 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);
+
+ // 1) Initialize a metaball array. See InitializeAnimatedMetaballs()
+ Metaball blobs[N_METABALLS]; // out variable
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 16.f); // CycleDuration = a value of your choice that will
+ // define how much time it takes for an animation to restart
+
+ // 2) Test intersections on the metaballs to find the minimum t and the maximum t to raymarch between.
+ float t0 = 0;
+ float t1 = 0;
+ TestMetaballsIntersection(ray, t0, t1, blobs);
+
+ // 3) Use some number of steps (~128 is a good number for raymarching) to do the following:
+ float maxSteps = 128.f;
+ float dt = (t1 - t0) / maxSteps;
+ for (int steps = 0; steps < (int) maxSteps; steps++) {
+ // a) Compute the total metaball potential over this point by summing ALL potentials of each metaball.
+ // See CalculateMetaballsPotential().
+ float3 position = ray.origin + t0 * ray.direction;
+ float sumFieldPotential = CalculateMetaballsPotential(position, blobs);
+
+ // b) If the total potential crosses an isosurface threshold (defined on (0,1]), then we will potentially
+ // render this point:
+ if (sumFieldPotential > 0.1) {
+ // i) We compute the normal at this point (see CalculateMetaballsNormal())
+ float3 norm = CalculateMetaballsNormal(position, blobs);
+ // ii) Only render this point if it is valid hit. See is_a_valid_hit(), which checks segment .
+ if (is_a_valid_hit(ray, t0, norm)) {
+ attr.normal = norm;
+ thit = t0;
+ return true;
+ }
+ }
+ // If this condition fails, keep raymarching! - At a constant value since no SDF (dt)
+ t0 += dt;
+ }
return false;
}