.. 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-73 .. 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, 0.2, 0.7, 3, 2]) spheres_actor = actor.sphere( centers, colors, radii=scales, phi=8, theta=8, use_primitive=False ) .. GENERATED FROM PYTHON SOURCE LINES 74-77 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 77-86 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 87-91 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 91-98 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 99-100 Let's clean the scene and play with the parameters `phi` and `theta`. .. GENERATED FROM PYTHON SOURCE LINES 100-112 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 113-118 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 120-123 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 125-129 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 129-131 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 132-134 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 134-143 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 144-148 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 148-150 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 151-153 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 153-155 .. code-block:: Python sd_sphere = import_fury_shader(os.path.join("sdf", "sd_sphere.frag")) .. GENERATED FROM PYTHON SOURCE LINES 156-159 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 159-161 .. code-block:: Python sphere_radius = "const float RADIUS = 1.;" .. GENERATED FROM PYTHON SOURCE LINES 162-164 Let's calculate the distance to the sphere by combining the previously defined variables. .. GENERATED FROM PYTHON SOURCE LINES 164-166 .. code-block:: Python sphere_dist = "float dist = sdSphere(point, RADIUS);" .. GENERATED FROM PYTHON SOURCE LINES 167-168 Now, evaluate the signed distance function. .. GENERATED FROM PYTHON SOURCE LINES 168-175 .. code-block:: Python sdf_eval = """ if (dist < 0) fragOutput0 = vec4(color, opacity); else discard; """ .. GENERATED FROM PYTHON SOURCE LINES 176-178 Putting all of our declarations (constants and function) and implementations (distance calculation and evaluation) together. .. GENERATED FROM PYTHON SOURCE LINES 178-181 .. code-block:: Python fs_dec = compose_shader([sphere_radius, sd_sphere]) fs_impl = compose_shader([sphere_dist, sdf_eval]) .. GENERATED FROM PYTHON SOURCE LINES 182-183 We are ready to create and visualize our SDF-billboard actors. .. GENERATED FROM PYTHON SOURCE LINES 183-193 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 194-198 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 198-200 .. code-block:: Python scene.clear() .. GENERATED FROM PYTHON SOURCE LINES 201-206 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 206-208 .. code-block:: Python central_diffs_normal = import_fury_shader(os.path.join("sdf", "central_diffs.frag")) .. GENERATED FROM PYTHON SOURCE LINES 209-211 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 211-218 .. code-block:: Python sd_sphere_normal = """ float map(vec3 p) { return sdSphere(p, RADIUS); } """ .. GENERATED FROM PYTHON SOURCE LINES 219-220 Then we can load the Blinn-Phong illumination model. .. GENERATED FROM PYTHON SOURCE LINES 220-224 .. code-block:: Python blinn_phong_model = import_fury_shader( os.path.join("lighting", "blinn_phong_model.frag") ) .. GENERATED FROM PYTHON SOURCE LINES 225-227 Again, let's bring all of our declarations (constants and functions) together. .. GENERATED FROM PYTHON SOURCE LINES 227-237 .. 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 238-243 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 243-245 .. code-block:: Python sdf_eval = "opacity *= 1 - step(0, dist);" .. GENERATED FROM PYTHON SOURCE LINES 246-248 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 248-250 .. code-block:: Python abs_dist = "float absDist = abs(dist);" .. GENERATED FROM PYTHON SOURCE LINES 251-252 We are ready to calculate the normals. .. GENERATED FROM PYTHON SOURCE LINES 252-254 .. code-block:: Python normal = "vec3 normal = centralDiffsNormals(vec3(point.xy, absDist), .0001);" .. GENERATED FROM PYTHON SOURCE LINES 255-256 With the normals we can calculate a light attenuation factor. .. GENERATED FROM PYTHON SOURCE LINES 256-258 .. code-block:: Python light_attenuation = "float lightAttenuation = normal.z;" .. GENERATED FROM PYTHON SOURCE LINES 259-260 Now, we are ready to calculate the color and output it. .. GENERATED FROM PYTHON SOURCE LINES 260-268 .. code-block:: Python color = """ color = blinnPhongIllumModel( lightAttenuation, lightColor0, diffuseColor, specularPower, specularColor, ambientColor); """ frag_output = "fragOutput0 = vec4(color, opacity);" .. GENERATED FROM PYTHON SOURCE LINES 269-270 As before, we can bring our implementation code together. .. GENERATED FROM PYTHON SOURCE LINES 270-274 .. code-block:: Python fs_impl = compose_shader( [sphere_dist, sdf_eval, abs_dist, normal, light_attenuation, color, frag_output] ) .. GENERATED FROM PYTHON SOURCE LINES 275-276 Finally, recreate the SDF billboard actors and visualize them. .. GENERATED FROM PYTHON SOURCE LINES 276-286 .. 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") .. GENERATED FROM PYTHON SOURCE LINES 287-294 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 .. _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 `_