diff --git a/README.md b/README.md
index b0189d0..c1d34de 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,73 @@
+DirectX Ray Tracing
+======================
+
**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)
+* Srinath Rajagopalan
+ * [LinkedIn](https://www.linkedin.com/in/srinath-rajagopalan-07a43155)
+* Tested on: Windows 10, i7-6700 @ 3.4GHz 16GB, Nvidia GTX 1070 (SIG Lab)
+
+
+
+
+
+# Ray Tracing
+In this project, we implement ray tracing for procedural geometries using DirectX Ray Tracing API. Conceptually, ray tracing is similar to path tracing with a few differences. To render a 3D scene, we project it onto a 2D image representing the plane it would be captured if we took a picture of it from the camera. Each pixel on the image has a ray associated with it. This ray is responsible for shading the pixel with a color. Like a path tracer, we trace the ray and find which objects it intersects but after that we don't recursively follow the path of the ray. As soon as it hits an object, we directly reflect it towards a light source. If the ray hits any other obstacle before the light source it is treated as a shadow ray. This is different from a path tracer where we did not explicitly handle shadows. We traced the natural course of the ray and the shadows were generated as an emergent property. In ray tracing, we have only a single pass over the scene. It's deterministic unlike the probabilistic model we had in the path tracer.
+
+
+
+
+
+The ray tracing pipeline using the DirectX API is structured as follows,
+
+
+
+
+
+We trace a ray, and for each we traverse the acceleration structures to determine the objects of intersection. In the path tracer object, we naively tested each ray with ALL the objects in the scene. This will not scale when we are dealing with a complex scene with many objects. Acceleration structures dramatically reduce the time taken for intersection tests by eliminating a large group of objects the ray is never likely to intersect. Fortunately we don't have to implement the acceleration structures ourselves as DXR handles this. We define the AABBs (which dramatically simplifies the ray-intersection-test logic) and the corresponding transformations and the geometry equation for each object. If the ray intersects an object, we keep going till it doesn't intersect anything else. The intersection shader is responsible for this and it computes the point of intersection and the surface normal (if there is a hit). Finally, if there is a hit, the closest hit shader is responsible for assigning a color to this ray. We only evaluate the intersection of the closest object from the camera. Using the point of intersection and the surface normal, the closest-hit shader generates a reflected ray to the light source. If the ray is intersects another object before the light source, this ray is shadowed. If it hits the light source, it is shaded by factoring both the color of the material and the color of the light source.
+
+## Output
+
+Without factoring any lighting and from just the intersection tests and shadow rays, we get the following output,
+
+
+
+
+
+In this project, we color the objects using the [Phong Reflection Model](https://en.wikipedia.org/wiki/Phong_reflection_model) which factors ambient, diffuse, and specular lighting.
+
+
+
+
+
+If don't factor in shadows,
+
+
+
+
+
+With the camera and lighting moving, we get the following,
+
+
+
+
+
+
+
+
+
+With just 1 recursion depth we get the following,
+
+
+
+
+
+## Performance Analysis
+
+
+
+
-### (TODO: Your README)
+Tracking the performance across multiple recursion depths. Unsurprisingly we see huge dips as we go from 1 to 3 but after that the performance is stable. In our particular scene, most rays hit a light source or are shadowed after 3-4 bounces, so there is no additional cost for extra recursion depth as the rays terminate way earlier.
-Include screenshots, analysis, etc. (Remember, this is public, so don't put
-anything here that you don't want to share with the world.)
diff --git a/data/depth1.png b/data/depth1.png
new file mode 100644
index 0000000..bae8594
Binary files /dev/null and b/data/depth1.png differ
diff --git a/data/final.gif b/data/final.gif
new file mode 100644
index 0000000..64745c5
Binary files /dev/null and b/data/final.gif differ
diff --git a/data/final_camera.gif b/data/final_camera.gif
new file mode 100644
index 0000000..bd232fb
Binary files /dev/null and b/data/final_camera.gif differ
diff --git a/data/final_fresnel.gif b/data/final_fresnel.gif
new file mode 100644
index 0000000..13ac0cb
Binary files /dev/null and b/data/final_fresnel.gif differ
diff --git a/data/final_lighting.gif b/data/final_lighting.gif
new file mode 100644
index 0000000..fbb1e2f
Binary files /dev/null and b/data/final_lighting.gif differ
diff --git a/data/final_no_shadow.gif b/data/final_no_shadow.gif
new file mode 100644
index 0000000..412962a
Binary files /dev/null and b/data/final_no_shadow.gif differ
diff --git a/data/fps_depth.png b/data/fps_depth.png
new file mode 100644
index 0000000..18c22c4
Binary files /dev/null and b/data/fps_depth.png differ
diff --git a/data/no_phong.gif b/data/no_phong.gif
new file mode 100644
index 0000000..3b7a3ae
Binary files /dev/null and b/data/no_phong.gif differ
diff --git a/data/rtx_data.xlsx b/data/rtx_data.xlsx
new file mode 100644
index 0000000..bfff7a4
Binary files /dev/null and b/data/rtx_data.xlsx differ
diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
index c6ccebb..caac3d3 100644
--- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli
@@ -165,19 +165,48 @@ bool RaySolidSphereIntersectionTest(in Ray ray, out float thit, out float tmax,
// You can hardcode the local centers/radii of the spheres, just try to maintain them between 1 and -1 (and > 0 for the radii).
bool RayMultipleSpheresIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr)
{
- // Define the spheres in local space (within the aabb)
- float3 center = float3(-0.2, 0, -0.2);
- float radius = 0.7f;
- thit = RayTCurrent();
+ //float3 center = float3(-0.2, 0, -0.2);
+ //float radius = 0.7f;
- float tmax;
- if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
- {
- return true;
- }
+ //thit = RayTCurrent();
- return false;
+ //float tmax;
+ //if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius))
+ //{
+ // return true;
+ //}
+
+ //return false;
+
+ float3 center[3] = { float3(-0.2, 0, -0.2), float3(0.4, 0.7, 0.3), float3(0.6, -0.3, 0.6)};
+ float radius[3] = { 0.7f, 0.2f, 0.3f };
+
+ thit = RayTCurrent();
+
+ float tmax;
+ float tmin = thit;
+ bool hit = false;
+
+ ProceduralPrimitiveAttributes attrMin;
+
+ for (int i = 0; i < 3; i++) {
+ ProceduralPrimitiveAttributes tempAttr;
+ if (RaySphereIntersectionTest(ray, thit, tmax, tempAttr, center[i], radius[i]))
+ {
+ if (thit < tmin) {
+ tmin = thit;
+ attrMin = tempAttr;
+ }
+
+ hit = true;
+ }
+ }
+
+ thit = tmin;
+ attr = attrMin;
+
+ return hit;
}
#endif // ANALYTICPRIMITIVES_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
index 905341d..75e2ed1 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp
@@ -32,8 +32,15 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width / sizeof(Index);
+ geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex);
+ geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress();
+ geometryDesc.Triangles.VertexBuffer = {m_vertexBuffer.resource->GetGPUVirtualAddress(), sizeof(Vertex) };
+
}
{
@@ -51,6 +58,10 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + pIndex * size;
+ }
}
}
@@ -70,6 +81,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// Again, these tell the AS where the actual geometry data is and how it is laid out.
// TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout.
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs;
+ bottomLevelInputs.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.
@@ -110,7 +126,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc.
// Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.
// This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls.
-
+ bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress();
+ bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress();
// Fill up the command list with a command that tells the GPU how to build the bottom-level AS.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -129,7 +146,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto
// the AccelerationStructureBuffers struct so the top-level AS can use it!
// Don't forget that this is the return value.
// Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h
- return AccelerationStructureBuffers{};
+ return AccelerationStructureBuffers{ scratch, bottomLevelAS, nullptr, bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes};
}
// TODO-2.6: Build the instance descriptor for each bottom-level AS you built before.
@@ -181,7 +198,18 @@ 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 vBasePosition = c_aabbWidth * XMLoadFloat3(&XMFLOAT3(0.0f, 0.5f, 0.0f));
+
+ XMMATRIX mTranslation = XMMatrixTranslationFromVector(vBasePosition);
+ //XMMATRIX mScale = XMMatrixScaling(c_aabbWidth, c_aabbWidth, c_aabbWidth);
+ XMMATRIX mTransform = mTranslation;
+ XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform);
}
// Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource.
@@ -204,7 +232,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.InstanceDescs = BottomLevelASType::Count;
+ topLevelInputs.NumDescs = BottomLevelASType::Count;
+ topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -218,7 +250,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"ScratchResourceTop");
// Allocate space for the top-level AS.
{
@@ -233,7 +265,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"TopLevelAS");
}
// Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer:
@@ -261,7 +293,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above.
-
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
else // DirectX Raytracing
{
@@ -273,6 +305,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
};
// TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above.
+ BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource);
}
@@ -285,7 +318,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)
@@ -294,9 +329,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
ID3D12DescriptorHeap *pDescriptorHeaps[] = { m_descriptorHeap.Get() };
m_fallbackCommandList->SetDescriptorHeaps(ARRAYSIZE(pDescriptorHeaps), pDescriptorHeaps);
m_fallbackCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr);
- }
- else // DirectX Raytracing
- {
+ } else {
m_dxrCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr);
}
@@ -304,7 +337,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt
// Very similar to how you did this in BuildBottomLevelAS() except now you have to worry about topLevelASBuffers.instanceDesc.
// Consider looking into the AccelerationStructureBuffers struct in DXR-Structs.h.
// Make sure to return the topLevelASBuffers before you exit the function.
- return AccelerationStructureBuffers{};
+ return AccelerationStructureBuffers{scratch, topLevelAS, instanceDescsResource, topLevelPrebuildInfo.ResultDataMaxSizeInBytes };
}
// TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene.
@@ -320,12 +353,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];
-
+ for (UINT i = 0; i < BottomLevelASType::Count; i++)
+ bottomLevelAS[i] = BuildBottomLevelAS(geometryDescs[i]);
// Batch all resource barriers for bottom-level AS builds.
// This will Notifies the driver that it needs to synchronize multiple accesses to resources.
@@ -338,7 +372,7 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-2.6: Build top-level AS. Hint, you already made a function that does this.
AccelerationStructureBuffers topLevelAS;
-
+ topLevelAS = BuildTopLevelAS(bottomLevelAS);
// Kick off acceleration structure construction.
m_deviceResources->ExecuteCommandList();
@@ -349,5 +383,9 @@ void DXProceduralProject::BuildAccelerationStructures()
// TODO-2.6: Store the AS buffers. The rest of the buffers will be released once we exit the function.
// Do this for both the bottom-level and the top-level AS. Consider re-reading the DXProceduralProject class
// to find what member variables should be set.
-
+ for (UINT i = 0; i < BottomLevelASType::Count; i++)
+ m_bottomLevelAS[i] = bottomLevelAS[i].accelerationStructure;
+
+ m_topLevelAS = topLevelAS.accelerationStructure;
+
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp
index 03a8c58..f07e361 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());
// Bind the descriptor heaps.
if (m_raytracingAPI == RaytracingAPI::FallbackLayer)
@@ -49,10 +50,10 @@ void DXProceduralProject::DoRaytracing()
// This should be done by telling the commandList to SetComputeRoot*(). You just have to figure out what * is.
// Example: in the case of GlobalRootSignature::Slot::SceneConstant above, we used SetComputeRootConstantBufferView()
// Hint: look at CreateRootSignatures() in DXR-Pipeline.cpp.
-
+ commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::VertexBuffers, m_indexBuffer.gpuDescriptorHandle);
// TODO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer.
-
+ commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor);
// This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor
// This will set the hooks using the shader tables built before and call DispatchRays on the command list
@@ -60,13 +61,13 @@ void DXProceduralProject::DoRaytracing()
{
// You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc).
// TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE
-
+ dispatchDesc->HitGroupTable = { m_hitGroupShaderTable->GetGPUVirtualAddress(), m_hitGroupShaderTable->GetDesc().Width ,m_hitGroupShaderTableStrideInBytes };
// TODO-2.8: now fill in dispatchDesc->MissShaderTable
-
+ dispatchDesc->MissShaderTable = { m_missShaderTable->GetGPUVirtualAddress(), m_missShaderTable->GetDesc().Width , m_missShaderTableStrideInBytes };
// TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord
-
+ dispatchDesc->RayGenerationShaderRecord = { m_rayGenShaderTable->GetGPUVirtualAddress(), m_rayGenShaderTable->GetDesc().Width };
// We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA!
dispatchDesc->Width = m_width;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp
index e3ff63c..f4d39e5 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();
+ int numElements = m_aabbs.size();
+ m_aabbPrimitiveAttributeBuffer.Create(device, numElements, frameCount, L"Primitive Attribute Buffer");
}
// LOOKAT-2.1: Update camera matrices stored in m_sceneCB.
@@ -164,6 +168,11 @@ 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 transformMatrix = XMMatrixMultiply(XMMatrixMultiply(mScale , mRotation) , mTranslation);
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = transformMatrix;
+ m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, transformMatrix);
};
UINT offset = 0;
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
index 9d93504..7b5c907 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp
@@ -86,9 +86,16 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// This should take into account the basePosition and the stride defined above.
auto InitializeAABB = [&](auto& offsetIndex, auto& size)
{
- D3D12_RAYTRACING_AABB aabb{};
+ D3D12_RAYTRACING_AABB aabb{
+ basePosition.x + offsetIndex.x * stride.x,
+ basePosition.y + offsetIndex.y * stride.y,
+ basePosition.z + offsetIndex.z * stride.z,
+ basePosition.x + offsetIndex.x * stride.x + size.x,
+ basePosition.y + offsetIndex.y * stride.y + size.y,
+ basePosition.z + offsetIndex.z * stride.z + size.z};
return aabb;
};
+
m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount);
UINT offset = 0;
@@ -110,12 +117,13 @@ void DXProceduralProject::BuildProceduralGeometryAABBs()
// TODO-2.5: Allocate an upload buffer for this AABB data.
// The base data lives in m_aabbs.data() (the stuff you filled in!), but the allocationg should be pointed
// towards m_aabbBuffer.resource (the actual D3D12 resource that will hold all of our AABB data as a contiguous buffer).
-
+ AllocateUploadBuffer(device, m_aabbs.data(), m_aabbs.size() * sizeof(m_aabbs[0]), &m_aabbBuffer.resource);
}
}
// TODO-2.5: Build geometry used in the project. As easy as calling both functions above :)
void DXProceduralProject::BuildGeometry()
{
-
+ DXProceduralProject::BuildPlaneGeometry();
+ DXProceduralProject::BuildProceduralGeometryAABBs();
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
index 33899bd..dbcb452 100644
--- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
+++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp
@@ -29,7 +29,22 @@ void DXProceduralProject::CreateHitGroupSubobjects(CD3D12_STATE_OBJECT_DESC* ray
// TODO-2.3: AABB geometry hit groups. Very similar to triangles, except now you have to *also* loop over the primitive types.
{
+ for (UINT rayType = 0; rayType < RayType::Count; rayType++)
+ {
+ for (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++)
+ {
+ auto hitGroup = raytracingPipeline->CreateSubobject();
+ if (rayType == RayType::Radiance)
+ {
+ hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]);
+ }
+ hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primitiveType]);
+ hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primitiveType][rayType]);
+ hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE);
+ }
+
+ }
}
}
@@ -54,6 +69,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 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 (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) {
+ rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primitiveType]);
+ }
}
}
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp
index 2dff8b5..982b847 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::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]);
+ rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0);
+ rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0);
+ rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3);
+
// 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..6ce1fe3 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;
+ missShaderIDs[0] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[0]);
+ missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]);
+
+ shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0];
+ shaderIdToStringMap[missShaderIDs[1]] = c_missShaderNames[1];
// Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray.
for (UINT i = 0; i < RayType::Count; i++)
@@ -43,7 +46,13 @@ 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 j = 0; j < IntersectionShaderType::Count; j++)
+ {
+ hitGroupShaderIDs_AABBGeometry[j][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[j][i]);
+ shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[j][i]] = c_hitGroupNames_AABBGeometry[j][i];
+ }
+ }
};
// Get shader identifiers using the lambda function defined above.
@@ -95,6 +104,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 RayGen shader table contains a single ShaderRecord: the one single raygen shader!
+ ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
+
+ // Push back the shader record, which does not need any root signatures.
+ 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();
}
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..4317c90 100644
--- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
+++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl
@@ -41,7 +41,7 @@ ConstantBuffer l_aabbCB: register(b2); // other
// Remember to clamp the dot product term!
float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
{
- return 0.0f;
+ return saturate(dot(normal, incidentLightRay));
}
// TODO-3.6: Phong lighting specular component.
@@ -49,9 +49,13 @@ float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal)
// 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)
+float CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal, in float specularPower)
{
- return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float3 reflectedRay = normalize(reflect(-incidentLightRay, normal));
+ float3 reverseViewRay = normalize(-WorldRayDirection());
+ float specularCoeff = pow(saturate(dot(reflectedRay, reverseViewRay)), specularPower);
+
+ return specularCoeff;
}
// TODO-3.6: Phong lighting model = ambient + diffuse + specular components.
@@ -76,7 +80,26 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh
float a = 1 - saturate(dot(normal, float3(0, -1, 0)));
ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a);
- return ambientColor;
+ float3 lightPos = g_sceneCB.lightPosition.xyz;
+ float3 hitP = HitWorldPosition();
+
+ float3 incidentRay = normalize(lightPos - hitP);
+
+ diffuseCoef = CalculateDiffuseCoefficient(incidentRay, normal);
+
+ float4 diffuseColor = albedo * diffuseCoef * g_sceneCB.lightDiffuseColor;
+ if (isInShadow)
+ diffuseColor *= InShadowRadiance;
+
+ specularCoef = CalculateSpecularCoefficient(incidentRay, normal, specularPower);
+
+ float4 specularColor;
+ if (isInShadow)
+ specularColor = float4(1.0f, 1.0f, 1.0f, 1.0f) * specularCoef;
+ else
+ specularColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
+
+ return ambientColor + specularColor + diffuseColor;
}
//***************************************************************************
@@ -135,7 +158,29 @@ 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;
+ }
+
+ RayDesc rayDesc;
+ rayDesc.Origin = ray.origin;
+ rayDesc.Direction = ray.direction;
+ rayDesc.TMin = 0;
+ rayDesc.TMax = 10000;
+
+ ShadowRayPayload rayPayload = { true };
+
+ TraceRay(g_scene,
+ RAY_FLAG_CULL_BACK_FACING_TRIANGLES,
+ TraceRayParameters::InstanceMask,
+ TraceRayParameters::HitGroup::Offset[RayType::Shadow],
+ TraceRayParameters::HitGroup::GeometryStride,
+ TraceRayParameters::MissShader::Offset[RayType::Shadow],
+ rayDesc, rayPayload);
+
+
+ return rayPayload.hit;
}
//***************************************************************************
@@ -149,9 +194,10 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth)
[shader("raygeneration")]
void MyRaygenShader()
{
-
+ Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld);
+ float4 color = TraceRadianceRay(ray, 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] = color;
}
//***************************************************************************
@@ -210,6 +256,8 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
// Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color.
// When t is big, we want the background color to be more pronounced.
+ float t = RayTCurrent();
+ color = lerp(color, BackgroundColor, 1 - exp(-0.000001*pow(t, 3.0f)));
rayPayload.color = color;
}
@@ -227,7 +275,31 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle
[shader("closesthit")]
void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr)
{
+ float3 hitPosition = HitWorldPosition();
+
+ // Trace a ray from the hit position towards the single light source we have. If on our way to the light we hit something, then we have a shadow!
+ Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) };
+ bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth);
+ // Reflected component ray.
+ float4 reflectedColor = float4(0, 0, 0, 0);
+ if (l_materialCB.reflectanceCoef > 0.001)
+ {
+ // Trace a reflection ray from the intersection points using Snell's law. The reflect() HLSL built-in function does this for you!
+ Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), attr.normal) };
+ float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth);
+
+ float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, l_materialCB.albedo.xyz);
+ reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor;
+ }
+
+ // Calculate final color.
+ float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, attr.normal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower);
+ float4 color = (phongColor + reflectedColor);
+
+ float t = RayTCurrent();
+ color = lerp(color, BackgroundColor, 1 - exp(-0.000001*pow(t, 3.0f)));
+ rayPayload.color = color;
}
//***************************************************************************
@@ -240,14 +312,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 +371,24 @@ void MyIntersectionShader_AnalyticPrimitive()
[shader("intersection")]
void MyIntersectionShader_VolumetricPrimitive()
{
+ Ray localRay = GetRayInAABBPrimitiveLocalSpace();
+ VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType;
+ // The point of the intersection shader is to:
+ // (1) find out what is the t at which the ray hits the procedural
+ // (2) pass on some attributes used by the closest hit shader to do some shading (e.g: normal vector)
+ float thit;
+ ProceduralPrimitiveAttributes attr;
+ if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime))
+ {
+ PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex];
+
+ // Make sure the normals are stored in BLAS space and not the local space
+ attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS);
+ attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal));
+
+ // thit is invariant to the space transformation
+ ReportHit(thit, /*hitKind*/ 0, attr);
+ }
}
#endif // RAYTRACING_HLSL
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
index 94bf5cc..e38591c 100644
--- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli
@@ -68,7 +68,14 @@ 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 curCycleTime = fmod(elapsedTime, cycleDuration)/ cycleDuration;
+ if (curCycleTime > 0.5) {
+ curCycleTime = 1.0 - 2.0*(curCycleTime - 0.5);
+ }
+ else {
+ curCycleTime = 2.0*curCycleTime;
+ }
+ return smoothstep(0, 1, curCycleTime);
}
// Load three 2-byte indices from a ByteAddressBuffer.
@@ -129,9 +136,18 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics)
// as long as the direction of the ray is correct then the depth does not matter.
inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld)
{
+
+ uint3 dim = DispatchRaysDimensions();
+ float2 ndc = float2(index.x / (dim.x * 1.0f), index.y / (dim.y * 1.0f));
+ ndc.x = 2.0f * (ndc.x - 0.5f);
+ ndc.y = 2.0f * (0.5f - ndc.y);
+
+ float4 rayDir = normalize(float4(ndc.x, ndc.y, 1.0f, 1.0f));
+ float4 point3d = mul(rayDir, 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;
+ ray.direction = normalize(float3(point3d.x, point3d.y, point3d.z));
return ray;
}
@@ -141,7 +157,10 @@ 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;
+ float3 fR;
+ fR = f0 + (1 - f0)*pow(1 - saturate(dot(-I, N)), 5.0f);
+ return fR;
+
}
#endif // RAYTRACINGSHADERHELPER_H
\ No newline at end of file
diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
index 31a9444..cec11d6 100644
--- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
+++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli
@@ -22,7 +22,12 @@ struct Metaball
// of the distance from the center to the radius.
float CalculateMetaballPotential(in float3 position, in Metaball blob)
{
- return 0.0f;
+ float d = length(position - blob.center);
+ if (d > blob.radius) return 0.0f;
+
+ float x = (blob.radius - d)/ blob.radius;
+ float potential = 6.0f * pow(x, 5) - 15.0 * pow(x, 4) + 10.0 * pow(x, 3);
+ return potential;
}
// LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials.
@@ -83,6 +88,20 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
{
tmin = INFINITY;
tmax = -INFINITY;
+
+
+ for (int i = 0; i < N_METABALLS; i++) {
+
+ float tminCurr, tmaxCurr;
+ if (RaySolidSphereIntersectionTest(ray, tminCurr, tmaxCurr, blobs[i].center, blobs[i].radius)) {
+ tmin = min(tmin, tminCurr);
+ tmax = max(tmax, tmaxCurr);
+ }
+ }
+
+ tmin = max(tmin, RayTMin());
+ tmax = min(tmax, RayTCurrent());
+
}
// TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field.
@@ -100,8 +119,36 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout
// If this condition fails, keep raymarching!
bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime)
{
- thit = 0.0f;
- attr.normal = float3(0.0f, 0.0f, 0.0f);
+ Metaball blobs[N_METABALLS];
+ InitializeAnimatedMetaballs(blobs, elapsedTime, 8.0f);
+
+
+ float tmin, tmax;
+
+ TestMetaballsIntersection(ray, tmin, tmax, blobs);
+
+ int steps = 128;
+ float th = 0.20f;
+
+ float tp = tmin;
+ float jump = (tmax - tmin) /((float)steps);
+
+ while (tp <= tmax) {
+ float3 pos = ray.origin + tp * ray.direction;
+ float totalPotential = CalculateMetaballsPotential(pos, blobs);
+
+ if (totalPotential > th) {
+ float3 normal = CalculateMetaballsNormal(pos, blobs);
+ if (is_a_valid_hit(ray, tp, normal)) {
+ thit = tp;
+ attr.normal = normal;
+ return true;
+ }
+ }
+
+ tp += jump;
+ }
+
return false;
}