diff --git a/DXR-Project.sdf b/DXR-Project.sdf new file mode 100644 index 0000000..81000cb Binary files /dev/null and b/DXR-Project.sdf differ diff --git a/DXR-Project.suo b/DXR-Project.suo new file mode 100644 index 0000000..1295b3c Binary files /dev/null and b/DXR-Project.suo differ diff --git a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters index cc2db39..b2678ea 100644 --- a/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters +++ b/Libraries/D3D12RaytracingFallback/src/FallbackLayer.vcxproj.filters @@ -201,6 +201,12 @@ + + + + + + diff --git a/README.md b/README.md index b0189d0..e268d78 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,86 @@ **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) +* Saket Karve + * [LinkedIn](https://www.linkedin.com/in/saket-karve-43930511b/), [twitter](), etc. +* Tested on: Windows 10, Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 16GB, NVIDIA Titan V (SIGLAB) -### (TODO: Your README) +### Highlights -Include screenshots, analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +![](images/three_spheres1.gif) + +### Contents + +- Ray Tracing +- Outputs +- Performance Analysis +- Bloopers + +### Ray Tracing + +ray tracing is a process similar to path tracing, except that it is deterministic (no more probabilities!) and that we only do a single pass over the entire scene (no more multiple iterations). This image summarizes what goes on in ray tracing: + +

+ +

+ +Specifically, the DXR execution pipeline mimics all the interactions depicted above. This diagram summarizes the DXR execution pipeline: + +

+ +

+ +This does not prevent us from calling `TraceRay()` multiple times. In fact, any self-respecting raytracing project will allow multiple (~3) `TraceRay()` calls. The common denominator between ray and path tracing is the depth of the ray. In this project, we use a *minimum depth of 3* to allow tracing the following: + +1. a **primary (radiance) ray** generated from the camera +2. a **shadow ray** just in case the ray hits a geometry on its way to the light source +3. a **reflection** ray in case the material of the object is reflective + +Therefore, the lifecycle of a single ray can be thought of as follows: + +1. generate a ray, see if it hits something +2. if it hits something, then attempt to *light/color* it + * attempting to color that hit point is equivalent to **tracing that ray towards the light source**. + * if that ray hits *another* object on its way to the light, then the region is effectively shadowed + * if not, then we successfully colored that point +3. if at any point we hit a reflective material, then trace another ray in the reflected direction and repeat the process + +### Outputs + +After completing all parts of the assignment, following output was observed. + +![](images/three_spheres1.gif) + +Adding a fourth sphere to the metaball produced this output. + +![](images/four_spheres.gif) + +Enabling lighting animation. + +![](images/three_spheres_light.gif) + +I tried using Sigmoid function to produce the blur effect for long distance objects. Following output was produced when a suffucient offset was used. + +![](images/sigmoid50PG) + +### Performance Analysis + +The performance of the render was tested for different values of Recursion depth. Performance is measured in terms of frames per second of the render. + +![](images/performance.PNG) + +It can be seen from the figure that the performance decreases with increasing depth. This is because for every render, the ray is traced more number of times. However, after a certain depth, the performance more or less remains same since the ray will most probably bounce into a light source (radiance ray) or become a shadow ray. + +### Bloopers + +Incorrect conversion to normalized device coordinates. Produced an inverted image + +![](images/incorrect_ndc.JPG) + +Forgot to change the function producing blur effect for AABB and changed it only for triangles. + +![](images/lerp_didnot_change_for_AABB.JPG) + +Sigmoid function for blur effect without any offset generated an image with a smoke/fog like effect + +![](images/sigmoid.JPG) diff --git a/images/Q3.PNG b/images/Q3.PNG new file mode 100644 index 0000000..18036e0 Binary files /dev/null and b/images/Q3.PNG differ diff --git a/images/four_spheres.gif b/images/four_spheres.gif new file mode 100644 index 0000000..d1195b4 Binary files /dev/null and b/images/four_spheres.gif differ diff --git a/images/incorrect_ndc.JPG b/images/incorrect_ndc.JPG new file mode 100644 index 0000000..1cce7a7 Binary files /dev/null and b/images/incorrect_ndc.JPG differ diff --git a/images/lerp_didnot_change_for_AABB.JPG b/images/lerp_didnot_change_for_AABB.JPG new file mode 100644 index 0000000..269be68 Binary files /dev/null and b/images/lerp_didnot_change_for_AABB.JPG differ diff --git a/images/performance.PNG b/images/performance.PNG new file mode 100644 index 0000000..64675b6 Binary files /dev/null and b/images/performance.PNG differ diff --git a/images/sigmoid.JPG b/images/sigmoid.JPG new file mode 100644 index 0000000..4830f1e Binary files /dev/null and b/images/sigmoid.JPG differ diff --git a/images/sigmoid50.JPG b/images/sigmoid50.JPG new file mode 100644 index 0000000..cd02100 Binary files /dev/null and b/images/sigmoid50.JPG differ diff --git a/images/three_spheres.gif b/images/three_spheres.gif new file mode 100644 index 0000000..1f3c29c Binary files /dev/null and b/images/three_spheres.gif differ diff --git a/images/three_spheres1.gif b/images/three_spheres1.gif new file mode 100644 index 0000000..cd5ac19 Binary files /dev/null and b/images/three_spheres1.gif differ diff --git a/images/three_spheres_camera_still.JPG b/images/three_spheres_camera_still.JPG new file mode 100644 index 0000000..cd23efe Binary files /dev/null and b/images/three_spheres_camera_still.JPG differ diff --git a/images/three_spheres_light.gif b/images/three_spheres_light.gif new file mode 100644 index 0000000..56afe07 Binary files /dev/null and b/images/three_spheres_light.gif differ diff --git a/images/three_spheres_light_still.JPG b/images/three_spheres_light_still.JPG new file mode 100644 index 0000000..a13474f Binary files /dev/null and b/images/three_spheres_light_still.JPG differ diff --git a/images/three_spheres_still.JPG b/images/three_spheres_still.JPG new file mode 100644 index 0000000..1f1613a Binary files /dev/null and b/images/three_spheres_still.JPG differ diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli index c6ccebb..b508df1 100644 --- a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli @@ -103,7 +103,7 @@ float3 CalculateNormalForARaySphereHit(in Ray ray, in float thit, float3 center) } // Test if a ray with RayFlags and segment intersects a hollow sphere. -bool RaySphereIntersectionTest(in Ray ray, out float thit, out float tmax, in ProceduralPrimitiveAttributes attr, in float3 center = float3(0, 0, 0), in float radius = 1) +bool RaySphereIntersectionTest(in Ray ray, out float thit, out float tmax, out ProceduralPrimitiveAttributes attr, in float3 center = float3(0, 0, 0), in float radius = 1) { float t0, t1; // solutions for t if the ray intersects @@ -166,18 +166,27 @@ 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.5, 0.5, -0.5), float3(0.2, 0, 0.2) }; + float radii[3] = { 0.7f, 0.3f, 0.5f }; thit = RayTCurrent(); float tmax; - if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius)) - { - return true; + float curr_thit = thit; + ProceduralPrimitiveAttributes curr_attr; + bool hit = false; + for (int i = 0; i < 3; i++) { + if (RaySphereIntersectionTest(ray, curr_thit, tmax, curr_attr, centers[i], radii[i])) + { + if (curr_thit < thit) { + thit = curr_thit; + attr = curr_attr; + } + hit = true; + } } - - return false; + return hit; + } #endif // ANALYTICPRIMITIVES_H \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj index 4a8b9ab..5fb1399 100644 --- a/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj +++ b/src/D3D12RaytracingProceduralGeometry/D3D12RaytracingProceduralGeometry.vcxproj @@ -15,7 +15,7 @@ Win32Proj D3D12Raytracing D3D12RaytracingProceduralGeometry - 10.0.17763.0 + 10.0.18362.0 @@ -76,8 +76,9 @@ Windows true - d3d12.lib;dxgi.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + d3d12.lib;dxgi.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64\atls.lib;%(AdditionalDependencies) d3d12.dll + C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64\;%(AdditionalLibraryDirectories) true @@ -131,6 +132,7 @@ PrebuildCheck.bat true d3d12.lib;dxgi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) d3d12.dll + C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64\;%(AdditionalLibraryDirectories) true diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp index 905341d..00270af 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -31,9 +31,15 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width) // * We filled in the format of the buffers to avoid confusion. auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0]; - geometryDesc = {}; + geometryDesc = {}; + geometryDesc.Flags = geometryFlags; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index); + geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width / sizeof(Vertex); + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer = {m_vertexBuffer.resource->GetGPUVirtualAddress(), 2 * sizeof(XMFLOAT3)}; } { @@ -51,7 +57,10 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + primitiveType * sizeof(D3D12_RAYTRACING_AABB); + } } } @@ -70,7 +79,11 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto // Again, these tell the AS where the actual geometry data is and how it is laid out. // TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs; - + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bottomLevelInputs.Flags = buildFlags; + bottomLevelInputs.NumDescs = geometryDescs.size(); + bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bottomLevelInputs.pGeometryDescs = geometryDescs.data(); // Query the driver for resource requirements to build an acceleration structure. We've done this for you. D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {}; @@ -110,7 +123,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 +143,12 @@ 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 +200,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 { + auto& instanceDesc = instanceDescs[BottomLevelASType::AABB]; + instanceDesc = {}; + instanceDesc.InstanceMask = 1; + instanceDesc.InstanceContributionToHitGroupIndex = 2; + instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB]; + + const XMVECTOR vBasePosition = XMLoadFloat3(&XMFLOAT3(0.0f, c_aabbWidth * 0.5f , 0.0f)); + XMMATRIX mTranslation = XMMatrixTranslationFromVector(vBasePosition); + XMMATRIX mTransform = 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 +235,12 @@ 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 = BottomLevelASType::Count; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + //CHECK AGAIN + //topLevelInputs.InstanceDescs = bottomLevelAS; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -218,7 +254,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,7 +269,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt } // TODO-2.6: Allocate a UAV buffer for the actual top-level AS. - + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &topLevelAS, initialResourceState, L"TopLevelAccelerationStructure"); } // Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer: @@ -261,6 +297,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 +310,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + BuildBottomLevelASInstanceDescs(bottomLevelASaddresses, &instanceDescsResource); } // Create a wrapped pointer to the acceleration structure. @@ -285,7 +322,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) @@ -304,7 +343,12 @@ 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 +364,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(auto bottomLevelASCount = 0; bottomLevelASCount < BottomLevelASType::Count; bottomLevelASCount++){ + bottomLevelAS[bottomLevelASCount] = BuildBottomLevelAS(geometryDescs[bottomLevelASCount]); + } // Batch all resource barriers for bottom-level AS builds. @@ -337,8 +384,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 +395,10 @@ 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(auto bottomLevelASCount = 0; bottomLevelASCount < BottomLevelASType::Count; bottomLevelASCount++){ + m_bottomLevelAS[bottomLevelASCount] = bottomLevelAS[bottomLevelASCount].accelerationStructure; + } + //CHECK AGAIN + 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..0362994 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DoRaytracing.cpp @@ -22,7 +22,8 @@ void DXProceduralProject::DoRaytracing() commandList->SetComputeRootConstantBufferView(GlobalRootSignature::Slot::SceneConstant, m_sceneCB.GpuVirtualAddress(frameIndex)); // TODO-2.8: do a very similar operation for the m_aabbPrimitiveAttributeBuffer - + m_aabbPrimitiveAttributeBuffer.CopyStagingToGpu(frameIndex); + commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AABBattributeBuffer, m_aabbPrimitiveAttributeBuffer.GpuVirtualAddress(frameIndex)); // Bind the descriptor heaps. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -49,10 +50,10 @@ void DXProceduralProject::DoRaytracing() // This should be done by telling the commandList to SetComputeRoot*(). You just have to figure out what * is. // Example: in the case of GlobalRootSignature::Slot::SceneConstant above, we used SetComputeRootConstantBufferView() // Hint: look at CreateRootSignatures() in DXR-Pipeline.cpp. - + commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::VertexBuffers, m_indexBuffer.gpuDescriptorHandle); // TODO-2.8: Bind the OutputView (basically m_raytracingOutputResourceUAVGpuDescriptor). Very similar to the Index/Vertex buffer. - + commandList->SetComputeRootDescriptorTable(GlobalRootSignature::Slot::OutputView, m_raytracingOutputResourceUAVGpuDescriptor); // This will define a `DispatchRays` function that takes in a command list, a pipeline state, and a descriptor // This will set the hooks using the shader tables built before and call DispatchRays on the command list @@ -60,13 +61,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..0fa5433 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -111,6 +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 num_elements = m_aabbs.size(); + auto frameCount = m_deviceResources->GetBackBufferCount(); + + m_aabbPrimitiveAttributeBuffer.Create(device, num_elements, frameCount, L"Scene Structured Buffer"); } @@ -164,6 +169,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 transform = XMMatrixMultiply(mScale, mRotation); + XMMATRIX final_transform = XMMatrixMultiply(transform, mTranslation); + XMMATRIX final_inverse_transform = XMMatrixInverse(nullptr, final_transform); + m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = final_transform; + m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = final_inverse_transform; }; UINT offset = 0; diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp index 9d93504..aecf053 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp @@ -86,7 +86,14 @@ 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 +117,14 @@ 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..ef15c01 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 primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++){ + auto hitGroup = raytracingPipeline->CreateSubobject(); + if (rayType == RayType::Radiance) + { + // We import the closest hit shader name + hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]); + } + //Set Intersection shader for hitgroup + hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[primitiveType]); + // 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); + } + } } } @@ -55,5 +71,16 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT // Very similar to triangles, except now one for each primitive type. { + auto localRootSignature = raytracingPipeline->CreateSubobject(); + + // This is the triangle local root signature you already filled in before. + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB].Get()); + + // Shader association + auto rootSignatureAssociation = raytracingPipeline->CreateSubobject(); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + for (UINT primitiveType = 0; primitiveType < IntersectionShaderType::Count; primitiveType++){ + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[primitiveType]); + } } } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp index 2dff8b5..77bbaf3 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. @@ -38,8 +38,15 @@ void DXProceduralProject::CreateRootSignatures() // u registers --> UAV // t registers --> SRV // 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 +74,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..019cb1e 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -32,8 +32,10 @@ void DXProceduralProject::BuildShaderTables() // TODO-2.7: Miss shaders. // Similar to the raygen shader, but now we have 1 for each ray type (radiance, shadow) // Don't forget to update shaderIdToStringMap. - missShaderIDs[0] = nullptr; - missShaderIDs[1] = nullptr; + missShaderIDs[0] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[0]); + shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0]; + missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]); + 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,6 +45,13 @@ void DXProceduralProject::BuildShaderTables() } // TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. + for (UINT i = 0; i < IntersectionShaderType::Count; i++) { + for (UINT j = 0; j < RayType::Count; j++) + { + hitGroupShaderIDs_AABBGeometry[i][j] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[i][j]); + shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[i][j]] = c_hitGroupNames_AABBGeometry[i][j]; + } + } }; @@ -95,7 +104,21 @@ 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"); + + for (UINT i = 0; i < RayType::Count; i++) { + // Push back the shader record, which does not need any root signatures. + missShaderTable.push_back(ShaderRecord(missShaderIDs[i], shaderRecordSize, nullptr, 0)); + } + + // Save the uploaded resource (remember that the uploaded resource is created when we call Allocate() on a GpuUploadBuffer + missShaderTable.DebugPrint(shaderIdToStringMap); + m_missShaderTable = missShaderTable.GetResource(); + m_missShaderTableStrideInBytes = missShaderTable.GetShaderRecordSize(); } // Hit group shader table. This one is slightly different given that a hit group requires its own custom root signature. diff --git a/src/D3D12RaytracingProceduralGeometry/Debug/D3D12RaytracingProceduralGeometry.vcxprojResolveAssemblyReference.cache b/src/D3D12RaytracingProceduralGeometry/Debug/D3D12RaytracingProceduralGeometry.vcxprojResolveAssemblyReference.cache new file mode 100644 index 0000000..1a52c1a Binary files /dev/null and b/src/D3D12RaytracingProceduralGeometry/Debug/D3D12RaytracingProceduralGeometry.vcxprojResolveAssemblyReference.cache differ 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..c6d1861 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,10 @@ 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(-incidentLightRay, normal)); + float3 reverseRayDirection = normalize(-WorldRayDirection()); + float4 specularComponent = pow(saturate(dot(reflectedRay, reverseRayDirection)), specularPower); + return specularComponent; } // TODO-3.6: Phong lighting model = ambient + diffuse + specular components. @@ -75,8 +78,27 @@ float4 CalculatePhongLighting(in float4 albedo, in float3 normal, in bool isInSh float4 ambientColorMax = g_sceneCB.lightAmbientColor; float a = 1 - saturate(dot(normal, float3(0, -1, 0))); ambientColor = albedo * lerp(ambientColorMin, ambientColorMax, a); + float shadowCoefficient = 1.0f; + if (isInShadow) { + shadowCoefficient = InShadowRadiance; + } - return ambientColor; + float3 hitPosition = HitWorldPosition(); + float3 lightPosition = g_sceneCB.lightPosition; + + float3 incidentLightRay = normalize(lightPosition - hitPosition); + float lambertianCoefficient = CalculateDiffuseCoefficient(incidentLightRay, normal); + float4 diffuseColor = shadowCoefficient * lambertianCoefficient * g_sceneCB.lightDiffuseColor * albedo; //albedo = Intensity!!?? + + float4 specularColor = float4(0.0f, 0.0f, 0.0f, 0.0f); + if (!isInShadow) { + float4 lightSpecularColor = float4(1.0f, 1.0f, 1.0f, 1.0f); + float4 specularCoefficient = CalculateSpecularCoefficient(incidentLightRay, normal, specularPower); + specularColor = specularCoefficient * lightSpecularColor; + } + + + return ambientColor + specularColor + diffuseColor; } //*************************************************************************** @@ -135,7 +157,36 @@ float4 TraceRadianceRay(in Ray ray, in UINT currentRayRecursionDepth) // Hint 2: remember what the ShadowRay payload looks like. See RaytracingHlslCompat.h bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) { - return false; + if (currentRayRecursionDepth >= MAX_RAY_RECURSION_DEPTH) + { + return false; + } + + // Set the ray's extents. + RayDesc rayDesc; + rayDesc.Origin = ray.origin; + rayDesc.Direction = ray.direction; + // Set TMin to a zero value to avoid aliasing artifacts along contact areas. + // Note: make sure to enable face culling so as to avoid surface face fighting. + rayDesc.TMin = 0; + rayDesc.TMax = 10000; + + ShadowRayPayload shadowRayPayload = { true }; + + + TraceRay(g_scene, + RAY_FLAG_CULL_BACK_FACING_TRIANGLES + | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH + | RAY_FLAG_FORCE_OPAQUE // ~skip any hit shaders + | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER, + TraceRayParameters::InstanceMask, + TraceRayParameters::HitGroup::Offset[RayType::Shadow], + TraceRayParameters::HitGroup::GeometryStride, + TraceRayParameters::MissShader::Offset[RayType::Shadow], + rayDesc, shadowRayPayload); + + return shadowRayPayload.hit; + //return false; } //*************************************************************************** @@ -149,9 +200,11 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) [shader("raygeneration")] void MyRaygenShader() { - + uint2 index = (uint2)DispatchRaysIndex().xy; + Ray ray = GenerateCameraRay(index, g_sceneCB.cameraPosition, 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; } //*************************************************************************** @@ -209,7 +262,9 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle // Hint 1: look at the intrinsic function RayTCurrent() that returns how "far away" your ray is. // Hint 2: use the built-in function lerp() to linearly interpolate between the computed color and the Background color. // When t is big, we want the background color to be more pronounced. - + float t = RayTCurrent(); + color = lerp(color, BackgroundColor, 1 - exp(-0.000001*pow(t, 3.0f))); + //color = lerp(color, BackgroundColor, (1 / (1 + exp(-(t - 50))))); rayPayload.color = color; } @@ -227,7 +282,34 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle [shader("closesthit")] void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr) { + // This is the intersection point on the triangle. + float3 hitPosition = HitWorldPosition(); + + // Trace a 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! + // 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); + + float t = RayTCurrent(); + color = lerp(color, BackgroundColor, 1 - exp(-0.000001*pow(t, 3.0f))); + //color = lerp(color, BackgroundColor, (1 / (1 + exp(-(t - 10))))); + rayPayload.color = color; } //*************************************************************************** @@ -240,14 +322,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 +381,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/RaytracingHlslCompat.h b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h index 6e10f0d..4b4d664 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 10 // ~ 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..f15a5a5 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 how_far = fmod(elapsedTime, cycleDuration) / cycleDuration; + if (how_far <= 0.5) { + how_far = 2.0f * how_far; + } + else { + how_far = 1.0f - (2.0f * (how_far - 0.5f)); + } + return smoothstep(0, 1, how_far); } // Load three 2-byte indices from a ByteAddressBuffer. @@ -129,11 +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 dimensions = DispatchRaysDimensions(); + float4 normalized_device_coordinates = { 2.0f * ((index.x /(1.0f * dimensions.x)) - 0.5f), 2.0f * (0.5f - (index.y /(1.0f * dimensions.y))), 1.0f, 1.0f }; + normalized_device_coordinates = normalize(normalized_device_coordinates); + //float4 normalized_device_coordinates = { (2.0f * (index.x - dimensions.x / 2.0f)) / dimensions.x, (2.0f * (index.y - dimensions.y / 2.0f)) / dimensions.y, 1.0f, 1.0f }; + + float4 world_coordinates = mul(normalized_device_coordinates, 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(world_coordinates.x, world_coordinates.y, world_coordinates.z)); - return ray; + return ray; } // TODO-3.6: Fresnel reflectance - schlick approximation. @@ -141,7 +155,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 cosi = saturate(dot(-I, N)); + return (f0 + (1 - f0) * pow(1 - cosi, 5)); } #endif // RAYTRACINGSHADERHELPER_H \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 31a9444..02992b4 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -15,14 +15,20 @@ struct Metaball // TODO-3.4.2: Calculate a magnitude of an influence from a Metaball charge. // This function should return a metaball potential, which is a float in range [0,1]. -// 1) If the point is at the center, the potential is maximum = 1. -// 2) If it is at the radius or beyond, the potential is 0. +// 1) If the point is at the center, the potential is maximum = 1.f +// 2) it is at the radius or beyond, the potential is 0.ff // 3) In between (i.e the distance from the center is between 0 and radius), consider using the a // quintic polynomial field function of the form 6x^5 - 15x^4 + 10x^3, such that x is the ratio // of the distance from the center to the radius. float CalculateMetaballPotential(in float3 position, in Metaball blob) { - return 0.0f; + float dist = distance(position, blob.center); + if (dist >= blob.radius) { + return 0.0f; + } + float ratio = dist / blob.radius; + float potential = 6 * pow(1 - ratio, 5) - 15 * pow(1 - ratio, 4) + 10 * pow(1 - ratio, 3); + return potential; } // LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials. @@ -62,11 +68,11 @@ void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elaps { { float3(-0.3, -0.3, -0.4),float3(0.3,-0.3,-0.0) }, // begin center --> end center { float3(0.0, -0.2, 0.5), float3(0.0, 0.4, 0.5) }, - { float3(0.4,0.4, 0.4), float3(-0.4, 0.2, -0.4) } + { float3(0.4,0.4, 0.4), float3(-0.4, 0.2, -0.4) }, }; // Metaball field radii of max influence - float radii[N_METABALLS] = { 0.45, 0.55, 0.45 }; + float radii[N_METABALLS] = { 0.6, 0.8, 0.7 }; // Calculate animated metaball center positions. float tAnimate = CalculateAnimationInterpolant(elapsedTime, cycleDuration); @@ -82,7 +88,18 @@ 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 curr_thit, curr_tmax; + if (RaySolidSphereIntersectionTest(ray, curr_thit, curr_tmax, blobs[i].center, blobs[i].radius)) { + tmin = min(tmin, curr_thit); + tmax = max(tmax, curr_tmax); + } + } + // Since it's a solid sphere, clip intersection points to ray extents. + 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 +117,30 @@ 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, 14.0f); + + float tmin, tmax; + TestMetaballsIntersection(ray, tmin, tmax, blobs); + + UINT MAXIMUM_STEPS = 128; + float t = tmin; + float step = (tmax - tmin) / MAXIMUM_STEPS; + float threshold = 0.9f; + while (t < tmax) { + float3 pos = ray.origin + t * ray.direction; + float total_potential = CalculateMetaballsPotential(pos, blobs); + if (total_potential > threshold) { + float3 normal = CalculateMetaballsNormal(pos, blobs); + if (is_a_valid_hit(ray, t, normal)) { + thit = t; + attr.normal = normal; + return true; + } + } + t += step; + } + return false; } #endif // VOLUMETRICPRIMITIVESLIBRARY_H \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/util/DeviceResources.cpp b/src/D3D12RaytracingProceduralGeometry/util/DeviceResources.cpp index 1ca04bb..21cd49b 100644 --- a/src/D3D12RaytracingProceduralGeometry/util/DeviceResources.cpp +++ b/src/D3D12RaytracingProceduralGeometry/util/DeviceResources.cpp @@ -509,6 +509,7 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) // Recommended to always use tearing if supported when using a sync interval of 0. // Note this will fail if in true 'fullscreen' mode. hr = m_swapChain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + //hr = m_swapChain->Present(1, 0); } else {