{"id":324,"date":"2024-07-13T15:27:39","date_gmt":"2024-07-13T13:27:39","guid":{"rendered":"https:\/\/cammonte.com\/?page_id=324"},"modified":"2024-07-15T15:05:10","modified_gmt":"2024-07-15T13:05:10","slug":"nori-an-educational-ray-tracer","status":"publish","type":"page","link":"https:\/\/cammonte.com\/index.php\/nori-an-educational-ray-tracer\/","title":{"rendered":"Nori: An educational ray tracer"},"content":{"rendered":"\n<p>As part of the <a href=\"https:\/\/rgl.epfl.ch\/courses\/ACG23\">Advanced Computer Graphics class<\/a>, taught by Prof. Dr. Wenzel Jakob at <a href=\"https:\/\/www.epfl.ch\/about\/\">EPFL<\/a>, I worked on Nori: a minimalist ray tracer written in C++. The objective of the class was to <strong>lay the groundwork for an effective renderer by implementing its core features<\/strong>. We built on top of the Nori base code, which includes many pre-built components that would be laborious to create from scratch, such as a simple GUI for real-time image rendering, an XML-based scene file loader, and basic geometric primitives classes. Below are the various topics covered in the course and the corresponding features I implemented in the renderer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">TL;DR<\/h3>\n\n\n\n<p>I implemented the core features of a renderer written in C++, including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ray tracing acceleration data structures<\/li>\n\n\n\n<li>Basic rendering algorithms: whitted-style, path tracer with multiple importance sampling<\/li>\n\n\n\n<li>Homogeneous participating media<\/li>\n\n\n\n<li>Light and materials modelisation<\/li>\n\n\n\n<li>Textures and normal mapping<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Ray tracing acceleration data structures<\/h1>\n\n\n\n<p>I implemented an <strong>octree<\/strong>, a hierarchical data structure designed to expedite ray intersection computations with large numbers of triangles. This step involved constructing the octree, optimising its data organisation for <strong>efficient memory usage<\/strong>, and sing it for ray tracing by performing an <strong>ordered traversal <\/strong>of the tree&#8217;s children from nearest to farthest.<\/p>\n\n\n\n<p>I also focused on <strong>performance optimisation<\/strong>, employing the <strong>parallelisation<\/strong> abilities to enhance the efficiency and speed of the implementation.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Monte Carlo sampling<\/h1>\n\n\n\n<p>I implemented <strong>Monte Carlo sampling techniques<\/strong> to transform an initially uniform distribution into various target distributions. This involved computing and implementing both the distribution functions and the corresponding sample warping schemes.<\/p>\n\n\n\n<p>This gave me a first hands-on experience in <strong>probabilistic modeling and numerical methods<\/strong>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Basic rendering algorithms<\/h1>\n\n\n\n<p>I implemented several <strong>basic rendering algorithms<\/strong> including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ambient occlusion<\/strong>, a rendering technique that assumes that a surface receives uniform illumination from all directions and that visibility is the only effect that matters.<\/li>\n\n\n\n<li><strong>Whitted-style ray-tracer<\/strong>, which integrates the incident radiance by sampling points on a set of light sources and is able to account for specular reflections and refractions.<\/li>\n\n\n\n<li><strong>Path tracer with Multiple Importance Sampling<\/strong>, which enhances accuracy and efficiency by weighting the contributions from different sampling techniques based on their effectiveness in capturing various lighting scenarios.<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis.jpg\" alt=\"\" class=\"wp-image-445\" style=\"width:auto;height:375px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis.jpg 800w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis-300x225.jpg 300w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis-768x576.jpg 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><strong>Rendered using multiple importance sampling<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"512\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis_veach.jpg\" alt=\"\" class=\"wp-image-446\" style=\"width:auto;height:375px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis_veach.jpg 768w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis_veach-300x200.jpg 300w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><figcaption class=\"wp-element-caption\"><strong>Rendered using multiple importance sampling<\/strong><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h1 class=\"wp-block-heading\">Homogeneous participating media<\/h1>\n\n\n\n<p>I added to the renderer support for handling homogeneous participating media (media with constant density and scattering properties), mainly in order to render beams of light.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"450\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/homogeneous_medium_windows.png\" alt=\"\" class=\"wp-image-453\" style=\"width:auto;height:375px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/homogeneous_medium_windows.png 600w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/homogeneous_medium_windows-300x225.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Light and materials modelisation<\/h1>\n\n\n\n<p>Throughout the project, I implemented various models of light sources and materials to be used to render interesting things and validate my other implementations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Light sources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Point lights<\/li>\n\n\n\n<li>Area lights<\/li>\n\n\n\n<li>Specialised emitters (rectangular, spherical, and disk lights)<\/li>\n\n\n\n<li>Image-based lighting<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">BRDFs<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dielectric<\/li>\n\n\n\n<li>Microfacet<\/li>\n\n\n\n<li>Thin dielectric<\/li>\n\n\n\n<li>Mirror<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/thin-dielectric-cbox.png\" alt=\"\" class=\"wp-image-450\" style=\"width:auto;height:375px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/thin-dielectric-cbox.png 800w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/thin-dielectric-cbox-300x225.png 300w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/thin-dielectric-cbox-768x576.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><strong>Thin dielectric and dielectric spheres<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"http:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis.jpg\" alt=\"\" class=\"wp-image-445\" style=\"width:auto;height:375px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis.jpg 800w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis-300x225.jpg 300w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis-768x576.jpg 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\"><strong>Microfacet bowl<\/strong><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h1 class=\"wp-block-heading\">Textures and normal mapping<\/h1>\n\n\n\n<p>I ultimately added to the renderer a few &#8220;low-effort&#8221; features to be able to model more realistic looking scenes, notably the Ability to add <strong>textures<\/strong> <strong>and normal maps<\/strong> to the different meshes in the scene with different simple types of UV mapping.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"857\" height=\"858\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/spherical.png\" alt=\"\" class=\"wp-image-441\" style=\"width:255px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/spherical.png 857w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/spherical-300x300.png 300w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/spherical-150x150.png 150w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/spherical-768x769.png 768w\" sizes=\"auto, (max-width: 857px) 100vw, 857px\" \/><figcaption class=\"wp-element-caption\"><strong>Texture mapped on a sphere<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"870\" height=\"870\" src=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/sphere_6normal.png\" alt=\"\" class=\"wp-image-442\" style=\"width:255px\" srcset=\"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/sphere_6normal.png 870w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/sphere_6normal-300x300.png 300w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/sphere_6normal-150x150.png 150w, https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/sphere_6normal-768x768.png 768w\" sizes=\"auto, (max-width: 870px) 100vw, 870px\" \/><figcaption class=\"wp-element-caption\"><strong>Normal map and texture mapped on a sphere<\/strong><\/figcaption><\/figure>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>As part of the Advanced Computer Graphics class, taught by Prof. Dr. Wenzel Jakob at EPFL, I worked on Nori: a minimalist ray tracer written in C++. The objective of the class was to lay the groundwork for an effective renderer by implementing its core features. We built on top of the Nori base code, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":445,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"ub_ctt_via":"","site-container-style":"default","site-container-layout":"default","site-sidebar-layout":"default","disable-article-header":"default","disable-site-header":"default","disable-site-footer":"default","disable-content-area-spacing":"default","footnotes":""},"class_list":["post-324","page","type-page","status-publish","has-post-thumbnail","hentry"],"featured_image_src":"https:\/\/cammonte.com\/wp-content\/uploads\/2024\/07\/mis.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/pages\/324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/comments?post=324"}],"version-history":[{"count":29,"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/pages\/324\/revisions"}],"predecessor-version":[{"id":539,"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/pages\/324\/revisions\/539"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/media\/445"}],"wp:attachment":[{"href":"https:\/\/cammonte.com\/index.php\/wp-json\/wp\/v2\/media?parent=324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}