.. 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-21 .. code-block:: Python from fury import actor, material, ui, window from fury.data import fetch_viz_cubemaps, read_viz_cubemap from fury.io import load_cubemap_texture from fury.utils import ( normals_from_actor, tangents_from_direction_of_anisotropy, tangents_to_actor, ) .. GENERATED FROM PYTHON SOURCE LINES 22-23 The following functions will help us to manage the sliders events. .. GENERATED FROM PYTHON SOURCE LINES 23-86 .. 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 = tangents_from_direction_of_anisotropy(normals, doa) tangents_to_actor(sphere, tangents) def change_slice_anisotropy_direction_y(slider): global doa, normals, sphere doa[1] = slider.value tangents = tangents_from_direction_of_anisotropy(normals, doa) tangents_to_actor(sphere, tangents) def change_slice_anisotropy_direction_z(slider): global doa, normals, sphere doa[2] = slider.value tangents = tangents_from_direction_of_anisotropy(normals, doa) 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 87-89 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 89-100 .. 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 101-102 Let's fetch a skybox texture from the FURY data repository. .. GENERATED FROM PYTHON SOURCE LINES 102-105 .. code-block:: Python 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 106-108 The following function returns the full path of the 6 images composing the skybox. .. GENERATED FROM PYTHON SOURCE LINES 108-111 .. code-block:: Python textures = read_viz_cubemap('skybox') .. GENERATED FROM PYTHON SOURCE LINES 112-114 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 114-117 .. code-block:: Python cubemap = load_cubemap_texture(textures) .. GENERATED FROM PYTHON SOURCE LINES 118-122 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 122-125 .. code-block:: Python scene = window.Scene(skybox=cubemap) .. GENERATED FROM PYTHON SOURCE LINES 126-128 With the scene created, we can then populate it. In this demo we will only add a sphere actor. .. GENERATED FROM PYTHON SOURCE LINES 128-131 .. code-block:: Python sphere = actor.sphere([[0, 0, 0]], (0.7, 0.7, 0.7), radii=2, theta=64, phi=64) .. GENERATED FROM PYTHON SOURCE LINES 132-134 The direction of anisotropy (DoA) defines the direction at which all the tangents of our actor are pointing. .. GENERATED FROM PYTHON SOURCE LINES 134-137 .. code-block:: Python doa = [0, 1, 0.5] .. GENERATED FROM PYTHON SOURCE LINES 138-141 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 actor. .. GENERATED FROM PYTHON SOURCE LINES 141-146 .. code-block:: Python normals = normals_from_actor(sphere) tangents = tangents_from_direction_of_anisotropy(normals, doa) tangents_to_actor(sphere, tangents) .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 147-149 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 149-152 .. code-block:: Python pbr_params = material.manifest_pbr(sphere) .. GENERATED FROM PYTHON SOURCE LINES 153-154 Our actor is now ready to be added to the scene. .. GENERATED FROM PYTHON SOURCE LINES 154-157 .. code-block:: Python scene.add(sphere) .. GENERATED FROM PYTHON SOURCE LINES 158-159 Let's setup now the window and the UI. .. GENERATED FROM PYTHON SOURCE LINES 159-165 .. code-block:: Python show_m = window.ShowManager( scene=scene, size=(1920, 1080), reset_camera=False, order_transparent=True ) .. GENERATED FROM PYTHON SOURCE LINES 166-167 We will create one single panel with all of our labels and sliders. .. GENERATED FROM PYTHON SOURCE LINES 167-172 .. code-block:: Python control_panel = ui.Panel2D( (400, 500), position=(5, 5), color=(0.25, 0.25, 0.25), opacity=0.75, align='right' ) .. GENERATED FROM PYTHON SOURCE LINES 173-175 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 175-208 .. code-block:: Python slider_label_metallic = ui.TextBlock2D(text='Metallic', font_size=16) slider_label_roughness = ui.TextBlock2D(text='Roughness', font_size=16) slider_label_anisotropy = ui.TextBlock2D(text='Anisotropy', font_size=16) slider_label_anisotropy_rotation = ui.TextBlock2D( text='Anisotropy Rotation', font_size=16 ) slider_label_anisotropy_direction_x = ui.TextBlock2D( text='Anisotropy Direction X', font_size=16 ) slider_label_anisotropy_direction_y = ui.TextBlock2D( text='Anisotropy Direction Y', font_size=16 ) slider_label_anisotropy_direction_z = ui.TextBlock2D( text='Anisotropy Direction Z', font_size=16 ) slider_label_coat_strength = ui.TextBlock2D(text='Coat Strength', font_size=16) slider_label_coat_roughness = ui.TextBlock2D(text='Coat Roughness', font_size=16) slider_label_base_ior = ui.TextBlock2D(text='Base IoR', font_size=16) slider_label_coat_ior = 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 209-210 Our sliders are created and added to the panel in the following way. .. GENERATED FROM PYTHON SOURCE LINES 210-248 .. code-block:: Python slider_slice_metallic = ui.LineSlider2D( initial_value=pbr_params.metallic, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_roughness = ui.LineSlider2D( initial_value=pbr_params.roughness, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_anisotropy = ui.LineSlider2D( initial_value=pbr_params.anisotropy, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_anisotropy_rotation = ui.LineSlider2D( initial_value=pbr_params.anisotropy_rotation, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_coat_strength = ui.LineSlider2D( initial_value=pbr_params.coat_strength, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_coat_roughness = ui.LineSlider2D( initial_value=pbr_params.coat_roughness, max_value=1, length=195, text_template='{value:.1f}', ) .. GENERATED FROM PYTHON SOURCE LINES 249-252 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 252-275 .. code-block:: Python slider_slice_anisotropy_direction_x = ui.LineSlider2D( initial_value=doa[0], min_value=-1, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_anisotropy_direction_y = ui.LineSlider2D( initial_value=doa[1], min_value=-1, max_value=1, length=195, text_template='{value:.1f}', ) slider_slice_anisotropy_direction_z = ui.LineSlider2D( initial_value=doa[2], min_value=-1, max_value=1, length=195, text_template='{value:.1f}', ) .. GENERATED FROM PYTHON SOURCE LINES 276-279 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 279-295 .. code-block:: Python slider_slice_base_ior = 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 = 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 296-297 Let's add the event handlers functions to the corresponding sliders. .. GENERATED FROM PYTHON SOURCE LINES 297-310 .. 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 311-312 And then add the sliders to the panel. .. GENERATED FROM PYTHON SOURCE LINES 312-325 .. 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 326-327 Consequently, we add the panel to the scene. .. GENERATED FROM PYTHON SOURCE LINES 327-330 .. code-block:: Python scene.add(control_panel) .. GENERATED FROM PYTHON SOURCE LINES 331-334 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 334-339 .. code-block:: Python size = scene.GetSize() show_m.add_window_callback(win_callback) .. GENERATED FROM PYTHON SOURCE LINES 340-341 Finally, let's visualize our demo. .. GENERATED FROM PYTHON SOURCE LINES 341-347 .. code-block:: Python interactive = False if interactive: show_m.start() 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-timing **Total running time of the script:** (0 minutes 0.974 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 `_