• Randomization

Randomization Advanced Distribution

By: SKY ENGINE AI
scroll down ↓to find out morerandomization-advanced-distribution_12_resourcesTutorial

Randomization - Advanced Distribution

In this section you will get to know various Distribution tricks.

Agenda:

  • Passing Distributions into a Transform Provider
  • Practical control of Gaussian params
  • Modifying other existing Transform Providers
  • Custom plot-based Distributions
  • Random Gaussian use case
  • Custom Distribution trickery

Scene setup

Let's use custom scene composer to set up the scene.

from skyrenderer.cases.utils import RandomizationAdvancedDistributionSceneComposer scene_composer = RandomizationAdvancedDistributionSceneComposer(antialiasing_level=64) scene_composer.setup_scene() scene_composer.visualize() rc = scene_composer.renderer_context
randomization-advanced-distribution_1_resourcesTutorial
2025-01-30 13:13:50,693 | skyrenderer.scene.renderer_context | INFO: Root paths: - root path: /dli/skyenvironment/skyrenderer/skyrenderer - assets path: /dli/mount/assets - config path: /dli/skyenvironment/skyrenderer/skyrenderer/config - gpu sources path: /dli/skyenvironment/skyrenderer/skyrenderer/optix_sources/sources - cache path: /dli/mount/cache - ptx cache path: compiled_ptx/ptx - ocio path: ocio_configs/aces_1.2/config.ocio 2025-01-30 13:13:51,061 | skyrenderer.basic_types.provider.unit_providers.hdr_texture_provider | WARNING: Parameter light_adapt provided in HDR json is not supported 2025-01-30 13:13:51,062 | skyrenderer.basic_types.provider.unit_providers.hdr_texture_provider | WARNING: Parameter color_adapt provided in HDR json is not supported 2025-01-30 13:13:51,063 | skyrenderer.scene.renderer_context | WARNING: Light with light_id=back_LIGHT_NUL already exists in the scene and will be replace with a new one.There can only be a single light for a single node. 2025-01-30 13:13:51,064 | skyrenderer.scene.renderer_context | WARNING: Light with light_id=front_LIGHT_NUL already exists in the scene and will be replace with a new one.There can only be a single light for a single node. 2025-01-30 13:13:51,064 | skyrenderer.scene.renderer_context | WARNING: Light with light_id=moon_LIGHT_NUL already exists in the scene and will be replace with a new one.There can only be a single light for a single node. 2025-01-30 13:13:51,146 | skyrenderer.utils.time_measurement | INFO: Setup time: 80 ms 2025-01-30 13:13:51,214 | skyrenderer.utils.time_measurement | INFO: Setup time: 65 ms 2025-01-30 13:13:52,249 | skyrenderer.utils.time_measurement | INFO: Context update time: 1.04 seconds 2025-01-30 13:15:07,343 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-01-30 13:15:07,344 | skyrenderer.utils.time_measurement | INFO: Render time: 75.09 seconds

In our initial scene we can see an alley between two office buildings. There is a single firefly floating in the
middle (that lone yellow pixel, 1/3 from the bottom and 1/3 from the right edge). His name is Fred. He has the
following coordinates in meters:

cx = -14.401 # negative x is deeper into the alley, front face of the left building is at -4.5 cy = 6.0256 # positive y is vertically upwards, roof of the left building is at +11 cz = 0.98872 # z is horizontal between the buildings, left facade is at +3.5, right at -1.5 center = (cx, cy, cz) fred = rc.layout().get_node("firefly_GEO_NUL") fred_geometry = "firefly_GEO"

Passing Distributions into a Transform Provider

Fred has organized a meetup for his friends in this particular spot because there are many nice reflective windows
around, which allows everyone to show off their evening glow.

Fred's friends have arrived:

fred.n_instances = 500

They all want to greet Fred, so they queued up.

from skyrenderer.basic_types.provider import SimpleTransformProvider from skyrenderer.randomization.strategy import DrawingStrategy, UniformRandomInput, RelativeGaussianRandomInput def greet_fred(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(cx, cx + 15), translation_range_y=(cy, cy), translation_range_z=(cz - 2, cz + 2), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": UniformRandomInput(), "translation_z": RelativeGaussianRandomInput(relative_mu=0.5, relative_sigma=0.02), }, ), ) greet_fred() scene_composer.visualize()
randomization-advanced-distribution_2_resourcesTutorial
2025-01-30 13:15:09,065 | skyrenderer.utils.time_measurement | INFO: Setup time: 935 ms 2025-01-30 13:15:11,485 | skyrenderer.utils.time_measurement | INFO: Context update time: 2.42 seconds 2025-01-30 13:16:30,825 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-01-30 13:16:30,825 | skyrenderer.utils.time_measurement | INFO: Render time: 79.34 seconds

For the courteous beginning of the party they want to be evenly spaced out to be able to mingle properly. Time for
really spacing out will come later. Let's define an aquarium-shaped volume within which the fireflies are
allowed to move about.

rx = 10.65 # +/- range in meters, so in this case from (-14.401 - 10.65 = -25.051) to (-14.401 + 10.65 = -3.751) ry = 3.64 rz = 2.3 uniform = UniformRandomInput() def disperse(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(cx - rx, cx + rx), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": uniform, "translation_y": uniform, "translation_z": uniform, }, ), ) disperse() scene_composer.visualize()
randomization-advanced-distribution_3_resourcesTutorial
2025-01-30 13:16:32,523 | skyrenderer.utils.time_measurement | INFO: Setup time: 926 ms 2025-01-30 13:16:34,881 | skyrenderer.utils.time_measurement | INFO: Context update time: 2.36 seconds 2025-01-30 13:17:53,475 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-01-30 13:17:53,476 | skyrenderer.utils.time_measurement | INFO: Render time: 78.59 seconds

Practical control of Gaussian params

Turns out one firefly is Elon's cousin - Felon. Everyone suddenly wants a selfie with him, but Felon is an
introvert, just like Elon, so he tries to hide in the corner. Now Fred's friends' hyped friends joined the party
and everyone piles up on Felon.

fred.n_instances = 2000 front_of_depth_gauss = RelativeGaussianRandomInput(relative_mu=1, relative_sigma=0.1) mid_of_height_gauss = RelativeGaussianRandomInput(relative_mu=0.5, relative_sigma=0.2) left_of_width_gauss = RelativeGaussianRandomInput(relative_mu=1, relative_sigma=0.2) def pile(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(cx - rx, cx + rx), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": front_of_depth_gauss, # end of x range is the alley entrance "translation_y": mid_of_height_gauss, # poor Felon is trapped by the crowd from above and below "translation_z": left_of_width_gauss, # end of z range is the left facade }, ), ) pile() scene_composer.visualize()
randomization-advanced-distribution_4_resourcesTutorial
2025-01-30 13:17:54,249 | skyrenderer.basic_types.provider.iprovider | WARNING: Provider for this config has been already initialized! There should be just one Provider created per unit source, consider fixing the scene. Config: {'translation_range_x': (-25.051000000000002, -3.7509999999999994), 'translation_range_y': (2.3855999999999997, 9.6656), 'translation_range_z': (-1.3112799999999998, 3.2887199999999996), 'rotation_range_x': (0.0, 0.0), 'rotation_range_y': (0.0, 0.0), 'rotation_range_z': (0.0, 0.0), 'scale_range_x': (1, 1), 'scale_range_y': (1, 1), 'scale_range_z': (1, 1), 'number_of_steps': 1000} 2025-01-30 13:17:57,657 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.41 seconds 2025-01-30 13:18:04,485 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.83 seconds 2025-01-30 13:19:23,504 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:19:23,505 | skyrenderer.utils.time_measurement | INFO: Render time: 79.02 seconds

Modifying other existing Transform Providers

To rescue Felon, Fred recruits Fiona to give a keynote speech about success and work-life balance. All guests are
asked to gather around.

from skyrenderer.basic_types.provider.transform_providers.ball_transform_provider import BallTransformProvider def around(): fred.modify_locus_definition(transform_provider=BallTransformProvider(rc, radius=rz, center=(cx + 5, cy, cz))) around() scene_composer.visualize()
randomization-advanced-distribution_5_resourcesTutorial
2025-01-30 13:19:27,734 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.45 seconds 2025-01-30 13:19:33,647 | skyrenderer.utils.time_measurement | INFO: Context update time: 5.91 seconds 2025-01-30 13:20:52,321 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:20:52,322 | skyrenderer.utils.time_measurement | INFO: Render time: 78.67 seconds

Fiona tells the crowd that while it's nice to feel like a supernova, it'd be better for them to back up some, so
everyone can have a chance to see and hear her.

def around_away(): fred.modify_locus_definition( transform_provider=BallTransformProvider(rc, radius=rz, center=(cx + 5, cy, cz)), strategy=DrawingStrategy( rc, inputs_strategies={ "radius": RelativeGaussianRandomInput( # we are affecting the internal "radius" param of the BallTransformProvider, # which controls how far from the center the positions are drawn relative_mu=1, # 0~1 range represents from center to ball surface relative_sigma=0.1, # we want the guests packed somewhat tightly at ~equal distance from Fiona ) }, ), ) around_away() scene_composer.visualize()
randomization-advanced-distribution_6_resourcesTutorial
2025-01-30 13:20:53,085 | skyrenderer.basic_types.provider.iprovider | WARNING: Provider for this config has been already initialized! There should be just one Provider created per unit source, consider fixing the scene. Config: {'center': (-9.401, 6.0256, 0.98872), 'radius': 2.3, 'number_of_points': 1000000} 2025-01-30 13:20:56,673 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.59 seconds 2025-01-30 13:21:02,920 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.25 seconds 2025-01-30 13:22:22,431 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:22:22,431 | skyrenderer.utils.time_measurement | INFO: Render time: 79.51 seconds

Custom plot-based Distributions

After the keynote, fireflies took a drink break and flocked to two opposite bars: cocktails on the left and
pumpkin spice soy latte decaf with stevia on the right. That's right, the right one serves precisely only that.

def two_sides(x): # Create any continuous plot in the square between 0,0 and 1,1 - it will become your distribution. # In this example we will use: y=(2x-1)^4 return (2 x - 1) * 4 # https://www.desmos.com/calculator/v3x50bgtwb from skyrenderer.randomization.strategy import ProbabilityShapeFunctionRandomInput def sides(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(cx + rx - 3, cx + rx), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": uniform, "translation_y": mid_of_height_gauss, # bars are near the middle, that's why Felon stayed nearby "translation_z": ProbabilityShapeFunctionRandomInput(two_sides), # see func definition above }, ), ) sides() scene_composer.visualize()
randomization-advanced-distribution_7_resourcesTutorial
2025-01-30 13:22:27,360 | skyrenderer.utils.time_measurement | INFO: Setup time: 4.14 seconds 2025-01-30 13:22:33,993 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.63 seconds 2025-01-30 13:23:52,844 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:23:52,845 | skyrenderer.utils.time_measurement | INFO: Render time: 78.85 seconds

As they got buzzed, the guests decided they no longer need to be politically correct, so they went on to join
their respective groups of self-perceived social status.

import math def four_levels(x): # In this example we will use: y=2sin(7pix)-1 return 2 math.sin(7 math.pi * x) - 1 # Again, focus on the 0,0 ~ 1,1 square - there are 4 distinct bumps. # https://www.desmos.com/calculator/osnqrxkk3w def strata(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(-10, -8), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": uniform, "translation_y": ProbabilityShapeFunctionRandomInput(four_levels), "translation_z": uniform, }, ), )

They also let go of their inhibitions and came out with their true colors. There is no judgment among fireflies.
Or is there?

from skyrenderer.scene.scene_layout.layout_elements_definitions import MaterialDefinition from skyrenderer.basic_types.procedure import PBRShader from skyrenderer.basic_types.provider.provider_inputs import HSVColorInput def diversity(): rc.instancers[fred_geometry].set_material_definition( material_definition=MaterialDefinition( shader=PBRShader(rc), parameter_set=PBRShader.create_parameter_provider( rc, specular_factor=0, roughness=1, base_color=HSVColorInput( hue_range=(0.466, 0.006), saturation_range=(0.903, 0.903), value_range=(1, 1) ), ambient_gain=6, casts_shadows=False, ), ) ) strata() diversity() scene_composer.visualize()
randomization-advanced-distribution_8_resourcesTutorial
2025-01-30 13:23:53,612 | skyrenderer.randomization.strategy.input_drawing_strategy | WARNING: Probability shape function: four_levels() yields negative y values, they have been clamped to 0. 2025-01-30 13:23:57,293 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.68 seconds 2025-01-30 13:24:04,198 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.90 seconds 2025-01-30 13:25:23,324 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:25:23,324 | skyrenderer.utils.time_measurement | INFO: Render time: 79.13 seconds

Random Gaussian usecase

Fireflies from the lower levels got offended by some smug looks and posh utterances from above. Their leader
Farquaad decided to invite everyone into a drunken brawl.

from skyrenderer.randomization.strategy import RandomGaussianRandomInput random_gauss = RandomGaussianRandomInput(sigma_relative_limits=(0.05, 0.2)) def brawl(): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(-12, -6), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ # by using RandomGaussianRandomInput, mu and sigma will be randomized each frame, # so the furious crowd will keep moving from one place to the next "translation_x": random_gauss, "translation_y": random_gauss, "translation_z": random_gauss, }, ), ) brawl() scene_composer.visualize(frame=1) scene_composer.visualize(frame=2) scene_composer.visualize(frame=3)
randomization-advanced-distribution_9_resourcesTutorial
randomization-advanced-distribution_10_resourcesTutorial
randomization-advanced-distribution_11_resourcesTutorial
2025-01-30 13:25:27,878 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.75 seconds 2025-01-30 13:25:34,733 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.85 seconds 2025-01-30 13:26:54,244 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:26:54,245 | skyrenderer.utils.time_measurement | INFO: Render time: 79.51 seconds 2025-01-30 13:26:58,536 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.55 seconds 2025-01-30 13:27:05,335 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.80 seconds 2025-01-30 13:28:24,875 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:28:24,877 | skyrenderer.utils.time_measurement | INFO: Render time: 79.54 seconds 2025-01-30 13:28:29,273 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.67 seconds 2025-01-30 13:28:36,073 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.80 seconds 2025-01-30 13:29:55,233 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:29:55,234 | skyrenderer.utils.time_measurement | INFO: Render time: 79.16 seconds

Custom Distribution trickery

Situation quickly spiraled out of control.

from skyrenderer.randomization.strategy import CustomRandomInput import random last_x_seed = -1 def spiral_generator(seed, number_of_states, input_name, y_func, z_func): # note the use of input_name fuzz = 0.002 global last_x_seed if input_name "translation_x": last_x_seed = seed else: seed = last_x_seed max_seed = 2*32 - 1 frac = seed / max_seed frac += fuzz (random.random() - 0.5) if input_name "translation_x": ret_val = round(frac number_of_states) elif input_name == "translation_y": ret_val = round(y_func(frac) number_of_states) elif input_name == "translation_z": ret_val = round(z_func(frac) * number_of_states) if ret_val < 0: ret_val = 0 elif ret_val >= number_of_states: ret_val = number_of_states - 1 return ret_val def normal_spiral(seed, number_of_states, input_name, args): freq = 10 # https://www.desmos.com/calculator/tdzig3kpme def normal_y(x): return math.sin(freq math.pi x) / 2 + 0.5 def normal_z(x): return math.cos(freq math.pi * x) / 2 + 0.5 return spiral_generator(seed, number_of_states, input_name, normal_y, normal_z) def spiral(spiral_func): fred.modify_locus_definition( transform_provider=SimpleTransformProvider( rc, translation_range_x=(cx - rx, cx + rx), translation_range_y=(cy - ry, cy + ry), translation_range_z=(cz - rz, cz + rz), ), strategy=DrawingStrategy( rc, inputs_strategies={ "translation_x": CustomRandomInput(spiral_func), "translation_y": CustomRandomInput(spiral_func), "translation_z": CustomRandomInput(spiral_func), }, ), ) spiral(normal_spiral) scene_composer.visualize()
randomization-advanced-distribution_12_resourcesTutorial
2025-01-30 13:29:56,008 | skyrenderer.basic_types.provider.iprovider | WARNING: Provider for this config has been already initialized! There should be just one Provider created per unit source, consider fixing the scene. Config: {'translation_range_x': (-25.051000000000002, -3.7509999999999994), 'translation_range_y': (2.3855999999999997, 9.6656), 'translation_range_z': (-1.3112799999999998, 3.2887199999999996), 'rotation_range_x': (0.0, 0.0), 'rotation_range_y': (0.0, 0.0), 'rotation_range_z': (0.0, 0.0), 'scale_range_x': (1, 1), 'scale_range_y': (1, 1), 'scale_range_z': (1, 1), 'number_of_steps': 1000} 2025-01-30 13:29:59,595 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.59 seconds 2025-01-30 13:30:06,544 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.95 seconds 2025-01-30 13:31:26,531 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:31:26,532 | skyrenderer.utils.time_measurement | INFO: Render time: 79.99 seconds

def bonkers_spiral(seed, number_of_states, input_name, args): freq = 10 phase = 0.02 # https://www.desmos.com/calculator/0rizu1fihh def bonkers_y(x): return (math.sin(freq math.pi (x - phase)) / 2) / (2 - math.sin((freq + 1) math.pi x)) + 0.5 def bonkers_z(x): return (math.cos(freq math.pi (x - phase)) / 2) / ( 2 - math.cos((freq / 2 - 0.4) math.pi * (x - phase - 0.1)) ) + 0.5 return spiral_generator(seed, number_of_states, input_name, bonkers_y, bonkers_z) spiral(bonkers_spiral) scene_composer.visualize()
randomization-advanced-distribution_13_resourcesTutorial
2025-01-30 13:31:27,302 | skyrenderer.basic_types.provider.iprovider | WARNING: Provider for this config has been already initialized! There should be just one Provider created per unit source, consider fixing the scene. Config: {'translation_range_x': (-25.051000000000002, -3.7509999999999994), 'translation_range_y': (2.3855999999999997, 9.6656), 'translation_range_z': (-1.3112799999999998, 3.2887199999999996), 'rotation_range_x': (0.0, 0.0), 'rotation_range_y': (0.0, 0.0), 'rotation_range_z': (0.0, 0.0), 'scale_range_x': (1, 1), 'scale_range_y': (1, 1), 'scale_range_z': (1, 1), 'number_of_steps': 1000} 2025-01-30 13:31:30,968 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.67 seconds 2025-01-30 13:31:37,941 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.97 seconds 2025-01-30 13:32:57,264 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:32:57,265 | skyrenderer.utils.time_measurement | INFO: Render time: 79.32 seconds

Epilogue

Finally, Fred, Fiona, Felon and Farquaad managed to convince everyone to chill. Fireflies put some festive lights
on and flew away, singing merry tunes.

from skyrenderer.basic_types.provider.transform_providers.disc_transform_provider import DiscTransformProvider def waves(x): # https://www.desmos.com/calculator/ilfdueawyl return (2 math.sin(12 math.pi x) math.sin(8 x)) / (39 (x + 0.01)) + 0.65 - 1.5 * x def milky_way(): fred.modify_locus_definition( transform_provider=DiscTransformProvider( rc, center=(cx + 2, cy - 2, cz - rz - 1), radius=3 * rz, normal_vector=(1, 1, -0.2), number_of_points=1000000, ), strategy=DrawingStrategy( rc, inputs_strategies={ "radius": RelativeGaussianRandomInput(relative_mu=0.7, relative_sigma=0.1), "phi": ProbabilityShapeFunctionRandomInput(waves), }, ), ) rc.instancers[fred_geometry].set_material_definition( material_definition=MaterialDefinition( shader=PBRShader(rc), parameter_set=PBRShader.create_parameter_provider( rc, specular_factor=0, roughness=1, base_color=HSVColorInput(hue_range=(0, 1), saturation_range=(1, 1), value_range=(1, 1)), ambient_gain=6, casts_shadows=False, ), ) ) milky_way() scene_composer.visualize()
randomization-advanced-distribution_14_resourcesTutorial
2025-01-30 13:32:58,047 | skyrenderer.randomization.strategy.input_drawing_strategy | WARNING: Probability shape function: waves() yields negative y values, they have been clamped to 0. 2025-01-30 13:33:01,183 | skyrenderer.utils.time_measurement | INFO: Setup time: 3.14 seconds 2025-01-30 13:33:07,468 | skyrenderer.utils.time_measurement | INFO: Context update time: 6.28 seconds 2025-01-30 13:34:26,829 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 1 ms 2025-01-30 13:34:26,830 | skyrenderer.utils.time_measurement | INFO: Render time: 79.36 seconds

Summary

In this section you have learnt:

  • There are many ways to use Distributions, aside from the default Uniform.
  • Built-in Gaussians with sensible parameters will cover many common cases.
  • Understanding Custom callbacks enables handling unusual scenarios with arbitrary math.