Getting started with NVIDIA OptiX
Photorealism is a technique aiming at obtaining artificial images indistinguishable from natural ones.
Photorealistic images require the full simulation of light propagation, reflection and refraction from a light source, passing through the surfaces involved in the illumination until the light reaches the human eye. All the electromagnetic properties of a surface, like colour, reflectivity and opacity must be properly taken into account in the optical model. Ray tracing is the ultimate step towards photorealism.
From a practical point of view, to assign a colour to a pixel, inverse ray tracing is operated. The inverse path of light is traced backwards from a camera playing the role of the human eye, passing through the relevant pixel until it eventually reaches the light source. Inverse ray tracing relieves computation since the rays emitted by the source could possibly not interact wth the observer.
Ray tracing is capable to dramatically improve the image quality, but requires much computational power to reach real-time execution. For this reason, currently, ray tracing is not yet a real-time process and only since recently it has started peeping in video games industry.
Boundary Volume Hierarchy
To operate ray tracing, the scene is organized in primitives, namely, the relevant surfaces are approximated by polygons, triangles typically. Tracing the rays requires to test if an inverse ray leaving the camera and passing through the image pixel intersects the scene primitives. If we take into account that the number of rays that must be usually launched can be of different millions and that the primitives approximating the scene can be different millions as well, performing the intersection tests for each ray against each primitive can be computationally prohibitive.
To reduce the number of intersection tests, the primitives can be organized in a Boundary Volume Hierarchy (BVH). By a BVH, the primitives are partitioned into a hierarchy of disjoint sets.
In the figure above, a small collection of primitives is illustrated along with their corresponding bounding boxes (dashed lines). The primitives are aggregated based on proximity. In the above example, the sphere and the equilateral triangle are close and they are incuded in another bounding box (solid line). The bounding boxes of the sphere+equilateral triangle and of the isosceles triangle are then included in an external bounding box that encompasses the entire scene (again shown in solid line).
The bounding boxes are then organized in a bounding volume hierarchy. The root node holds the bounding box of the entire scene. The root node has then two children, one storing a bounding box that encompasses the sphere+ equilateral triangle and the other storing the skinny triangle. The bounding box storing sphere and equilateral triangle has in turn two children containing one primitive each.
Primitives are finally stored in the leaves. Each node stores a bounding box of the primitives in the nodes beneath it. By this construction, as a ray traverses through the tree, any time it doesn’t intersect a node’s bounds, the subtree beneath that node can be skipped and intersection tests can be saved.
OptiX is a general purpose ray-tracing engine developed by NVIDIA and released in 2009 which is intended for ray tracing applications that use the NVIDIA CUDA technology.
Typical ray-tracing applications rely on operations like ray generation, material shading, object intersection and scene traversal. Instead of implementing all these components from scratch every time, one can use the OptiX APIs instead. In this way, development time is reduced and the application becomes much more flexible as it can be compiled and optimized by OptiX for different architectures transparently to the User. Due to the engine overhead, however, some performance may be in principle loss if compared to a highly optimized specific implementation.
In order to speed up ray-tracing, OptiX exploits BVHs as acceleration structures. BVHs are generated and updated automatically from the polygon meshes defining the objects in the scene.
The OptiX pipeline
OptiX works by combining its hardcoded kernel code with CUDA kernels programmed by the User.
The setup and launch of the ray-tracing engine is handled with a context object which encapsulates all the needed OptiX resources such as User-defined kernels and geometries.
The way the rays interact with the scene objects when they intersect the primitives is specified by User-defined kernels such as:
Ray generation kernel
All the computations (e.g., tracing the rays) follow the ray generation kernel.
The closest-hit kernel is executed when the ray intersects the closest object.
The any-hit kernel is executed once for every object the ray intersects.
The miss kernel is executed if a ray does not intersect with any object.
It is called when certain types of errors occur.
The intersection kernel is used to determine whether a ray intersects the geometry.