diff --git a/README.md b/README.md index b0189d0..0bad28d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,73 @@ **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) +* Eric Micallef + * https://www.linkedin.com/in/eric-micallef-99291714b/ + +* Tested on: Windows 10, i5, Nvidia GTX1660 (Personal) -### (TODO: Your README) +- [Overview](#Overview) +- [Performance Analysis](#Performance-Analysis) +- [Debilitating Bugs/Bloopers](#Debilitating-Bugs-AKA-Bloopers-In-CIS565-World) +- [References](#References) + +# Overview + +Special thanks to Ziad. He is probably sick of seeing me in his OH and on Piazza. Without him I would have thrown my desktop out the window. + +In this project we used DirectX12 to implement a basic path tracer. In this homework we were asked to implement the CPU side of DirectX12 as well as the GPU logic of raytracing in DirectX12. Below you will see some animations of the ray tracer in motion. + +![](images/sceenshot.PNG) + +![](images/done.gif) + +![](images/lighting.gif) + +![](images/pivot.gif) + +# Performance Analysis + +Below are some data points after manipulating the recursion depth. As you can see, the further we follow the ray slower our application becomes. I also did not see any noticeable rendering difference with these increasing recursion depths. + +![](images/FPS.png) + +# Debilitating Bugs AKA Bloopers In CIS565 World + +![](images/looks_close_enough.PNG) + +After watching my program crash 10000000000000000000 times because my cpu side was wrong, finally I got something up... but then I had to sadly go back and fix alot of stuff ... + +![](images/fuzzy.PNG) + +turns out there was alot wrong here ... alot ... But I do like the darker purple and darker yellow colors! + +![](images/wtf.PNG) + +my lerping for fading was off.. way offf.... Lerping is not to be confused with the ever popular LARPing. + +![](images/weird_balls.gif) + +If you look you can see that the two purple balls move in a very odd manner. was kind of neat. + +problem was my logic for keeping track of closet hit was a bit off. I was keeping track of tmax, a sensical person would have looked at the function return and saw it was evaluating thit... I became sensical after about 2 hours of debugging. + +![](images/floatvsfloat3.gif) + +This is what happens when you accidentally return a float instead of float3 ): + +# References + +Ziad ... without him I would have thrown my computer out the window + +http://viclw17.github.io/2018/11/29/raymarching-algorithm/ + +https://www.freecodecamp.org/news/understanding-linear-interpolation-in-ui-animations-74701eb9957c/ + +https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection + +https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection + +https://www.scratchapixel.com/lessons/3d-basic-rendering/phong-shader-BRDF + +https://www.scratchapixel.com/lessons/advanced-rendering/rendering-distance-fields/blobbies -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/AABB.PNG b/images/AABB.PNG new file mode 100644 index 0000000..7168ec2 Binary files /dev/null and b/images/AABB.PNG differ diff --git a/images/FPS.png b/images/FPS.png new file mode 100644 index 0000000..2af56c4 Binary files /dev/null and b/images/FPS.png differ diff --git a/images/TLAS_and_BLAS.png b/images/TLAS_and_BLAS.png new file mode 100644 index 0000000..de425e8 Binary files /dev/null and b/images/TLAS_and_BLAS.png differ diff --git a/images/aa.PNG b/images/aa.PNG new file mode 100644 index 0000000..e7004d3 Binary files /dev/null and b/images/aa.PNG differ diff --git a/images/done.gif b/images/done.gif new file mode 100644 index 0000000..d0966e8 Binary files /dev/null and b/images/done.gif differ diff --git a/images/fin.gif b/images/fin.gif new file mode 100644 index 0000000..45c7b87 Binary files /dev/null and b/images/fin.gif differ diff --git a/images/floatvsfloat3.gif b/images/floatvsfloat3.gif new file mode 100644 index 0000000..bf16c6a Binary files /dev/null and b/images/floatvsfloat3.gif differ diff --git a/images/fuzzy.PNG b/images/fuzzy.PNG new file mode 100644 index 0000000..c7553ab Binary files /dev/null and b/images/fuzzy.PNG differ diff --git a/images/lerp.PNG b/images/lerp.PNG new file mode 100644 index 0000000..ac2da58 Binary files /dev/null and b/images/lerp.PNG differ diff --git a/images/lighting.gif b/images/lighting.gif new file mode 100644 index 0000000..de805e7 Binary files /dev/null and b/images/lighting.gif differ diff --git a/images/looks_close_enough.PNG b/images/looks_close_enough.PNG new file mode 100644 index 0000000..27a7e3d Binary files /dev/null and b/images/looks_close_enough.PNG differ diff --git a/images/metaball.PNG b/images/metaball.PNG new file mode 100644 index 0000000..49ac51b Binary files /dev/null and b/images/metaball.PNG differ diff --git a/images/pivot.gif b/images/pivot.gif new file mode 100644 index 0000000..270f127 Binary files /dev/null and b/images/pivot.gif differ diff --git a/images/pix.PNG b/images/pix.PNG new file mode 100644 index 0000000..085a483 Binary files /dev/null and b/images/pix.PNG differ diff --git a/images/regular.gif b/images/regular.gif new file mode 100644 index 0000000..6cfbe3d Binary files /dev/null and b/images/regular.gif differ diff --git a/images/sceenshot.PNG b/images/sceenshot.PNG new file mode 100644 index 0000000..0aef28d Binary files /dev/null and b/images/sceenshot.PNG differ diff --git a/images/weird_balls.gif b/images/weird_balls.gif new file mode 100644 index 0000000..1ae6f7d Binary files /dev/null and b/images/weird_balls.gif differ diff --git a/images/wtf.PNG b/images/wtf.PNG new file mode 100644 index 0000000..9ceac4b Binary files /dev/null and b/images/wtf.PNG differ diff --git a/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/AnalyticPrimitives.hlsli index c6ccebb..6b32cb8 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,39 @@ 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 center[3]; + float radius[3]; + bool hit = false; + + center[0] = float3(-0.2, 0, -0.2); + radius[0] = 0.5f; + center[1] = float3(-.4, -.5, -0.9); + radius[1] = .3f; + center[2] = float3(0.7, 0.8, 0.2); + radius[2] = .2f; - thit = RayTCurrent(); - float tmax; - if (RaySphereIntersectionTest(ray, thit, tmax, attr, center, radius)) + thit = RayTCurrent(); + float temp_max =0; + float tmax = 0; + float temp_hit = thit; + ProceduralPrimitiveAttributes temp_attr; + for (int i = 0; i < 3; i++) { - return true; + if (RaySphereIntersectionTest(ray, temp_hit, temp_max, temp_attr, center[i], radius[i])) + { + //not sure why thit and not max but oh well tmax causes for a mind bending bug + if (temp_hit < thit) + { + tmax = temp_max; + attr = temp_attr; + thit = temp_hit; + hit = 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 084077a..0c1aa0d 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-AccelerationStructure.cpp @@ -31,7 +31,28 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetDesc().Width) auto& geometryDesc = geometryDescs[BottomLevelASType::Triangle][0]; geometryDesc = {}; + + // + geometryDesc.Flags = geometryFlags; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + + // + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + // why is width count? because it says so above i guess? + geometryDesc.Triangles.IndexCount = m_indexBuffer.resource->GetDesc().Width / sizeof(Index); + geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; + + // think stride is ok based on below + //https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_raytracing_geometry_triangles_desc + // doc says zero again? + UINT64 stride = sizeof(Vertex); + geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StrideInBytes = stride; + + //how do I get count? + geometryDesc.Triangles.VertexCount = m_vertexBuffer.resource->GetDesc().Width/ sizeof(Vertex); + geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; } { @@ -49,6 +70,15 @@ void DXProceduralProject::BuildGeometryDescsForBottomLevelAS(arrayGetGPUVirtualAddress() + stride; + } } } @@ -61,14 +91,17 @@ AccelerationStructureBuffers DXProceduralProject::BuildBottomLevelAS(const vecto ComPtr scratch; // temporary AS data ComPtr bottomLevelAS; // actual bottom-level AS resource - // This is what we need to fill in. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {}; // The bottom-level inputs are the geometries you build in BuildGeometryDescsForBottomLevelAS() // Again, these tell the AS where the actual geometry data is and how it is laid out. // TODO-2.6: fill the bottom-level inputs. Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as the DescsLayout. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &bottomLevelInputs = bottomLevelBuildDesc.Inputs; - + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bottomLevelInputs.Flags = buildFlags; + bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bottomLevelInputs.NumDescs = static_cast( geometryDescs.size() ); + bottomLevelInputs.pGeometryDescs = geometryDescs.data(); // Query the driver for resource requirements to build an acceleration structure. We've done this for you. D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {}; @@ -108,7 +141,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. - + // there are two virtual address fields in the structure so we set them here + bottomLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); + bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAS->GetGPUVirtualAddress(); // Fill up the command list with a command that tells the GPU how to build the bottom-level AS. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -127,7 +162,17 @@ 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{}; + //ComPtr scratch; // temp data needed by the GPU to build the AS + //ComPtr accelerationStructure; // actual AS data + //ComPtr instanceDesc; // used only for top-level AS + //UINT64 ResultDataMaxSizeInBytes; // this can be acquired from the prebuild info + AccelerationStructureBuffers return_buff = {}; + return_buff.scratch = scratch; + return_buff.accelerationStructure = bottomLevelAS; + return_buff.instanceDesc = nullptr; // used only for top level + return_buff.ResultDataMaxSizeInBytes = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes; + + return return_buff; } // TODO-2.6: Build the instance descriptor for each bottom-level AS you built before. @@ -179,7 +224,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; // making the mask same as triangle for now + // how the fuck is this intuitive? I try numbers until stuff appeared on my screen .. great use of my time and money + instanceDesc.InstanceContributionToHitGroupIndex = BottomLevelASType::AABB * RayType::Count;// maybe this? + instanceDesc.AccelerationStructure = bottomLevelASaddresses[BottomLevelASType::AABB]; + + // TODO do I need anything else here? + // also not intuitive + XMMATRIX mTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&XMFLOAT3(0, c_aabbWidth / 2, 0))); + // Store the transform in the instanceDesc. + XMStoreFloat3x4(reinterpret_cast(instanceDesc.Transform), mTranslation); } // Upload all these instances to the GPU, and make sure the resouce is set to instanceDescsResource. @@ -202,7 +259,10 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt // TODO-2.6: fill in the topLevelInputs, read about D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS. // Consider using D3D12_ELEMENTS_LAYOUT_ARRAY as a DescsLayout since we are using an array of bottom-level AS. D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS &topLevelInputs = topLevelBuildDesc.Inputs; - + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + topLevelInputs.Flags = buildFlags; + topLevelInputs.NumDescs = BottomLevelASType::Count;// trianlge and AABB or are there more or some value ERIC + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -216,7 +276,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); // TODO-2.6: Allocate a UAV buffer for the scracth/temporary top-level AS data. - + // give unordered access the explanation is on line 121 + AllocateUAVBuffer(device, topLevelPrebuildInfo.ScratchDataSizeInBytes, &scratch, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchTODO2.6"); // Allocate space for the top-level AS. { @@ -231,7 +292,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"TopLevelAS2.6"); } // Note on Emulated GPU pointers (AKA Wrapped pointers) requirement in Fallback Layer: @@ -259,7 +320,8 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the fallback-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + // what do I pass in for the template. WRAPPED_GPU_POINTER seems wrong + BuildBottomLevelASInstanceDescs< D3D12_RAYTRACING_FALLBACK_INSTANCE_DESC, WRAPPED_GPU_POINTER>(bottomLevelASaddresses, &instanceDescsResource); } else // DirectX Raytracing { @@ -271,7 +333,7 @@ AccelerationStructureBuffers DXProceduralProject::BuildTopLevelAS(AccelerationSt }; // TODO-2.6: Call the DXR-templated version of BuildBottomLevelASInstanceDescs() you completed above. - + BuildBottomLevelASInstanceDescs< D3D12_RAYTRACING_INSTANCE_DESC, D3D12_GPU_VIRTUAL_ADDRESS>(bottomLevelASaddresses, &instanceDescsResource); } // Create a wrapped pointer to the acceleration structure. @@ -283,7 +345,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. - + // like above except we add the instance since we are top level + topLevelInputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress(); + topLevelBuildDesc.DestAccelerationStructureData = topLevelAS->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = scratch->GetGPUVirtualAddress(); // Build acceleration structure. if (m_raytracingAPI == RaytracingAPI::FallbackLayer) @@ -302,7 +367,17 @@ 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{}; + //ComPtr scratch; // temp data needed by the GPU to build the AS + //ComPtr accelerationStructure; // actual AS data + //ComPtr instanceDesc; // used only for top-level AS + //UINT64 ResultDataMaxSizeInBytes; // this can be acquired from the prebuild info + AccelerationStructureBuffers return_buff = {}; + return_buff.scratch = scratch; + return_buff.accelerationStructure = topLevelAS; + return_buff.instanceDesc = instanceDescsResource; + return_buff.ResultDataMaxSizeInBytes = topLevelPrebuildInfo.ResultDataMaxSizeInBytes; + + return return_buff; } // TODO-2.6: This will wrap building the Acceleration Structure! This is what we will call when building our scene. @@ -312,18 +387,23 @@ void DXProceduralProject::BuildAccelerationStructures() auto commandList = m_deviceResources->GetCommandList(); auto commandQueue = m_deviceResources->GetCommandQueue(); auto commandAllocator = m_deviceResources->GetCommandAllocator(); + auto flag = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE; // the other flags seem too fancy for me // Reset the command list for the acceleration structure construction. commandList->Reset(commandAllocator, nullptr); // TODO-2.6: Build the geometry descriptors. Hint: you filled in a function that does this. array, BottomLevelASType::Count> geometryDescs; - + + BuildGeometryDescsForBottomLevelAS(geometryDescs); // TODO-2.6: For each bottom-level object (triangle, procedural), build a bottom-level AS. // Hint: you filled in a function that does this. AccelerationStructureBuffers bottomLevelAS[BottomLevelASType::Count]; - + for (UINT count = 0; count < BottomLevelASType::Count; count++) + { + bottomLevelAS[count] = BuildBottomLevelAS(geometryDescs[count], flag); + } // Batch all resource barriers for bottom-level AS builds. // This will Notifies the driver that it needs to synchronize multiple accesses to resources. @@ -336,7 +416,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, flag); // Kick off acceleration structure construction. m_deviceResources->ExecuteCommandList(); @@ -347,5 +427,11 @@ 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. - + // set all bottoms + for (UINT count = 0; count < BottomLevelASType::Count; count++) + { + m_bottomLevelAS[count] = bottomLevelAS[count].accelerationStructure; + } + // set the top level we only have one for this prj + 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..ed56895 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) @@ -45,27 +46,32 @@ void DXProceduralProject::DoRaytracing() commandList->SetComputeRootShaderResourceView(GlobalRootSignature::Slot::AccelerationStructure, m_topLevelAS->GetGPUVirtualAddress()); } - // TODO-2.8: Bind the Index/Vertex buffer (basically m_indexBuffer. Think about why this isn't m_vertexBuffer too. Hint: CreateRootSignatures() in DXR-Pipeline.cpp.) + // TDO-2.8: Bind the Index/Vertex buffer (basically m_indexBuffer. Think about why this isn't m_vertexBuffer too. Hint: CreateRootSignatures() in DXR-Pipeline.cpp.) // 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. - + + // TDO-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 auto DispatchRays = [&](auto* raytracingCommandList, auto* stateObject, auto* dispatchDesc) { // 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 - + // TDO-2.8: fill in dispatchDesc->HitGroupTable. Look up the struct D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE - // TODO-2.8: now fill in dispatchDesc->MissShaderTable - + // the stride in bytes was set earlier ... by me ... so hopefully I am right + dispatchDesc->HitGroupTable = { m_hitGroupShaderTable->GetGPUVirtualAddress(),m_hitGroupShaderTable->GetDesc().Width,m_hitGroupShaderTableStrideInBytes }; + + // TDO-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 + // praying width is in bytes... + // TDO-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! diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp index e3ff63c..733b1c7 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-DynamicBuffers.cpp @@ -111,7 +111,12 @@ 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() { - + // similiar to creating the scene buffer above + auto device = m_deviceResources->GetD3DDevice(); + //look at 1.7 holds 1 AABB per procedural object (e.g 1 for metaballs, 1 for a sphere, etc..) + auto aabb_objects = m_aabbs.size(); + // dxproceduralproject.h holds the primitive buffer LOOKAT 1.6 has info + m_aabbPrimitiveAttributeBuffer.Create(device, aabb_objects, 3, L"TODO2.1"); } // LOOKAT-2.1: Update camera matrices stored in m_sceneCB. @@ -164,6 +169,17 @@ 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. + + // do the scale * rotation part + auto transform_matrice = XMMatrixMultiply(mScale, mRotation); + // multiply translation to get your transform matrice + transform_matrice = XMMatrixMultiply(transform_matrice, mTranslation); + // create our inverse transform matrice + auto inv_transform_matrice = XMMatrixInverse(nullptr, transform_matrice); + // this is like our example here https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/mathematics-computing-2d-coordinates-of-3d-points + // creating local to world and world to local + m_aabbPrimitiveAttributeBuffer[primitiveIndex].localSpaceToBottomLevelAS = transform_matrice; + m_aabbPrimitiveAttributeBuffer[primitiveIndex].bottomLevelASToLocalSpace = inv_transform_matrice; }; UINT offset = 0; diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp index 9d93504..6bf0f73 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Geometry.cpp @@ -87,6 +87,19 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() auto InitializeAABB = [&](auto& offsetIndex, auto& size) { D3D12_RAYTRACING_AABB aabb{}; + /* FLOAT MinX; + FLOAT MinY; + FLOAT MinZ; + FLOAT MaxX; + FLOAT MaxY; + FLOAT MaxZ;*/ + // fuck with variables until a decent thing appears and then hope its fine + aabb.MinX = basePosition.x + offsetIndex.x * stride.x; + aabb.MinY = basePosition.y + offsetIndex.y * stride.y; + aabb.MinZ = basePosition.z + offsetIndex.z * stride.z; + aabb.MaxX = basePosition.x + offsetIndex.x * stride.x + size.x; + aabb.MaxY = basePosition.y + offsetIndex.y * stride.y + size.y; + aabb.MaxZ = basePosition.z + offsetIndex.z * stride.z + size.z; return aabb; }; m_aabbs.resize(IntersectionShaderType::TotalPrimitiveCount); @@ -110,12 +123,15 @@ void DXProceduralProject::BuildProceduralGeometryAABBs() // TODO-2.5: Allocate an upload buffer for this AABB data. // The base data lives in m_aabbs.data() (the stuff you filled in!), but the allocationg should be pointed // towards m_aabbBuffer.resource (the actual D3D12 resource that will hold all of our AABB data as a contiguous buffer). - + // m_aabbs is a vector of balls and metaballs + UINT64 copy_size = m_aabbs.size()*sizeof(D3D12_RAYTRACING_AABB); + AllocateUploadBuffer(device, m_aabbs.data(), copy_size, &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..a4cae8f 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-HitGroup.cpp @@ -28,8 +28,28 @@ 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. + // the primitive types are Analytic and volumetric in raytracing scene defines { + for (UINT rayType = 0; rayType < RayType::Count; rayType++) + { + for (UINT count = 0; count < IntersectionShaderType::Count; count++) + { + auto hitGroup = raytracingPipeline->CreateSubobject(); + if (rayType == RayType::Radiance) + { + // We import the closest hit shader name + hitGroup->SetClosestHitShaderImport(c_closestHitShaderNames[GeometryType::AABB]); + } + //and intersection shaders (mandatory but not for triangles). + // SetIntersectionShaderImport(LPCWSTR importName) + // set the name, as of today this is either the analytic name or volumetric name + hitGroup->SetIntersectionShaderImport(c_intersectionShaderNames[count]); + // We tell the hitgroup that it should export into the correct shader hit group name, with the correct type + hitGroup->SetHitGroupExport(c_hitGroupNames_AABBGeometry[count][rayType]); + hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE); + } + } } } @@ -54,6 +74,17 @@ void DXProceduralProject::CreateLocalRootSignatureSubobjects(CD3D12_STATE_OBJECT // TODO-2.3: AABB geometry hitgroup/local root signature association. // Very similar to triangles, except now one for each primitive type. { - + auto localRootSignature = raytracingPipeline->CreateSubobject(); + + // This is the AABB local root signature you already filled in before. + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature[LocalRootSignature::Type::AABB].Get()); + + // Shader association + auto rootSignatureAssociation = raytracingPipeline->CreateSubobject(); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + // same as above but AABB has both these types + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[IntersectionShaderType::AnalyticPrimitive]); + rootSignatureAssociation->AddExports(c_hitGroupNames_AABBGeometry[IntersectionShaderType::VolumetricPrimitive]); + } } \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp b/src/D3D12RaytracingProceduralGeometry/DXR-RootSignature.cpp index 2dff8b5..1bfa64a 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); // numdescriptors =2 base register 1 .. seems ok // 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,7 +38,27 @@ void DXProceduralProject::CreateRootSignatures() // u registers --> UAV // t registers --> SRV // b registers --> CBV + + //what we need to set + //OutputView = 0, + // AccelerationStructure, + // SceneConstant, + // AABBattributeBuffer, + // VertexBuffers, + // Count + // somewhat helpful? + //https://docs.microsoft.com/en-us/windows/win32/direct3d12/descriptor-tables-overview CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignature::Slot::Count]; + // init at as a descriptor table with our initd UAV bind to register 0 = + rootParameters[GlobalRootSignature::Slot::OutputView].InitAsDescriptorTable(1, &ranges[0]); + // set the vertex buffer set to register 1 in above init bind to register 1 hope it overlaps to 2? + rootParameters[GlobalRootSignature::Slot::VertexBuffers].InitAsDescriptorTable(1, &ranges[1]); + // acceleration SRV = shader resource view set to register 0 + rootParameters[GlobalRootSignature::Slot::AccelerationStructure].InitAsShaderResourceView(0); + // set as CBV to register 0 + rootParameters[GlobalRootSignature::Slot::SceneConstant].InitAsConstantBufferView(0); + // why 3? to match hlsl file thats why + 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); @@ -67,7 +87,15 @@ 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! { - + // I am inspired ... lol + 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); // I think instance is ok the other buffer seems to be for the material from .h file + + 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..92516c7 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp +++ b/src/D3D12RaytracingProceduralGeometry/DXR-ShaderTable.cpp @@ -32,8 +32,11 @@ void DXProceduralProject::BuildShaderTables() // TODO-2.7: Miss shaders. // Similar to the raygen shader, but now we have 1 for each ray type (radiance, shadow) // Don't forget to update shaderIdToStringMap. - missShaderIDs[0] = nullptr; - missShaderIDs[1] = nullptr; + missShaderIDs[0] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[0]); // this is miss shader + missShaderIDs[1] = stateObjectProperties->GetShaderIdentifier(c_missShaderNames[1]); // this is miss shadow ray + // sset their names + shaderIdToStringMap[missShaderIDs[0]] = c_missShaderNames[0]; + shaderIdToStringMap[missShaderIDs[1]] = c_missShaderNames[1]; // Hitgroup shaders for the Triangle. We have 2: one for radiance ray, and another for the shadow ray. for (UINT i = 0; i < RayType::Count; i++) @@ -43,6 +46,14 @@ void DXProceduralProject::BuildShaderTables() } // TODO-2.7: Hitgroup shaders for the AABBs. We have 2 for each AABB. + for (UINT i = 0; i < RayType::Count; i++) + { + for (UINT count = 0; count < IntersectionShaderType::Count; count++) + { + hitGroupShaderIDs_AABBGeometry[count][i] = stateObjectProperties->GetShaderIdentifier(c_hitGroupNames_AABBGeometry[count][i]); + shaderIdToStringMap[hitGroupShaderIDs_AABBGeometry[count][i]] = c_hitGroupNames_AABBGeometry[count][i]; + } + } }; @@ -95,7 +106,23 @@ 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! { + //ummm wtf + UINT numShaderRecords = 2; + UINT shaderRecordSize = shaderIDSize; // No root arguments + ShaderTable missed_table(device, numShaderRecords, shaderRecordSize, L"Missedtable"); + + // push back our missed shaders + missed_table.push_back(ShaderRecord(missShaderIDs[0], shaderRecordSize, nullptr, 0)); + missed_table.push_back(ShaderRecord(missShaderIDs[1], shaderRecordSize, nullptr, 0)); + + // + missed_table.DebugPrint(shaderIdToStringMap); + + // + m_missShaderTableStrideInBytes = missed_table.GetShaderRecordSize(); + // + m_missShaderTable = missed_table.GetResource(); } // 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/DXR-Structs.h b/src/D3D12RaytracingProceduralGeometry/DXR-Structs.h index ab6e8ee..c5d5505 100644 --- a/src/D3D12RaytracingProceduralGeometry/DXR-Structs.h +++ b/src/D3D12RaytracingProceduralGeometry/DXR-Structs.h @@ -13,7 +13,7 @@ struct AccelerationStructureBuffers UINT64 ResultDataMaxSizeInBytes; // this can be acquired from the prebuild info }; -// LOOKAT-1.6: Think of this as data that needs to be used by a shader. e.g: vertex data (position, color, normals) +// LOOKAT-1.6: Think of this as data that needs to be used by a shader. e.g: \ data (position, color, normals) struct D3DBuffer { ComPtr resource; 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..376f969 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,17 @@ 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); + // reflect + float3 reflected_ray = normalize(reflect(incidentLightRay,normal)); + // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions + // helpful aF + float3 reverse_ray_direction = normalize(-WorldRayDirection()); + + float4 coefficient = saturate(dot(reflected_ray, reverse_ray_direction)); + + coefficient = pow(coefficient, specularPower); + + return coefficient; } // TODO-3.6: Phong lighting model = ambient + diffuse + specular components. @@ -68,6 +78,30 @@ 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 mf_world_position = HitWorldPosition(); + float3 lightPosition = g_sceneCB.lightPosition.xyz; + float4 lightDiffuseColor = g_sceneCB.lightDiffuseColor; + float4 specularColor = float4(0, 0, 0, 0); + float4 lightSpecularColor = float4(1, 1, 1, 1); // a fully LIT light + + // get diffuse + float3 incidentLightRay = normalize(mf_world_position - lightPosition); + float diffuse_coefficient = CalculateDiffuseCoefficient(incidentLightRay, normal); + + //if its a shadow use the radiance number else 1 for fully LIT ... unlike my friday night because of this assignmnet + float visibility = isInShadow ? InShadowRadiance : 1.0f; + // now we can finlly calcuate + float4 diffuseColor = visibility * diffuseCoef * diffuse_coefficient * lightDiffuseColor * albedo; + + // if not in a shadow add specularity + if (!isInShadow) + { + float ks = CalculateSpecularCoefficient(incidentLightRay, normal, specularPower); + specularColor = specularCoef * ks * lightSpecularColor; + } + + // Ambient component // Fake AO: Darken faces with normal facing downwards/away from the sky a little bit float4 ambientColor = g_sceneCB.lightAmbientColor; @@ -76,7 +110,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 +specularColor + diffuseColor; } //*************************************************************************** @@ -135,9 +169,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; + } + + // below is a direct copy paste from above equation + 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; + + // assume miss unless otherwise + ShadowRayPayload rayPayload = { false }; + + // + 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, rayPayload); + + return rayPayload.hit; } + //*************************************************************************** //********************------ Ray gen shader -------************************** //*************************************************************************** @@ -149,9 +210,18 @@ bool TraceShadowRayAndReportIfHit(in Ray ray, in UINT currentRayRecursionDepth) [shader("raygeneration")] void MyRaygenShader() { - - // Write the color to the render target - g_renderTarget[DispatchRaysIndex().xy] = float4(0.0f, 0.0f, 0.0f, 0.0f); + // Seed the PRNG using the thread ID + UINT rng_state = DispatchRaysIndex().x; + + float3 cam = g_sceneCB.cameraPosition.xyz; + // Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid. + Ray ray = GenerateCameraRay(DispatchRaysIndex().xy, cam, g_sceneCB.projectionToWorld); + + // set depth to 0 other stuff handles the rest. Or it is a ray tracing fundamental concept I was never taught + float4 color = TraceRadianceRay(ray, 0); + + // Write the raytraced color to the output texture. + g_renderTarget[DispatchRaysIndex().xy] = color; } //*************************************************************************** @@ -209,6 +279,11 @@ 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_far = RayTCurrent(); + //Returns x + s(y - x). + //color = lerp(color, BackgroundColor, t_far); + //https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp + color = lerp(color, BackgroundColor, exp(-t_far) ); rayPayload.color = color; } @@ -227,6 +302,45 @@ void MyClosestHitShader_Triangle(inout RayPayload rayPayload, in BuiltInTriangle [shader("closesthit")] void MyClosestHitShader_AABB(inout RayPayload rayPayload, in ProceduralPrimitiveAttributes attr) { + // verbatim from above without the whole notion of triangles + + // This is the intersection point on the AABB + 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); + + // TODO: Apply a visibility falloff. + // 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_far = RayTCurrent(); + //Returns x + s(y - x). + //color = lerp(color, BackgroundColor, t_far); + //https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp + color = lerp(color, BackgroundColor, exp(-t_far)); + + rayPayload.color = color; } @@ -240,14 +354,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 +413,24 @@ void MyIntersectionShader_AnalyticPrimitive() [shader("intersection")] void MyIntersectionShader_VolumetricPrimitive() { + Ray localRay = GetRayInAABBPrimitiveLocalSpace(); + VolumetricPrimitive::Enum primitiveType = (VolumetricPrimitive::Enum) l_aabbCB.primitiveType; + float time = g_sceneCB.elapsedTime; + // 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,time)) + { + PrimitiveInstancePerFrameBuffer aabbAttribute = g_AABBPrimitiveAttributes[l_aabbCB.instanceIndex]; + // Make sure the normals are stored in BLAS space and not the local space + attr.normal = mul(attr.normal, (float3x3) aabbAttribute.localSpaceToBottomLevelAS); + attr.normal = normalize(mul((float3x3) ObjectToWorld3x4(), attr.normal)); + + // thit is invariant to the space transformation + ReportHit(thit, /*hitKind*/ 0, attr); + } } #endif // RAYTRACING_HLSL \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h b/src/D3D12RaytracingProceduralGeometry/RaytracingHlslCompat.h index 6e10f0d..42f064b 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 3 // ~ 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..fb81134 100644 --- a/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/RaytracingShaderHelper.hlsli @@ -68,7 +68,15 @@ 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); + // 1 + float interpolant = elapsedTime / cycleDuration; // gives something like 1/5 + + // 2 lol what ... I just double or nothing that shit + // https://www.freecodecamp.org/news/understanding-linear-interpolation-in-ui-animations-74701eb9957c/ + interpolant = interpolant <= .5 ? interpolant * 2 : 1 - 2 *(interpolant -.5f); + + // smooth my stuff please ... + return smoothstep(0, 1, interpolant); } // Load three 2-byte indices from a ByteAddressBuffer. @@ -119,6 +127,22 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]); } +uint rand_lcg(in UINT rng_state) +{ + // LCG values from Numerical Recipes + rng_state = 1664525 * rng_state + 1013904223; + return rng_state; +} + +uint rand_xorshift(in UINT rng_state) +{ + // Xorshift algorithm from George Marsaglia's paper + rng_state ^= (rng_state << 13); + rng_state ^= (rng_state >> 17); + rng_state ^= (rng_state << 5); + return rng_state; +} + // TODO-3.1: Generate a ray in world space for a camera pixel corresponding to a dispatch index (analogous to a thread index in CUDA). // Check out https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-12-raytracing-hlsl-system-value-intrinsics to see interesting // intrinsic HLSL raytracing functions you may use. @@ -128,10 +152,34 @@ float3 HitAttribute(float3 vertexAttribute[3], float2 barycentrics) // Once you have the normalized-device coordinates, use the projectionToWorld matrix to find a 3D location for this pixel. The depth will be wrong but // as long as the direction of the ray is correct then the depth does not matter. inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 projectionToWorld) -{ +{ + float2 xy_coordinate = index; + // http://webglfactory.blogspot.com/2011/05/how-to-convert-world-to-screen.html + // algorithm taken from above + // we now have our 2d point + float x = 2.0 * xy_coordinate.x / DispatchRaysDimensions().x - 1; + float y = -2.0 * xy_coordinate.y / DispatchRaysDimensions().y + 1; + float2 screenPos = { x,y }; + + // do what I said in hte conceptual question which is trafnsorm us + // screen pos = x,y 0 is z since we have no z and w = 1 + float4 world_star = mul(float4(screenPos, 0, 1), projectionToWorld); + + // do what ziad told me to do in OH divide everything by w to get the correct perspective view + world_star.xyz /= world_star.w; + Ray ray; - ray.origin = float3(0.0f, 0.0f, 0.0f); - ray.direction = normalize(float3(0.0f, 0.0f, 0.0f)); + + // in ray tracing our origin is always at the camera + ray.origin = cameraPosition; + + UINT rand = index.x; + + // Generate a random float in [0, 1)... + float f0 = float(rand_xorshift(rand)) * (1.0 / 4294967296.0); + + // normalize our coordinates because thats something CG ppl do + ray.direction = normalize(world_star.xyz - ray.origin); return ray; } @@ -141,7 +189,10 @@ inline Ray GenerateCameraRay(uint2 index, in float3 cameraPosition, in float4x4 // f0 is usually the albedo of the material assuming the outside environment is air. float3 FresnelReflectanceSchlick(in float3 I, in float3 N, in float3 f0) { - return f0; + float3 albedo = f0; + // saturate clamps from 0 to 1 + float cos_theta = saturate(dot(-I, N)); + return albedo + (1 - albedo)*pow(1 - cos_theta, 5); } #endif // RAYTRACINGSHADERHELPER_H \ No newline at end of file diff --git a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli index 31a9444..2f78c81 100644 --- a/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli +++ b/src/D3D12RaytracingProceduralGeometry/VolumetricPrimitives.hlsli @@ -22,7 +22,24 @@ struct Metaball // of the distance from the center to the radius. float CalculateMetaballPotential(in float3 position, in Metaball blob) { - return 0.0f; + // double check + float close_ness = length(position - blob.center); + + if (close_ness == 0) + return 1.0f; + if (close_ness >= blob.radius) + return 0.0f; + + // some ratio + close_ness = blob.radius - close_ness; + close_ness = close_ness / blob.radius; + + float powa = pow(close_ness, 5); + float powb = pow(close_ness, 4); + float powc = pow(close_ness, 3); + + // this needs toturn into ratio + return 6*powa - 15*powb + 10*powc; } // LOOKAT-1.9.4: Calculates field potential from all active metaballs. This is just the sum of all potentials. @@ -83,6 +100,21 @@ void TestMetaballsIntersection(in Ray ray, out float tmin, out float tmax, inout { tmin = INFINITY; tmax = -INFINITY; + + float temp_max; + float temp_min; + + for (int i = 0; i < N_METABALLS; i++) + { + Metaball blob = blobs[i]; + //bool RaySolidSphereIntersectionTest(in Ray ray, out float thit, out float tmax, in float3 center = float3(0, 0, 0), in float radius = 1) + if (RaySolidSphereIntersectionTest(ray, temp_min, temp_max, blob.center, blob.radius)) + { + // keep track of our min and max intersection locations important for marching + tmin = min(temp_min, tmin); + tmax = max(temp_max, tmax); + } + } } // TODO-3.4.2: Test if a ray with RayFlags and segment intersects metaball field. @@ -100,9 +132,54 @@ 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) { + Metaball blobs[N_METABALLS]; + + // because this class is satan + const float artistic_touch =.35f; + const float cycleDuration = 6.6666f; + + // default to 0 unless valid hit occurs thit = 0.0f; attr.normal = float3(0.0f, 0.0f, 0.0f); - return false; + + float temp_max; + float temp_min; + + // void InitializeAnimatedMetaballs(out Metaball blobs[N_METABALLS], in float elapsedTime, in float cycleDuration) + // step 1 + InitializeAnimatedMetaballs(blobs, elapsedTime, cycleDuration); + + // step 2 find min and max + TestMetaballsIntersection(ray, temp_min, temp_max,blobs); + + // slightly adjust our position every iteration based off of our findings of min and max scale to 128 so it ends on our temp_max boundary + float step = (temp_max - temp_min) / 128; + + // ray march this bitch + for (int i = 0; i < 128; i++) + { + //step 3a + float3 position = ray.origin + temp_min * ray.direction; + float potential = CalculateMetaballsPotential(position, blobs); + + // 3b + // potentially render this bitch + if (potential > artistic_touch) + { + //float3 CalculateMetaballsNormal(in float3 position, in Metaball blobs[N_METABALLS]) + float3 normal = CalculateMetaballsNormal(position, blobs); + if (is_a_valid_hit(ray, temp_min, normal)) + { + // set variables here or else bugs occur ): + thit = temp_min; + attr.normal = normal; + return true; + } + } + temp_min += step; + } + // no hit or some shit + return false; } #endif // VOLUMETRICPRIMITIVESLIBRARY_H \ No newline at end of file