WilliamKHo / Pollux-Renderer

2 stars 3 forks source link

title_pic

Tested on:

Built Platform Platform2 Languages Languages Developer Developer


macOS iOS

Pollux is a Monte Carlo Path Tracer built completely in Metal. It can run on both macOS and iOS.

Build Instructions

[Select the appropriate scheme](/Debug\ Views/scheme.png) and run by clicking on the play button.

If you are running on iOS you must run on an actual device as Metal is not supported on the simulator.

Scene Setup Instructions

Scenes are described in a json file format. Where each file must contain:

A camera dictionary with entries for:

A materials array of dictionaries with each material having entries for:

Example scenes can be found in the Scenes/ folder.

Code Structure

In order to make the code as portable as possible to different platforms, the UI is as simple as it gets: One ViewController with its view being cast as an MTKView to potentially take advantage of Direct-To-Display capabilities in Tier 2 devices.

As soon as the view controller loads, a PolluxRenderer is instantiated that handles everything. PolluxRenderer conforms to the MTKViewDelegate protocol and renders everything. In the initializer, it sets up the entire compute pipeline, and then later at each step, it calls pathtrace() which then sets the appropriate pipeline stages:

Data Structures / Frameworks Created:

PlatformXXXXXXXX: In order for this project to work with both macOS and iOS, I had to consolidate the different types of View elements for different platforms. In order to do that, I use a process Apple calls "Shimming", which basically defines types based on what OS that this is being built on. This is done using preprocessor flags. A very helpful description of this can be found in this WWDC talk from 2014. Because there are different names for different data types between macOS and iOS, I just define the appropriate data type using a preprocessor #if and a typealias (which is exactly like typedef in c) to make sure that the right type is being used. An example of this is how in macOS view controlles are called NSViewController, but in iOS they are called UIViewController. I just set the appropriate name to a unified PlatformViewController that I can use throughout the code without having to worry about platform compatability. The full list of defined types is available in PlatformTypes.swift.

SharedBuffer<T>: This project required the development of lots of support data structures to handle data management between the CPU and GPU. Luckily, I was able to encapsulate most large buffers in a SharedBuffer<T> class that represents a buffer that is accessible to both the GPU and CPU, with its storage mode being .storageModeShared. This allowed me to very cleanly use a buffer across the CPU and GPU with as much code as I would had it been an array on the CPU. The code is also extendable to all data types supported on the GPU, so feel free to use the code for any other Metal project you may need. TODO: Port this file to a framework/separate repo.

simd_la: All of my linear algebra calculation is done using simd types, which runs operations using simd processing to optimize linear algebra calculations. There is already a very comprehensive linear algebra toolkit using these types, but I found that there wasn't anything that did 3D space manipulations using them. So I created a mini-linear algebra library for translate, rotate, and scale operations for 3D vectors. This is used when I create the transformation matrices during scene parsing.

Loki: By far my favorite name for anything of made, this is basically a pseudo-random number generator built completely in Metal. TODO: bundle this up into a framework/separate repo. This took around 2 days to get to work, because random number generators are very important with Monte Carlo based calculations because any periodic behavior is instantly visible to the viewer. I realized very quickly that variance was not as important as the rng's period when I first used this code as a framework and found very noticeable artifacts in the image. The final implementation is based on this research paper from 2012 that uses a combination of which a hybrid approach combining LFSR with a Linear Congruential Generator (LCG) in order to produce random numbers with periods of well over 2^121. This works very well in my code. I also extended the algorithm to combine up to three seeds, which in my case are the threadid, iteration, and depth of each ray.

Features

[ ] Scene file loading [ ] Naive Shading [ ] Multiple Importance Sampling [ ] Stream Compaction [ ] Acceleration Structure / GLTF Loading? [ ] AR Kit??

Potential Improvements

Sample Renders

Illustrative scenes for reflection, refraction, subsurface scatter, and environment maps:

Debug Views

Ray Direction Debug View:

Analysis

Bloopers

Broken Stream Compaction

streamcompactiondegub02

Broken Partition

References