Portfolio


  • Home
  • DirectX Raytracing
  • Advanced Graphics
  • Roboflux
  • Boat Simulation

DirectX Raytracing



The project



This project was created as a vertical slice for my final year project which is looking at real-time interactive raytraced global illumination. As it is a vertical slice it is still very rudimentary but will serve as the foundations of the framework that I will continue to build on top of. The framework is written using the DXR API (DirectX Raytracing) and shaders are compiled at runtime using the DX Compiler library.



Physically Based Rendering (PBR)



I decided to use PBR for this project as it provides very intuitive parameters for artists to control. This allows easier authoring of materials meaning they can be created quicker while still maintaining the appearances desired. When implementing PBR I used the Cook-Torrance specular BRDF and Lambertian diffuse. For the Frensel term I used Schlick's approximation however I will probably switch to the Lazányi–Schlick approximation as it reduces errors when it comes to rendering conductors especially at glancing angles (Source). For the normal distribution function I used the Trowbridge-Reitz GGX and for the geometry function I used the Schlick-GGX approximation.



GLTF Model Loading



I implemented GLTF model loading using TinyGLTF. I did this as there are a variety of GLTF models available online for free such as the ones present in the Khronos Group Github repository. Another reason I implemented GLTF model loading is that it will allow proper testing of my global illumination implementation as a variety of meshes can be easily switched between. The implementation I created works by loading all the vertex and index information into one buffer. A node tree is then created following the format in the GLTF file. Each node can have primitives associated with it which in turn have their own attributes such as albedo, metallic, roughness and occlusion textures. These are actually just indexes of the textures in the descriptor heap allowing the reuse of textures across models. On top of these attributes each node also stores geometric information such as the vertex and index buffer offsets; number of vertices; number of indices etc. In the future I will also store a linear representation of the nodes to allow easier traversing of meshes.



Profiling and Debugging



While working on the vertical slice I had to debug various issues. To help with this I used an application called PIX. This involved loading a DLL at runtime to ensure the application could carry out GPU captures. I then created separate build configurations for the project called PixDebug and PixRelease. These configurations both copied the Debug and Release configurations respectively. I then added a PIX pre-processor define and guarded the code that loads the DLL meaning it is only loaded on the PIX configurations. Setting it up like this means the application can be profiled both with and without debug information allowing the collection of more accurate runtime data.


On top of providing debug information PIX can be used to profile the GPU. This will be vital when optimising the application as it will allow me to identify the bottlenecks and speed them up appropriately. This is commonly done by adding calls to the function PIXBeginEvent and PIXEndEvent which allows you to organise all the functions called into smaller chunks making the data collected more readable.



Future Work



As I am currently still working on this project there are a variety of features left to implement. In terms of global illumination specific features, I have planned to use an irradiance probe-based method to cache the radiance coming from a direction at any point inside an area. This is done by uniformly placing irradiance probes in a grid. The information gathered by the probes is written to a texture using octahedral mapping. Octahedral mapping will be used as it has less warping than other methods such as cube maps. The cached values can then be accessed with a big O notation of O(1) greatly speeding up global illumination calculations. Monte Carlo integration will then be used to collect the irradiance over a hemisphere at each point being rendered allowing real-time performance.


One change not specific to global illumination that I will be implementing is I will be switching to a bindless architecture. This architecture is ideal for raytracing as all resources must be available at all times due to the application not knowing which object will be intersected by the ray next. An advantage of a bindless architecture is that it greatly reduces overhead as the resources don't need to be bound every frame. A disadvantage is that it can make debugging shaders harder as there is an extra layer of abstraction between the resources and the geometry. This can be negated by using PIX to ensure the application is accessing the correct descriptors.