diff --git a/README.md b/README.md index b0189d0..d3e6594 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,42 @@ **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) +* Name: Vaibhav Arcot + * [LinkedIn] (https://www.linkedin.com/in/vaibhav-arcot-129829167/) +* Tested on: Windows 10, i7-7700HQ @ 2.8GHz (3.8 Boost) 32GB, External GTX 1080Ti, 11G (My personal laptop) +![Banner](./images/camera_light.gif) -### (TODO: Your README) +## Overview + +This repo contains a toy ray tracer using DirectX API. This differs from path tracing (can be found [here]( https://github.com/Black-Phoenix/Project3-CUDA-Path-Tracer )) in the sense that each ray has a deterministic way it bounces, so we don't need to sample multiple rays per pixel to generate a cohesive picture. + +## Approach +A general overview of ray tracing is well summarized in the following picture. We first generate rays for each pixel into the scene. Once we have the rays, we then find the intersection with the ray to objects in the scene. Once we hit an object we spawn 2 types of rays, a shadow ray and depending on the material, another view ray. +![](./images/raytrace.jpg) +The DXR pipeline follows a very similar pipeline, shown below. + +![](./images/pipeline.png) + +## Results + +To showcase the final output, below are a few gifs of the output. The first one is with just the metaballs & spheres moving + +![simple](./images/simple.gif) + +Below is a gif of the light moving around the objects in the scene (and a pause to the animation in the middle) + +![light](./images/light.gif) + +Finally below is a gif of the camera moving around the stationary objects + +![](./images/camera_fixed.gif) + +## Analysis +Performance metrics were collected to showcase the effects of depth on various metrics + +| Metric | Graph | +| ----------------------- | --------------------------------------- | +| FPS | ![FPS](./images/depth_vs_fps.png) | +| Dispatch ray function | ![](./images/depth_vs_dispatchRay.png) | +| Primary rays per second | ![](./images/depth_vs_primary_rays.png) | -Include screenshots, analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) diff --git a/images/camera_fixed.gif b/images/camera_fixed.gif new file mode 100644 index 0000000..c3766a2 Binary files /dev/null and b/images/camera_fixed.gif differ diff --git a/images/camera_light.gif b/images/camera_light.gif new file mode 100644 index 0000000..2f45007 Binary files /dev/null and b/images/camera_light.gif differ diff --git a/images/data.xlsx b/images/data.xlsx new file mode 100644 index 0000000..bdb2ca5 Binary files /dev/null and b/images/data.xlsx differ diff --git a/images/depth_vs_dispatchRay.png b/images/depth_vs_dispatchRay.png new file mode 100644 index 0000000..df7cc37 Binary files /dev/null and b/images/depth_vs_dispatchRay.png differ diff --git a/images/depth_vs_fps.png b/images/depth_vs_fps.png new file mode 100644 index 0000000..c5eb3b0 Binary files /dev/null and b/images/depth_vs_fps.png differ diff --git a/images/depth_vs_primary_rays.png b/images/depth_vs_primary_rays.png new file mode 100644 index 0000000..efaa50f Binary files /dev/null and b/images/depth_vs_primary_rays.png differ diff --git a/images/light.gif b/images/light.gif new file mode 100644 index 0000000..68be839 Binary files /dev/null and b/images/light.gif differ diff --git a/images/simple.gif b/images/simple.gif new file mode 100644 index 0000000..1b68b75 Binary files /dev/null and b/images/simple.gif differ diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli index c6ccebb..73413fa 100644 --- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli @@ -166,18 +166,30 @@ 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 centers[3] = + { + float3(-0.2, 0, -0.2), + float3(0.4, 0.1, 0.2), + float3(0.3, 0.2, 0.1) + }; + float radius[3] = { 0.7f, 0.3f, 0.6f }; thit = RayTCurrent(); + bool hit = false; + for (int i = 0; i < 3; i++) { + float tmax; + float thit_tmp; + ProceduralPrimitiveAttributes attr_tmp; + if (RaySphereIntersectionTest(ray, thit_tmp, tmax, attr_tmp, centers[i], radius[i])) + { + if (thit_tmp < thit) { + thit = thit_tmp; + attr = attr_tmp; + hit = true; + } + } - float tmax; - if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius)) - { - return true; } - - return false; + 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..bc4e625 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -28,12 +28,19 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress()) - // * The *total size* of the buffer can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width) + // * The *total size* of the buffer can be accessed from GetDesc().Width (e.g m_indexBuffer.resource->GetDesc().Width) (in bytes) // * We filled in the format of the buffers to avoid confusion. auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0]; geometryDesc = {}; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StrideInBytes = 2 * sizeof(XMFLOAT3); + geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width/sizeof(Index); + geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width/sizeof(Vertex); geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + geometryDesc.Flags = geometryFlags; } { @@ -51,7 +58,11 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + primitiveType * aabbDescTemplate.AABBs.AABBs.StrideInBytes; + } } } @@ -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.Flags = buildFlags; + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + 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. @@ -110,7 +126,9 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // TODO-2.6: Now that you have the scratch and actual bottom-level AS desc, pass their GPU addresses to the bottomLevelBuildDesc. // Consider reading about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. - + + bottomLevelBuildDesc.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 +147,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 +199,19 @@ 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 { + // Make the plane a little larger than the actual number of primitives in each dimension. + auto& instanceDesc = instanceDescs[BottomLevelASType::AABB]; + instanceDesc = {}; + instanceDesc.InstanceMask = 1; + instanceDesc.InstanceContributionToHitGroupIndex = 2; + instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB]; + + // Store the transform in the instanceDesc. + XMMATRIX mTransform = XMMatrixTranslationFromVector(XMLoadFloat3(&XMFLOAT3(0.0f, c_aabbWidth * 0.5f, 0.0f))); + + // Store the transform in the instanceDesc. + XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTransform); } // Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource. @@ -204,7 +234,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.Flags = buildFlags; + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + topLevelInputs.NumDescs = BottomLevelASType::Count; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + //topLevelInputs.InstanceDescs = bottomLevelAS[0].instanceDesc->GetGPUVirtualAddress(); D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -218,7 +252,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); // TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data. - + AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource"); // Allocate space for the top-level AS. { @@ -233,6 +267,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt } // TODO-2.6: Allocate a UAV buffer for the actual top-level AS. + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &topLevelAS, initialResourceState, L"TopLevelAccelerationStructure"); } @@ -261,7 +296,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource); } else // DirectX Raytracing { @@ -273,7 +308,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. @@ -286,7 +322,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // TODO-2.6: fill in the topLevelBuildDesc. Read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC. // This should be as easy as passing the GPU addresses to the struct using GetGPUVirtualAddress() calls. - + topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); + topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress(); + topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress(); + // Build acceleration structure. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) { @@ -304,7 +343,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,11 +359,14 @@ void DXProceduralProject::BuildAccelerationStructures() // TODO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this. array, BottomLevelASType::Count> geometryDescs; - + BuildGeometryDescsForBottomLevelAS(geometryDescs); // TODO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS. // Hint: you filled in a function that does this. AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count]; + for (int i = 0; i < BottomLevelASType::Count; i++) { + bottomLevelAS[i] = BuildBottomLevelAS(geometryDescs[i]); + } // Batch all resource barriers for bottom-level AS builds. @@ -338,8 +380,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 +390,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. - -} \ No newline at end of file + for (int i = 0; i < BottomLevelASType::Count; i++) { + m_bottomLevelAS[i] = bottomLevelAS[i].accelerationStructure; + } + m_topLevelAS = topLevelAS.accelerationStructure; +} diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp index 03a8c58..277945b 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -22,7 +22,9 @@ void DXProceduralProject::DoRaytracing() commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex)); // TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer - + m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex); + commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex)); + // Bind the descriptor heaps. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -50,9 +52,11 @@ void DXProceduralProject::DoRaytracing() // 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 +64,20 @@ void DXProceduralProject::DoRaytracing() { // You will fill in a D3D12_DISPATCH_RAYS_DESC (which is dispatchDesc). // TODO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE - + dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress(); + dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width; + dispatchDesc->HitGroupTable.StrideInBytes = m_hitGroupShaderTableStrideInBytes; // TODO-2.8: now fill in dispatchDesc->MissShaderTable + dispatchDesc->MissShaderTable.StartAddress = m_missShaderTable->GetGPUVirtualAddress(); + dispatchDesc->MissShaderTable.SizeInBytes = m_missShaderTable->GetDesc().Width; + dispatchDesc->MissShaderTable.StrideInBytes = m_missShaderTableStrideInBytes; // TODO-2.8: now fill in dispatchDesc->RayGenerationShaderRecord - + dispatchDesc->RayGenerationShaderRecord.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress(); + dispatchDesc->RayGenerationShaderRecord.SizeInBytes = m_rayGenShaderTable->GetDesc().Width; + // We do this for you. This will define how many threads will be dispatched. Basically like a blockDims in CUDA! dispatchDesc->Width = m_width; @@ -92,4 +103,4 @@ void DXProceduralProject::DoRaytracing() { DispatchRays(m_dxrCommandList.Get(), m_dxrStateObject.Get(), &dispatchDesc); } -} \ No newline at end of file +} diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp index e3ff63c..e1169e5 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -111,7 +111,10 @@ void DXProceduralProject::CreateConstantBuffers() // structured buffers are for structs that have dynamic data (e.g lights in a scene, or AABBs in this case) void DXProceduralProject::CreateAABBPrimitiveAttributesBuffers() { - + auto device = m_deviceResources->GetD3DDevice(); + auto frameCount = m_deviceResources->GetBackBufferCount(); + auto numElements = m_aabbs.size(); + m_aabbPrimitiveAttributeBuffer.Create(device, numElements, frameCount, L"AABB primitive attributes Buffer"); } // LOOKAT-2.1: Update camera matrices stored in m_sceneCB. @@ -164,6 +167,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 localSpaceToBottomLevelAS; // Matrix from local primitive space to bottom-level object space. + // XMMATRIX bottomLevelASToLocalSpace; // Matrix from bottom-level object space to local primitive space + XMMATRIX mTransform = XMMatrixMultiply(XMMatrixMultiply(mScale, mRotation), mTranslation); + m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = mTransform; + m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = XMMatrixInverse(nullptr, mTransform); }; UINT offset = 0; diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp index 9d93504..a753b01 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, + basePosition.y + offsetIndex.y * stride.y, + basePosition.z + offsetIndex.z * stride.z, + basePosition.x + offsetIndex.x * stride.x + size.x, + basePosition.y + offsetIndex.y * stride.y + size.y, + basePosition.z + offsetIndex.z * stride.z + size.z}; return aabb; }; m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount); @@ -110,12 +115,13 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() // TODO-2.5: Allocate an upload buffer for this AABB data. // The base data lives in m_aabbs.data() (the stuff you filled in!), but the allocationg should be pointed // towards m_aabbBuffer.resource (the actual D3D12 resource that will hold all of our AABB data as a contiguous buffer). - + AllocateUploadBuffer(device, m_aabbs.data(), m_aabbs.size() * sizeof(m_aabbs[0]), &m_aabbBuffer.resource); } } // TODO-2.5: Build geometry used in the project. As easy as calling both functions above :) void DXProceduralProject::BuildGeometry() { - + BuildPlaneGeometry(); + BuildProceduralGeometryAABBs(); } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp index 33899bd..d82ab46 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 primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++) { + for (UINT rayType = 0; rayType < RayType::Count; rayType++) + { + auto hitGroup = raytracingPipeline->CreateSubobject(); + hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primitiveType]); + if (rayType == RayType::Radiance) + { + // We import the closest hit shader name + hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]); + } + // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type + hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[primitiveType][rayType]); + hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE); + } + } } } @@ -54,6 +69,16 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT // TODO-2.3: AABB geometry hitgroup/local root signature association. // Very similar to triangles, except now one for each primitive type. { - + auto localRootSignature = raytracingPipeline->CreateSubobject(); + + // This is the 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..f1f8c5b 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp @@ -21,7 +21,8 @@ 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); // 2 SRV resources + // TODO-2.2: Initialize all the parameters of the GlobalRootSignature in their appropriate slots. // * See GlobalRootSignature in RaytracingSceneDefines.h to understand what they are. @@ -39,6 +40,13 @@ void DXProceduralProject::CreateRootSignatures() // t registers --> SRV // b registers --> CBV CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count]; + // set all rootParameter values + rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]); // bound to register 0 + rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0); + rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0); + rootParameters[GlobalRootSignature::Slot::AABBattributeBuffer].InitAsShaderResourceView(3); + rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]); // bound to register 1 + 2 + // 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); @@ -67,7 +75,14 @@ void DXProceduralProject::CreateRootSignatures() // to register 1, this overlap is allowed since we are talking about *local* root signatures // --> the values they hold will depend on the shader function the local signature is bound to! { - + namespace RootSignatureSlots = LocalRootSignature::AABB::Slot; + CD3DX12_ROOT_PARAMETER rootParameters[RootSignatureSlots::Count]; + rootParameters[RootSignatureSlots::MaterialConstant].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 1); + rootParameters[RootSignatureSlots::GeometryIndex].InitAsConstants(SizeOfInUint32(PrimitiveConstantBuffer), 2); + + CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB]); } } } diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp index 150e92d..b84600e 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -34,7 +34,10 @@ void DXProceduralProject::BuildShaderTables() // Don't forget to update shaderIdToStringMap. missShaderIDs[0] = nullptr; missShaderIDs[1] = nullptr; - + for (UINT i = 0; i < RayType::Count; i++) { + missShaderIDs[i] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[i]); + shaderIdToStringMap[missShaderIDs[i]] = c_missShaderNames[i]; + } // Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray. for (UINT i = 0; i < RayType::Count; i++) { @@ -43,7 +46,15 @@ void DXProceduralProject::BuildShaderTables() } // TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. - + for (UINT j = 0; j < IntersectionShaderType::Count; j++) { + + for (UINT i = 0; i < RayType::Count; i++) + { + hitGroupShaderIDs_AABBGeometry[j][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[j][i]); + shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[j][i]] = c_hitGroupNames_AABBGeometry[j][i]; + } + } + }; // Get shader identifiers using the lambda function defined above. @@ -95,7 +106,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 numMissShaderRecords = 2; + UINT missShaderRecordSize = shaderIDSize; // No root arguments ???? + + // The RayGen shader table contains a single ShaderRecord: the one single raygen shader! + ShaderTable MissShaderTable(device, numMissShaderRecords, missShaderRecordSize, L"MissShaderTable"); + + // Push back the shader record, which does not need any root signatures. + MissShaderTable.push_back(ShaderRecord(missShaderIDs[0], missShaderRecordSize, nullptr, 0)); + MissShaderTable.push_back(ShaderRecord(missShaderIDs[1], missShaderRecordSize, 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. @@ -133,6 +157,8 @@ void DXProceduralProject::BuildShaderTables() // in DXR-Pipeline.cpp. So if you did AABB, then Sphere, then Metaballs, then follow that order. // the primitive type is used to tell the shader what type of procedural geometry this is. // Remember that hitGroupShaderIDs_AABBGeometry is a 2-array indexed like so [type of geometry][ray type] + + // AABB geometry hit groups. { LocalRootSignature::AABB::RootArguments rootArgs; UINT instanceIndex = 0; 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..840140b 100644 --- a/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl +++ b/src/D3D12RaytracingProceduralGeometry/Raytracing.hlsl @@ -41,7 +41,7 @@ ConstantBuffer l_aabbCB: register(b2); // other // Remember to clamp the dot product term! float CalculateDiffuseCoefficient(in float3 incidentLightRay, in float3 normal) { - return 0.0f; + return saturate(dot(-incidentLightRay, normal)); } // TODO-3.6: Phong lighting specular component. @@ -51,7 +51,7 @@ 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); + return float(pow(saturate(dot(normalize(reflect(incidentLightRay, normal)), normalize(-WorldRayDirection()))), specularPower)); } // TODO-3.6: Phong lighting model = ambient + diffuse + specular components. @@ -68,6 +68,16 @@ float4 CalculateSpecularCoefficient(in float3 incidentLightRay, in float3 normal 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) { + float3 incidentLightRay = normalize(HitWorldPosition() - g_sceneCB.lightPosition.xyz); + float lambCoeff = CalculateDiffuseCoefficient(incidentLightRay, normal); + float shadowColor = isInShadow ? InShadowRadiance : 1.0; + float4 diffuseColor = diffuseCoef * lambCoeff * g_sceneCB.lightDiffuseColor * albedo * shadowColor; + + // Specular component. + float4 specularColor = float4(0, 0, 0, 0); + + if (!isInShadow) + specularColor = specularCoef * CalculateSpecularCoefficient(incidentLightRay, normal, specularPower) * float4(1, 1, 1, 1); // Ambient component // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit float4 ambientColor = g_sceneCB.lightAmbientColor; @@ -76,7 +86,7 @@ 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; + return ambientColor + diffuseColor + specularColor; } //*************************************************************************** @@ -135,7 +145,24 @@ 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; //avoids collision with aliasing artifacts along contact areas + rayDesc.TMax = 10000; + // Spawn a shadow ray + ShadowRayPayload shadowRayPayload = { true }; + TraceRay(g_scene, + RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, + TraceRayParameters::InstanceMask, + TraceRayParameters::HitGroup::Offset[RayType::Shadow], + TraceRayParameters::HitGroup::GeometryStride, + TraceRayParameters::MissShader::Offset[RayType::Shadow], + rayDesc, shadowRayPayload); + + return shadowRayPayload.hit; } //*************************************************************************** @@ -148,10 +175,10 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) // (3) Write that color to the render target [shader("raygeneration")] void MyRaygenShader() -{ - +{ + Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, g_sceneCB.cameraPosition.xyz, g_sceneCB.projectionToWorld); // Write the color to the render target - g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f); + g_renderTarget[DispatchRaysIndex().xy] = TraceRadianceRay(ray, 0); } //*************************************************************************** @@ -210,7 +237,10 @@ 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. - rayPayload.color = color; + float t = RayTCurrent() / 100.0f; + float f = 1.0f / (1.0f + exp(-t + 2.0f)); + color = lerp(color, BackgroundColor, f); + rayPayload.color = color; } // TODO: Write the closest hit shader for a procedural geometry. @@ -227,6 +257,37 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle [shader("closesthit")] void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr) { + float3 hitPosition = HitWorldPosition(); + + Ray shadowRay = { hitPosition, normalize(g_sceneCB.lightPosition.xyz - hitPosition) }; + bool shadowRayHit = TraceShadowRayAndReportIfHit(shadowRay, rayPayload.recursionDepth); + + float4 reflectedColor = float4(0, 0, 0, 0); + if (l_materialCB.reflectanceCoef > 0.001) + { + // Trace a reflection ray from the intersection points using Snell's law. The reflect() HLSL built-in function does this for you! + // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions + Ray reflectionRay = { hitPosition, reflect(WorldRayDirection(), attr.normal) }; + float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth); + + float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, l_materialCB.albedo.xyz); + reflectedColor = l_materialCB.reflectanceCoef * float4(fresnelR, 1) * reflectionColor; + } + + // Calculate final color. + float4 phongColor = CalculatePhongLighting(l_materialCB.albedo, attr.normal, shadowRayHit, l_materialCB.diffuseCoef, l_materialCB.specularCoef, l_materialCB.specularPower); + float4 color = (phongColor + reflectedColor); + + // If the ray is very very very far away, tends to sample the background color rather than the color you computed. + // This is to mimic some form of distance fog where farther objects appear to blend with the background. + // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is. + // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color. + // When t is big, we want the background color to be more pronounced. + + float t = RayTCurrent() / 100.0f; + float f = 1.0f / (1.0f + exp(-t + 2.0f)); + color = lerp(color, BackgroundColor, f); + rayPayload.color = color; } @@ -240,14 +301,14 @@ void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitive [shader("miss")] void MyMissShader(inout RayPayload rayPayload) { - + rayPayload.color = float4(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 +360,18 @@ void MyIntersectionShader_AnalyticPrimitive() [shader("intersection")] void MyIntersectionShader_VolumetricPrimitive() { - + Ray localRay = GetRayInAABBPrimitiveLocalSpace(); + VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType; + + float thit; + ProceduralPrimitiveAttributes attr; + if (RayVolumetricGeometryIntersectionTest(localRay, primitiveType, thit, attr, g_sceneCB.elapsedTime)) + { + PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex]; + attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS); + attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal)); + + ReportHit(thit, 0, attr); + } } -#endif // RAYTRACING_HLSL \ No newline at end of file +#endif // RAYTRACING_HLSL diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h index 6e10f0d..76a92af 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h @@ -28,7 +28,7 @@ typedef UINT16 Index; #define N_FRACTAL_ITERATIONS 5 // = <1,...> -#define MAX_RAY_RECURSION_DEPTH 3 // ~ primary rays + reflections + shadow rays from reflected geometry. +#define MAX_RAY_RECURSION_DEPTH 4 // ~ primary rays + reflections + shadow rays from reflected geometry. /**************** Scene *****************/ static const XMFLOAT4 ChromiumReflectance = XMFLOAT4(0.549f, 0.556f, 0.554f, 1.0f); diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli index 94bf5cc..2607798 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 interpolant = fmod(elapsedTime, cycleDuration)/cycleDuration; + if (interpolant <= 0.5) { + interpolant = 2 * interpolant; + } + else { + interpolant = 1 - 2 * (interpolant - 0.5f); + } + return smoothstep(0, 1, interpolant); } // Load three 2-byte indices from a ByteAddressBuffer. @@ -130,8 +137,13 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld) { Ray ray; - ray.origin = float3(0.0f, 0.0f, 0.0f); - ray.direction = normalize(float3(0.0f, 0.0f, 0.0f)); + ray.origin = cameraPosition; + float2 screenPos = (index + 0.5f) / DispatchRaysDimensions().xy * 2.0 - 1.0; + screenPos.y = -screenPos.y; // D3D12 reverses y apparently + float4 norm_points = { screenPos, 0, 1 }; + float4 dir = mul(norm_points, projectionToWorld); + dir.xyz /= dir.w; + ray.direction = normalize(dir.xyz - cameraPosition); return ray; } @@ -141,7 +153,7 @@ 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; + return f0 + (1-f0)*pow(1-saturate(dot(-I,N)),5); } -#endif // RAYTRACINGSHADERHELPER_H \ No newline at end of file +#endif // RAYTRACINGSHADERHELPER_H diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 31a9444..ec43512 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -22,7 +22,11 @@ struct Metaball // of the distance from the center to the radius. float CalculateMetaballPotential(in float3 position, in Metaball blob) { - return 0.0f; + float distance = length(position - blob.center); + if (distance >= blob.radius) + return 0.0f; + float x = float((blob.radius - distance)) / float(blob.radius); + return 6.0f * pow(x, 5) - 15.0f * pow(x, 4) + 10.0f * pow(x, 3); } // LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials. @@ -82,7 +86,17 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout Metaball blobs[N_METABALLS]) { tmin = INFINITY; - tmax = -INFINITY; + tmax = -INFINITY; + + for (UINT i = 0; i < N_METABALLS; i++) { + float temp_tmin, temp_tmax; + if (RaySolidSphereIntersectionTest(ray, temp_tmin, temp_tmax, blobs[i].center, blobs[i].radius)) { + tmin = min(temp_tmin, tmin); + tmax = max(temp_tmax, tmax); + } + } + tmin = max(tmin, RayTMin()); + tmax = min(tmax, RayTCurrent()); } // TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field. @@ -100,9 +114,26 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout // If this condition fails, keep raymarching! bool RayMetaballsIntersectionTest(in Ray ray, out float thit, out ProceduralPrimitiveAttributes attr, in float elapsedTime) { - thit = 0.0f; - attr.normal = float3(0.0f, 0.0f, 0.0f); - return false; + Metaball blobs[N_METABALLS]; + InitializeAnimatedMetaballs(blobs, elapsedTime, 11.0f); + float tmin, tmax; + TestMetaballsIntersection(ray, tmin, tmax, blobs); + float step = (tmax - tmin) / 128.0f; + for (float t = tmin; t <= tmax; t += step) + { + float3 position = ray.origin + t * ray.direction; + if (CalculateMetaballsPotential(position, blobs) > 0.2f) + { + float3 sur_normal = CalculateMetaballsNormal(position, blobs); + if (is_a_valid_hit(ray, t, sur_normal)) + { + thit = t; + attr.normal = sur_normal; + return true; + } + } + } + return false; } -#endif // VOLUMETRICPRIMITIVESLIBRARY_H \ No newline at end of file +#endif // VOLUMETRICPRIMITIVESLIBRARY_H