• Intro

Lights

By: SKY ENGINE AI
scroll down ↓to find out morelights_5_resourcesTutorial

Lights Introduction

In this case you will get high level understanding of Lights interface in SkyRenderer. You will learn what common
methods different types of lights share. A code example will be done based on a Point Light, however interface is
similar for most of the lights implemented in SkyRenderer. Feel free to return to this introduction tutorial as
you progress throughout light tutorials, since things will become clearer the more you know.

Agenda:

  • Light types in SkyRenderer
  • Light parameters
  • How to set up lights in SkyRenderer?
  • Manipulation of existing lights

Light Types

In SkyRenderer we have implemented multiple types of lights:

from skyrenderer.basic_types.light.light import LightType for light_type in LightType: print(light_type.name)
AREA_DIRECTION_LIGHT CONSTANT_LIGHT CONSTANT_SPHERE_LIGHT DIRECTION_LIGHT ENV_LIGHT MESH_LIGHT PLANE_LIGHT POINT_LIGHT PORTAL_SPHERE_LIGHT SPHERE_LIGHT SPOTLIGHT

Parameters

We can modify behaviour of lights with different parameters.
Parameters can be divided into two main groups:

  1. Coordinate space specific parameters
    Changes are done on the node level. Imagine it as moving/scaling/rotating the object in the space.
    Parameters:

    • translation - (X, Y, Z) position.
  2. Light specific parameters
    Changes concern light type specific behaviour e.g., color, strength, size, shape etc.
    Even though light specific parameters differ for different types of lights, changes to these parameters are done
    via the same interface. All lights have implemented ParameterProvider method that is used to pass custom values
    for light parameters. If it's not clear - do not worry, we will elaborate more in code usage section further in
    the tutorial.
    A few exemplary light specific parameters:

    • color - 3 channel (Red, Green, Blue) color tuple - (R, G, B),
    • illuminance - light strength, illuminance in candelas,
    • sample density - sample angular density (number of rays per 1 degree).
      Bear in mind that above parameters do not exist in all light types. For available light parameters and their
      default values check light specific classes and their documentation e.g., PointLight, SphereLight etc.

Test scene setup

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

from skyrenderer.cases.utils import LightsSceneComposer scene_composer = LightsSceneComposer() scene_composer.setup_scene() renderer_context = scene_composer.renderer_context
2025-02-03 14:48:17,880 | 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

Scene consists of:

  • shaderball - location: (0, 0, 0)
  • plane - location: (0, 0, 0)
  • camera - location: (4, 2, 2)

When we try to render the scene, everything is black.

scene_composer.visualize(scene_composer.get_render())
lights_1_resourcesTutorial
2025-02-03 14:48:18,321 | skyrenderer.utils.time_measurement | INFO: Setup time: 404 ms 2025-02-03 14:48:21,210 | skyrenderer.utils.time_measurement | INFO: Context update time: 2.89 seconds 2025-02-03 14:48:22,846 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:22,847 | skyrenderer.utils.time_measurement | INFO: Render time: 1.63 seconds

Why is that? There are no lights defined in the scene, so everything looks dark. In order to display colors on
the objects and shade them in proper way, we have to add lights to the scene.

Light setup process

Even though the lights differ, process of set up is similar for all of them.

High level description of flow is as follows:

  1. Create / use already existing point (node) in the coordinate space that will be used as a light origin.
  2. Create Light object in the aforementioned point.
  3. Register light node in the scene.

As an example we will use one of the most common type of lights - Point Light.
We will create basic Light with line by line explanation, afterward we will modify its parameters values using
ParameterProvider. Do not focus right now on Point Light properties, but overall process of the light setup.

Create light node

To create a Light we need to define its location in a coordinate system.

HINT: To see how nodes are created check INTRO_SceneLayout tutorial.

Let's create a node a few units to the left from camera origin (reminder: (4, 2, 2)).

from skyrenderer.scene.scene_layout.layout_elements_definitions import LocusDefinition from skyrenderer.basic_types.locus.transform import Transform light_locus = LocusDefinition(transform=Transform(translation_vector=[2, 2, 4])) renderer_context.add_node(node_key="light_LIGHT_NUL", locus_def=light_locus)
'light_LIGHT_NUL'

Create light object

Let's create a Point Light object in the location of a newly created node. To do so we must pass to PointLight
class two arguments:

  • context - renderer context object storing all information about scene,
  • origin_name - name of the origin of the light (node that we created).
from skyrenderer.basic_types.light import PointLight light = PointLight(context=renderer_context, origin_name="light_LIGHT_NUL")
2025-02-03 14:48:23,066 | skyrenderer.basic_types.light.point_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible

Most of the lights usually require only these two arguments, however there may be some specific lights that need
more. In case of issues check particular Light class init and check if there are more arguments required.

Register light

Afterward we have to register light node in the renderer_context by set_light() method.

renderer_context.set_light(light)
2025-02-03 14:48:23,072 | skyrenderer.scene.renderer_context | WARNING: Setting light after setup. Origin refers to last scene tree set up. Lights are stored in dict - change is visible immediately; Wrong parameter provider possible

Let's visualize the output. As you can see we have basic, white light shining from the left side.

scene_composer.visualize(scene_composer.get_render())
lights_2_resourcesTutorial
2025-02-03 14:48:23,169 | skyrenderer.utils.time_measurement | INFO: Setup time: 90 ms 2025-02-03 14:48:26,253 | skyrenderer.utils.time_measurement | INFO: Context update time: 3.08 seconds 2025-02-03 14:48:28,533 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:28,534 | skyrenderer.utils.time_measurement | INFO: Render time: 2.28 seconds

Light manipulation

We can modify light in render by modifying parameters of the light.

Location

By modification of origin's location, we get different render results. Imagine tiny light bulb that changes
location in the space.

Let's move light source further from the shaderball. Shaderball should be lit way weaker.

Change on Y axis: [2, 2, 4] -> [2, 2, 10]

renderer_context.get_node("light_LIGHT_NUL").translation = [2, 2, 10] scene_composer.visualize(scene_composer.get_render())
lights_3_resourcesTutorial
2025-02-03 14:48:28,918 | skyrenderer.utils.time_measurement | INFO: Setup time: 59 ms 2025-02-03 14:48:31,993 | skyrenderer.utils.time_measurement | INFO: Context update time: 3.07 seconds 2025-02-03 14:48:35,061 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:35,062 | skyrenderer.utils.time_measurement | INFO: Render time: 3.07 seconds

Let's move light source now extremely close to the shaderball. Shaderball should be lit extremely strong.

Change on Y axis: [2, 2, 10] -> [2, 2, 1]

renderer_context.get_node("light_LIGHT_NUL").translation = [2, 2, 1] scene_composer.visualize(scene_composer.get_render())
lights_4_resourcesTutorial
2025-02-03 14:48:35,447 | skyrenderer.utils.time_measurement | INFO: Setup time: 64 ms 2025-02-03 14:48:38,463 | skyrenderer.utils.time_measurement | INFO: Context update time: 3.01 seconds 2025-02-03 14:48:41,444 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:41,445 | skyrenderer.utils.time_measurement | INFO: Render time: 2.98 seconds

For the rest of the tutorial, we will change back to initial light position.

renderer_context.get_node("light_LIGHT_NUL").translation = [2, 2, 4]

Light specific parameters

These changes are done via ParameterProvider. To change light specific parameter values we have to create
ParameterProvider object. All types of lights have implemented create_parameter_provider() method. Mandatory
argument for that method is renderer_context.

mock_point_light_parameter_provider = PointLight.create_parameter_provider(renderer_context)
2025-02-03 14:48:41,777 | skyrenderer.basic_types.light.point_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible

In method's documentation you can see what parameters can be passed to PointLight's create_parameter_provider()
method.
For Point Light these are:

  • color
  • illuminance
  • sample_density
    Check a few other methods:
from skyrenderer.basic_types.light.sphere_light import SphereLight mock_sphere_light_parameter_provider = SphereLight.create_parameter_provider(renderer_context)
2025-02-03 14:48:41,786 | skyrenderer.basic_types.light.sphere_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible

For Sphere Light these are:

  • color
  • illuminance
  • radius
  • sample_densitiy
  • visibility
from skyrenderer.basic_types.light.constant_light import ConstantLight mock_constant_light_parameter_provider = ConstantLight.create_parameter_provider(renderer_context)
2025-02-03 14:48:41,795 | skyrenderer.basic_types.light.constant_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible

For Constant Light these are:

  • color
  • illuminance
  • visibility

As you can see arguments passed to create_parameter_provider() method vary for different types of lights.
Let's see how we can use this Parameter Provider method to change the color of the light in the current scene.

Color

Let's modify values of color channels for light (R, G, B) channels and make the light red.
Keep in mind that change of the light with a new ParameterProvider has to be registered in the renderer_context
with set_light() method.

red_light_parameter_provider = PointLight.create_parameter_provider(renderer_context, color=(1, 0.2, 0.2)) red_light = PointLight( context=renderer_context, origin_name="light_LIGHT_NUL", parameter_provider=red_light_parameter_provider ) renderer_context.set_light(red_light)
2025-02-03 14:48:41,801 | skyrenderer.basic_types.light.point_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible 2025-02-03 14:48:41,802 | skyrenderer.scene.renderer_context | WARNING: Setting light after setup. Origin refers to last scene tree set up. Lights are stored in dict - change is visible immediately; Wrong parameter provider possible 2025-02-03 14:48:41,802 | skyrenderer.scene.renderer_context | WARNING: Light with light_id=light_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.

Let's visualize the output. As we can see red_light_parameter_provider enabled us creation of red_light Light
object, that has a color parameter value set to (1, 0.2, 0.2). Registering red_light node in renderer_context
saves the change of the light.

scene_composer.visualize(scene_composer.get_render())
lights_5_resourcesTutorial
2025-02-03 14:48:41,878 | skyrenderer.utils.time_measurement | INFO: Setup time: 70 ms 2025-02-03 14:48:44,804 | skyrenderer.utils.time_measurement | INFO: Context update time: 2.93 seconds 2025-02-03 14:48:47,811 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:47,812 | skyrenderer.utils.time_measurement | INFO: Render time: 3.01 seconds

With create_parameter_provider() method you can change values of a few arguments at the same time, just pass into
the method values for a few arguments:

red_strong_light_parameter_provider = PointLight.create_parameter_provider( renderer_context, color=(1, 0.2, 0.2), illuminance=100 ) red_strong_light = PointLight( context=renderer_context, origin_name="light_LIGHT_NUL", parameter_provider=red_strong_light_parameter_provider ) renderer_context.set_light(red_strong_light)
2025-02-03 14:48:48,205 | skyrenderer.basic_types.light.point_light | WARNING: Creating light parameter provider in context which is set up - hanging link possible 2025-02-03 14:48:48,206 | skyrenderer.scene.renderer_context | WARNING: Setting light after setup. Origin refers to last scene tree set up. Lights are stored in dict - change is visible immediately; Wrong parameter provider possible 2025-02-03 14:48:48,206 | skyrenderer.scene.renderer_context | WARNING: Light with light_id=light_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.

As you will see, our light becomes stronger this way.

scene_composer.visualize(scene_composer.get_render())
lights_6_resourcesTutorial
2025-02-03 14:48:48,270 | skyrenderer.utils.time_measurement | INFO: Setup time: 59 ms 2025-02-03 14:48:51,056 | skyrenderer.utils.time_measurement | INFO: Context update time: 2.78 seconds 2025-02-03 14:48:54,111 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:48:54,112 | skyrenderer.utils.time_measurement | INFO: Render time: 3.05 seconds

Extra

What is more, when you create ParameterProvider, it can be passed to the other light instances, as long as it is
the same type of light. If you have in the scene two PointLights they can share the same ParameterProvider.
However, if you pass to SphereLight instance PointLight's ParameterProvider, you will encounter errors.

As mentioned in the Introduction we can also create lights in the location of already existing nodes.

Let's create in the location of camera (camera_CAM_NUL) a new light node using the same parameter provider as
already existing light_LIGHT_NUL.

red_camera_strong_light = PointLight( context=renderer_context, origin_name="camera_CAM_NUL", parameter_provider=red_strong_light_parameter_provider ) renderer_context.set_light(red_camera_strong_light) scene_composer.visualize(scene_composer.get_render())
lights_7_resourcesTutorial
2025-02-03 14:48:54,561 | skyrenderer.scene.renderer_context | WARNING: Setting light after setup. Origin refers to last scene tree set up. Lights are stored in dict - change is visible immediately; Wrong parameter provider possible 2025-02-03 14:48:54,623 | skyrenderer.utils.time_measurement | INFO: Setup time: 61 ms 2025-02-03 14:48:57,716 | skyrenderer.utils.time_measurement | INFO: Context update time: 3.09 seconds 2025-02-03 14:49:01,623 | skyrenderer.utils.time_measurement | INFO: Key points calculation time: 0 ms 2025-02-03 14:49:01,624 | skyrenderer.utils.time_measurement | INFO: Render time: 3.91 seconds

As you can see, the shaderball is brighter and the shadow of it is weaker. It is due to the fact that currently
two light sources are shining in the scene.

For mor information about specific Lights check classes documentation or Light specific tutorials.

Summary

In this section you have learnt:

  • The main flow for light creation.
  • Common interface for lights.
  • Modification of coordinate specific parameters.
  • Modification of light specific parameters.
  • Usage of ParameterProvider method.
  • Mandatory and optional parameters of Light classes can be checked in the code.