.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/13_shaders/viz_billboard_sdf_spheres.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_13_shaders_viz_billboard_sdf_spheres.py: =============================================================================== SDF Impostors on Billboards =============================================================================== Traditional rendering engines discretize surfaces using triangles or quadrilateral polygons. The visual quality of these elements depends on the number of polygons used to build the 3D mesh, i.e., a smoother surface will require more polygons. However, increasing the amount of rendered polygons comes at the cost of performance as it decreases the number of frames per second (FPS), which might compromise the real-time interactivity of a visualization. Billboarding is a technique that changes an object's orientation to always face a specific direction, in most cases, the camera. This technique became popular in games and applications with a high polygonal quota requirement. Signed Distance Functions (SDFs) are mathematical functions that take as input a point in a metric space and return the distance from that point to the boundary of the function. Depending on whether the point is contained within this boundary or outside it, the function will return negative or positive values [Hart1996]_. For visualization purposes, the task is to display only the points within the boundary or, in other words, those whose distance to the border is either negative or positive, depending on the definition of the SDF. This tutorial exemplifies why FURY's billboard actor is a suitable rendering option when thinking about performance and how it can be used to create impostors using SDFs. Let's start by importing the necessary modules: .. GENERATED FROM PYTHON SOURCE LINES 32-41 .. code-block:: Python import os import numpy as np from fury import actor, window from fury.shaders import compose_shader, import_fury_shader from fury.utils import represent_actor_as_wireframe .. GENERATED FROM PYTHON SOURCE LINES 42-43 Now set up a new scene to place our actors in. .. GENERATED FROM PYTHON SOURCE LINES 43-45 .. code-block:: Python scene = window.Scene() .. GENERATED FROM PYTHON SOURCE LINES 46-48 This tutorial is divided into two parts. First, we will render spheres in the traditional way and then render them using SDFs on billboards. .. GENERATED FROM PYTHON SOURCE LINES 50-54 Traditional sphere rendering ============================ FURY provides an easy way to create sphere glyphs from numpy arrays as follows: .. GENERATED FROM PYTHON SOURCE LINES 54-64 .. code-block:: Python centers = np.array([ [0, 0, 0], [-6, -6, -6], [8, 8, 8], [8.5, 9.5, 9.5], [10, -10, 10], [-13, 13, -13], [-17, -17, 17]]) colors = np.array([ [1, 1, 0], [1, 0, 1], [0, 0, 1], [1, 1, 1], [1, 0, 0], [0, 1, 0], [0, 1, 1]]) scales = np.array([6, 1.2, 1, .2, .7, 3, 2]) spheres_actor = actor.sphere( centers, colors, radii=scales, phi=8, theta=8, use_primitive=False) .. GENERATED FROM PYTHON SOURCE LINES 65-68 To interactively visualize the recently created actors, we only need to add them to the previously created `scene` and set the following variable to **True**, otherwise, we will screenshot the scene. .. GENERATED FROM PYTHON SOURCE LINES 68-77 .. code-block:: Python scene.add(spheres_actor) interactive = False if interactive: window.show(scene) else: window.record(scene, size=(600, 600), out_path='viz_regular_spheres.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_001.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 78-82 Now, let's explore our scene to understand what we have created. Traditional FURY spheres are designed using a set of interconnected triangles. To visualize them, we want to transform our representation from *Surface* to *Wireframe* using the following command. .. GENERATED FROM PYTHON SOURCE LINES 82-89 .. code-block:: Python represent_actor_as_wireframe(spheres_actor) if interactive: window.show(scene) else: window.record(scene, size=(600, 600), out_path='viz_low_res_wireframe.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_002.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 90-91 Let's clean the scene and play with the parameters `phi` and `theta`. .. GENERATED FROM PYTHON SOURCE LINES 91-102 .. code-block:: Python scene.clear() spheres_actor = actor.sphere( centers, colors, radii=scales, phi=16, theta=16, use_primitive=False) represent_actor_as_wireframe(spheres_actor) scene.add(spheres_actor) if interactive: window.show(scene) else: window.record(scene, size=(600, 600), out_path='viz_hi_res_wireframe.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_003.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 103-108 As you might have noticed, these parameters control the resolution of the spheres. Evidently, these parameters directly impact the quality of the visualization, but increasing such resolution comes at the cost of performance, i.e., more computing power will be needed and drawn to interact with these actors. .. GENERATED FROM PYTHON SOURCE LINES 110-113 Luckily for us, a technique delivers high-resolution glyphs at a much lower cost. This technique is known as Signed Distance Functions (SDFs), and they work as follows: .. GENERATED FROM PYTHON SOURCE LINES 115-119 SDF sphere rendering ==================== It is possible to render SDFs in FURY by using the following configuration, but first, let's clear the scene. .. GENERATED FROM PYTHON SOURCE LINES 119-121 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 122-124 The billboard actor is suited and continuously improved to render SDFs. To create and visualize it, we can use the following instructions: .. GENERATED FROM PYTHON SOURCE LINES 124-134 .. code-block:: Python billboards_actor = actor.billboard(centers, colors=colors, scales=scales) represent_actor_as_wireframe(billboards_actor) scene.add(billboards_actor) if interactive: window.show(scene) else: window.record( scene, size=(600, 600), out_path='viz_billboards_wireframe.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_004.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 135-139 If you interacted with this actor, you might have noticed how it always aligned itself to the camera or, in other words, your FURY window. Now that we know how billboards work, we can start working on our Signed Distance Spheres. Let's clear our scene first. .. GENERATED FROM PYTHON SOURCE LINES 139-141 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 142-144 FURY already includes a shader function with the definition of a Signed Distance Sphere. So we can load it and use it like this: .. GENERATED FROM PYTHON SOURCE LINES 144-146 .. code-block:: Python sd_sphere = import_fury_shader(os.path.join('sdf', 'sd_sphere.frag')) .. GENERATED FROM PYTHON SOURCE LINES 147-150 Additionally, we need to define the radii of our spheres. Since we prefer these to be determined by the billboards' size, we will use the maximum radius distance allowed by our billboards. .. GENERATED FROM PYTHON SOURCE LINES 150-152 .. code-block:: Python sphere_radius = 'const float RADIUS = 1.;' .. GENERATED FROM PYTHON SOURCE LINES 153-155 Let's calculate the distance to the sphere by combining the previously defined variables. .. GENERATED FROM PYTHON SOURCE LINES 155-157 .. code-block:: Python sphere_dist = 'float dist = sdSphere(point, RADIUS);' .. GENERATED FROM PYTHON SOURCE LINES 158-159 Now, evaluate the signed distance function. .. GENERATED FROM PYTHON SOURCE LINES 159-167 .. code-block:: Python sdf_eval = \ """ if (dist < 0) fragOutput0 = vec4(color, opacity); else discard; """ .. GENERATED FROM PYTHON SOURCE LINES 168-170 Putting all of our declarations (constants and function) and implementations (distance calculation and evaluation) together. .. GENERATED FROM PYTHON SOURCE LINES 170-173 .. code-block:: Python fs_dec = compose_shader([sphere_radius, sd_sphere]) fs_impl = compose_shader([sphere_dist, sdf_eval]) .. GENERATED FROM PYTHON SOURCE LINES 174-175 We are ready to create and visualize our SDF-billboard actors. .. GENERATED FROM PYTHON SOURCE LINES 175-185 .. code-block:: Python spheres_actor = actor.billboard( centers, colors=colors, scales=scales, fs_dec=fs_dec, fs_impl=fs_impl) scene.add(spheres_actor) if interactive: window.show(scene) else: window.record( scene, size=(600, 600), out_path='viz_billboards_circles.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_005.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_005.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 186-190 Hold on, those actors don't look exactly like the ones we created using traditional techniques; they don't even look 3D but 2D. Well, that's because we still need an essential component: shading. So let's clear our scene and add shading to our SDF billboard actors. .. GENERATED FROM PYTHON SOURCE LINES 190-192 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 193-198 The first thing necessary to add shading to our SDF-billboard actors is to calculate the normals of the SDFs. In this tutorial we are not going to get into detail in the gradient and derivatives of SDFs, so we will use the central differences technique implemented in the following FURY shader function: .. GENERATED FROM PYTHON SOURCE LINES 198-201 .. code-block:: Python central_diffs_normal = import_fury_shader( os.path.join('sdf', 'central_diffs.frag')) .. GENERATED FROM PYTHON SOURCE LINES 202-204 To use the central differences technique, we need to define a map function that wraps our SDF and evaluates only a point. .. GENERATED FROM PYTHON SOURCE LINES 204-212 .. code-block:: Python sd_sphere_normal = \ """ float map(vec3 p) { return sdSphere(p, RADIUS); } """ .. GENERATED FROM PYTHON SOURCE LINES 213-214 Then we can load the Blinn-Phong illumination model. .. GENERATED FROM PYTHON SOURCE LINES 214-217 .. code-block:: Python blinn_phong_model = import_fury_shader( os.path.join('lighting', 'blinn_phong_model.frag')) .. GENERATED FROM PYTHON SOURCE LINES 218-220 Again, let's bring all of our declarations (constants and functions) together. .. GENERATED FROM PYTHON SOURCE LINES 220-224 .. code-block:: Python fs_dec = compose_shader([ sphere_radius, sd_sphere, sd_sphere_normal, central_diffs_normal, blinn_phong_model]) .. GENERATED FROM PYTHON SOURCE LINES 225-230 Now, we can start our fragment shader implementation with the signed distance function evaluation. You might notice that in this case, we are not using an if statement but a `step` function, which is a more efficient way to perform this evaluation. You can also replace the `step` function with a `smoothstep` operation and, in that way, add a very efficient form of antialiasing. .. GENERATED FROM PYTHON SOURCE LINES 230-232 .. code-block:: Python sdf_eval = 'opacity *= 1 - step(0, dist);' .. GENERATED FROM PYTHON SOURCE LINES 233-235 In this case, we also need the absolute value of the distance to compensate for the depth of the SDF sphere. .. GENERATED FROM PYTHON SOURCE LINES 235-237 .. code-block:: Python abs_dist = 'float absDist = abs(dist);' .. GENERATED FROM PYTHON SOURCE LINES 238-239 We are ready to calculate the normals. .. GENERATED FROM PYTHON SOURCE LINES 239-241 .. code-block:: Python normal = 'vec3 normal = centralDiffsNormals(vec3(point.xy, absDist), .0001);' .. GENERATED FROM PYTHON SOURCE LINES 242-243 With the normals we can calculate a light attenuation factor. .. GENERATED FROM PYTHON SOURCE LINES 243-245 .. code-block:: Python light_attenuation = 'float lightAttenuation = normal.z;' .. GENERATED FROM PYTHON SOURCE LINES 246-247 Now, we are ready to calculate the color and output it. .. GENERATED FROM PYTHON SOURCE LINES 247-256 .. code-block:: Python color = \ """ color = blinnPhongIllumModel( lightAttenuation, lightColor0, diffuseColor, specularPower, specularColor, ambientColor); """ frag_output = 'fragOutput0 = vec4(color, opacity);' .. GENERATED FROM PYTHON SOURCE LINES 257-258 As before, we can bring our implementation code together. .. GENERATED FROM PYTHON SOURCE LINES 258-262 .. code-block:: Python fs_impl = compose_shader([ sphere_dist, sdf_eval, abs_dist, normal, light_attenuation, color, frag_output]) .. GENERATED FROM PYTHON SOURCE LINES 263-264 Finally, recreate the SDF billboard actors and visualize them. .. GENERATED FROM PYTHON SOURCE LINES 264-274 .. code-block:: Python spheres_actor = actor.billboard( centers, colors=colors, scales=scales, fs_dec=fs_dec, fs_impl=fs_impl) scene.add(spheres_actor) if interactive: window.show(scene) else: window.record( scene, size=(600, 600), out_path='viz_billboards_spheres.png') .. image-sg:: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_006.png :alt: viz billboard sdf spheres :srcset: /auto_examples/13_shaders/images/sphx_glr_viz_billboard_sdf_spheres_006.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 275-282 References ---------- .. [Hart1996] Hart, John C. "Sphere tracing: A geometric method for the antialiased ray tracing of implicit surfaces." The Visual Computer 12.10 (1996): 527-545. .. include:: ../links_names.inc .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.333 seconds) .. _sphx_glr_download_auto_examples_13_shaders_viz_billboard_sdf_spheres.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: viz_billboard_sdf_spheres.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: viz_billboard_sdf_spheres.py ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_