.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/04_demos/viz_pbr_interactive.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_04_demos_viz_pbr_interactive.py: =============================================== Interactive PBR demo =============================================== This is a demonstration of how Physically-Based Rendering (PBR) can be used to simulate different materials. Let's start by importing the necessary modules: .. GENERATED FROM PYTHON SOURCE LINES 11-14 .. code-block:: Python import fury .. GENERATED FROM PYTHON SOURCE LINES 15-16 The following functions will help us to manage the sliders events. .. GENERATED FROM PYTHON SOURCE LINES 16-79 .. code-block:: Python def change_slice_metallic(slider): global pbr_params pbr_params.metallic = slider.value def change_slice_roughness(slider): global pbr_params pbr_params.roughness = slider.value def change_slice_anisotropy(slider): global pbr_params pbr_params.anisotropy = slider.value def change_slice_anisotropy_direction_x(slider): global doa, normals, sphere doa[0] = slider.value tangents = fury.utils.tangents_from_direction_of_anisotropy(normals, doa) fury.utils.tangents_to_actor(sphere, tangents) def change_slice_anisotropy_direction_y(slider): global doa, normals, sphere doa[1] = slider.value tangents = fury.utils.tangents_from_direction_of_anisotropy(normals, doa) fury.utils.tangents_to_actor(sphere, tangents) def change_slice_anisotropy_direction_z(slider): global doa, normals, sphere doa[2] = slider.value tangents = fury.utils.tangents_from_direction_of_anisotropy(normals, doa) fury.utils.tangents_to_actor(sphere, tangents) def change_slice_anisotropy_rotation(slider): global pbr_params pbr_params.anisotropy_rotation = slider.value def change_slice_coat_strength(slider): global pbr_params pbr_params.coat_strength = slider.value def change_slice_coat_roughness(slider): global pbr_params pbr_params.coat_roughness = slider.value def change_slice_base_ior(slider): global pbr_params pbr_params.base_ior = slider.value def change_slice_coat_ior(slider): global pbr_params pbr_params.coat_ior = slider.value .. GENERATED FROM PYTHON SOURCE LINES 80-82 Last, but not least, we define the following function to help us to reposition the UI elements every time we resize the window. .. GENERATED FROM PYTHON SOURCE LINES 82-93 .. code-block:: Python def win_callback(obj, event): global control_panel, size if size != obj.GetSize(): size_old = size size = obj.GetSize() size_change = [size[0] - size_old[0], 0] control_panel.re_align(size_change) .. GENERATED FROM PYTHON SOURCE LINES 94-95 Let's fetch a skybox texture from the FURY data repository. .. GENERATED FROM PYTHON SOURCE LINES 95-98 .. code-block:: Python fury.data.fetch_viz_cubemaps() .. rst-class:: sphx-glr-script-out .. code-block:: none Dataset is already in place. If you want to fetch it again please first remove the folder /Users/skoudoro/.fury/cubemaps ({'skybox-nx.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-nx.jpg', '12B1CE6C91AA3AAF258A8A5944DF739A6C1CC76E89D4D7119D1F795A30FC1BF2'), 'skybox-ny.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-ny.jpg', 'E18FE2206B63D3DF2C879F5E0B9937A61D99734B6C43AC288226C58D2418D23E'), 'skybox-nz.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-nz.jpg', '00DDDD1B715D5877AF2A74C014FF6E47891F07435B471D213CD0673A8C47F2B2'), 'skybox-px.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-px.jpg', 'BF20ACD6817C9E7073E485BBE2D2CE56DACFF73C021C2B613BA072BA2DF2B754'), 'skybox-py.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-py.jpg', '16F0D692AF0B80E46929D8D8A7E596123C76729CC5EB7DFD1C9184B115DD143A'), 'skybox-pz.jpg': ('https://raw.githubusercontent.com/fury-gl/fury-data/master/cubemaps/skybox-pz.jpg', 'B850B5E882889DF26BE9289D7C25BA30524B37E56BC2075B968A83197AD977F3')}, '/Users/skoudoro/.fury/cubemaps') .. GENERATED FROM PYTHON SOURCE LINES 99-101 The following function returns the full path of the 6 images composing the skybox. .. GENERATED FROM PYTHON SOURCE LINES 101-104 .. code-block:: Python textures = fury.data.read_viz_cubemap("skybox") .. GENERATED FROM PYTHON SOURCE LINES 105-107 Now that we have the location of the textures, let's load them and create a Cube Map Texture object. .. GENERATED FROM PYTHON SOURCE LINES 107-110 .. code-block:: Python cubemap = fury.io.load_cubemap_texture(textures) .. GENERATED FROM PYTHON SOURCE LINES 111-115 The Scene object in FURY can handle cube map textures and extract light information from them, so it can be used to create more plausible materials interactions. The ``skybox`` parameter takes as input a cube map texture and performs the previously described process. .. GENERATED FROM PYTHON SOURCE LINES 115-118 .. code-block:: Python scene = fury.window.Scene(skybox=cubemap) .. GENERATED FROM PYTHON SOURCE LINES 119-121 With the scene created, we can then populate it. In this demo we will only add a sphere actor. .. GENERATED FROM PYTHON SOURCE LINES 121-124 .. code-block:: Python sphere = fury.actor.sphere([[0, 0, 0]], (0.7, 0.7, 0.7), radii=2, theta=64, phi=64) .. GENERATED FROM PYTHON SOURCE LINES 125-127 The direction of anisotropy (DoA) defines the direction at which all the tangents of our actor are pointing. .. GENERATED FROM PYTHON SOURCE LINES 127-130 .. code-block:: Python doa = [0, 1, 0.5] .. GENERATED FROM PYTHON SOURCE LINES 131-134 The following process gets the normals of the actor and computes the tangents that are aligned to the provided DoA. Then it registers those tangents to the fury.actor. .. GENERATED FROM PYTHON SOURCE LINES 134-139 .. code-block:: Python normals = fury.utils.normals_from_actor(sphere) tangents = fury.utils.tangents_from_direction_of_anisotropy(normals, doa) fury.utils.tangents_to_actor(sphere, tangents) .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 140-142 With the tangents computed and in place, we have all the elements needed to add some material properties to the actor. .. GENERATED FROM PYTHON SOURCE LINES 142-145 .. code-block:: Python pbr_params = fury.material.manifest_pbr(sphere) .. GENERATED FROM PYTHON SOURCE LINES 146-147 Our actor is now ready to be added to the scene. .. GENERATED FROM PYTHON SOURCE LINES 147-150 .. code-block:: Python scene.add(sphere) .. GENERATED FROM PYTHON SOURCE LINES 151-152 Let's setup now the window and the UI. .. GENERATED FROM PYTHON SOURCE LINES 152-158 .. code-block:: Python show_m = fury.window.ShowManager( scene=scene, size=(1920, 1080), reset_camera=False, order_transparent=True ) .. GENERATED FROM PYTHON SOURCE LINES 159-160 We will create one single panel with all of our labels and sliders. .. GENERATED FROM PYTHON SOURCE LINES 160-165 .. code-block:: Python control_panel = fury.ui.Panel2D( (400, 500), position=(5, 5), color=(0.25, 0.25, 0.25), opacity=0.75, align="right" ) .. GENERATED FROM PYTHON SOURCE LINES 166-168 By using our previously defined function, we can easily create all the labels we need for this demo. And then add them to the panel. .. GENERATED FROM PYTHON SOURCE LINES 168-201 .. code-block:: Python slider_label_metallic = fury.ui.TextBlock2D(text="Metallic", font_size=16) slider_label_roughness = fury.ui.TextBlock2D(text="Roughness", font_size=16) slider_label_anisotropy = fury.ui.TextBlock2D(text="Anisotropy", font_size=16) slider_label_anisotropy_rotation = fury.ui.TextBlock2D( text="Anisotropy Rotation", font_size=16 ) slider_label_anisotropy_direction_x = fury.ui.TextBlock2D( text="Anisotropy Direction X", font_size=16 ) slider_label_anisotropy_direction_y = fury.ui.TextBlock2D( text="Anisotropy Direction Y", font_size=16 ) slider_label_anisotropy_direction_z = fury.ui.TextBlock2D( text="Anisotropy Direction Z", font_size=16 ) slider_label_coat_strength = fury.ui.TextBlock2D(text="Coat Strength", font_size=16) slider_label_coat_roughness = fury.ui.TextBlock2D(text="Coat Roughness", font_size=16) slider_label_base_ior = fury.ui.TextBlock2D(text="Base IoR", font_size=16) slider_label_coat_ior = fury.ui.TextBlock2D(text="Coat IoR", font_size=16) control_panel.add_element(slider_label_metallic, (0.01, 0.95)) control_panel.add_element(slider_label_roughness, (0.01, 0.86)) control_panel.add_element(slider_label_anisotropy, (0.01, 0.77)) control_panel.add_element(slider_label_anisotropy_rotation, (0.01, 0.68)) control_panel.add_element(slider_label_anisotropy_direction_x, (0.01, 0.59)) control_panel.add_element(slider_label_anisotropy_direction_y, (0.01, 0.5)) control_panel.add_element(slider_label_anisotropy_direction_z, (0.01, 0.41)) control_panel.add_element(slider_label_coat_strength, (0.01, 0.32)) control_panel.add_element(slider_label_coat_roughness, (0.01, 0.23)) control_panel.add_element(slider_label_base_ior, (0.01, 0.14)) control_panel.add_element(slider_label_coat_ior, (0.01, 0.05)) .. GENERATED FROM PYTHON SOURCE LINES 202-203 Our sliders are created and added to the panel in the following way. .. GENERATED FROM PYTHON SOURCE LINES 203-241 .. code-block:: Python slider_slice_metallic = fury.ui.LineSlider2D( initial_value=pbr_params.metallic, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_roughness = fury.ui.LineSlider2D( initial_value=pbr_params.roughness, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_anisotropy = fury.ui.LineSlider2D( initial_value=pbr_params.anisotropy, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_anisotropy_rotation = fury.ui.LineSlider2D( initial_value=pbr_params.anisotropy_rotation, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_coat_strength = fury.ui.LineSlider2D( initial_value=pbr_params.coat_strength, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_coat_roughness = fury.ui.LineSlider2D( initial_value=pbr_params.coat_roughness, max_value=1, length=195, text_template="{value:.1f}", ) .. GENERATED FROM PYTHON SOURCE LINES 242-245 Notice that we are defining a range of [-1, 1] for the DoA. This is because within that range we cover all the possible 3D directions needed to align the tangents. .. GENERATED FROM PYTHON SOURCE LINES 245-268 .. code-block:: Python slider_slice_anisotropy_direction_x = fury.ui.LineSlider2D( initial_value=doa[0], min_value=-1, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_anisotropy_direction_y = fury.ui.LineSlider2D( initial_value=doa[1], min_value=-1, max_value=1, length=195, text_template="{value:.1f}", ) slider_slice_anisotropy_direction_z = fury.ui.LineSlider2D( initial_value=doa[2], min_value=-1, max_value=1, length=195, text_template="{value:.1f}", ) .. GENERATED FROM PYTHON SOURCE LINES 269-272 Another special case are the Index of Refraction (IoR) sliders. In these cases, the values are defined in the range [1, 2.3] according to the documentation of the material. .. GENERATED FROM PYTHON SOURCE LINES 272-288 .. code-block:: Python slider_slice_base_ior = fury.ui.LineSlider2D( initial_value=pbr_params.base_ior, min_value=1, max_value=2.3, length=195, text_template="{value:.02f}", ) slider_slice_coat_ior = fury.ui.LineSlider2D( initial_value=pbr_params.coat_ior, min_value=1, max_value=2.3, length=195, text_template="{value:.02f}", ) .. GENERATED FROM PYTHON SOURCE LINES 289-290 Let's add the event handlers functions to the corresponding sliders. .. GENERATED FROM PYTHON SOURCE LINES 290-303 .. code-block:: Python slider_slice_metallic.on_change = change_slice_metallic slider_slice_roughness.on_change = change_slice_roughness slider_slice_anisotropy.on_change = change_slice_anisotropy slider_slice_anisotropy_rotation.on_change = change_slice_anisotropy_rotation slider_slice_anisotropy_direction_x.on_change = change_slice_anisotropy_direction_x slider_slice_anisotropy_direction_y.on_change = change_slice_anisotropy_direction_y slider_slice_anisotropy_direction_z.on_change = change_slice_anisotropy_direction_z slider_slice_coat_strength.on_change = change_slice_coat_strength slider_slice_coat_roughness.on_change = change_slice_coat_roughness slider_slice_base_ior.on_change = change_slice_base_ior slider_slice_coat_ior.on_change = change_slice_coat_ior .. GENERATED FROM PYTHON SOURCE LINES 304-305 And then add the sliders to the panel. .. GENERATED FROM PYTHON SOURCE LINES 305-318 .. code-block:: Python control_panel.add_element(slider_slice_metallic, (0.44, 0.95)) control_panel.add_element(slider_slice_roughness, (0.44, 0.86)) control_panel.add_element(slider_slice_anisotropy, (0.44, 0.77)) control_panel.add_element(slider_slice_anisotropy_rotation, (0.44, 0.68)) control_panel.add_element(slider_slice_anisotropy_direction_x, (0.44, 0.59)) control_panel.add_element(slider_slice_anisotropy_direction_y, (0.44, 0.5)) control_panel.add_element(slider_slice_anisotropy_direction_z, (0.44, 0.41)) control_panel.add_element(slider_slice_coat_strength, (0.44, 0.32)) control_panel.add_element(slider_slice_coat_roughness, (0.44, 0.23)) control_panel.add_element(slider_slice_base_ior, (0.44, 0.14)) control_panel.add_element(slider_slice_coat_ior, (0.44, 0.05)) .. GENERATED FROM PYTHON SOURCE LINES 319-320 Consequently, we add the panel to the scene. .. GENERATED FROM PYTHON SOURCE LINES 320-323 .. code-block:: Python scene.add(control_panel) .. GENERATED FROM PYTHON SOURCE LINES 324-327 Previously we defined a function to help us when we resize the window, so let's capture the current size and add our helper function as a `window_callback` to the window. .. GENERATED FROM PYTHON SOURCE LINES 327-332 .. code-block:: Python size = scene.GetSize() show_m.add_window_callback(win_callback) .. GENERATED FROM PYTHON SOURCE LINES 333-334 Finally, let's visualize our demo. .. GENERATED FROM PYTHON SOURCE LINES 334-340 .. code-block:: Python interactive = False if interactive: show_m.start() fury.window.record(scene, size=(1920, 1080), out_path="viz_pbr_interactive.png") .. image-sg:: /auto_examples/04_demos/images/sphx_glr_viz_pbr_interactive_001.png :alt: viz pbr interactive :srcset: /auto_examples/04_demos/images/sphx_glr_viz_pbr_interactive_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none /opt/homebrew/Caskroom/miniforge/base/envs/py39/lib/python3.9/site-packages/sphinx_gallery/gen_rst.py:722: UserWarning: We'll no longer accept the way you call the record function in future versions of FURY. Here's how to call the Function record: record(scene='value', cam_pos='value', cam_focal='value', cam_view='value', out_path='value', path_numbering='value', n_frames='value', az_ang='value', magnification='value', size='value', reset_camera='value', screen_clip='value', stereo='value', verbose='value') exec(self.code, self.fake_main.__dict__) .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.998 seconds) .. _sphx_glr_download_auto_examples_04_demos_viz_pbr_interactive.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: viz_pbr_interactive.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: viz_pbr_interactive.py ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_