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:
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.
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())
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:
- Create / use already existing point (node) in the coordinate space that will be used as a light origin.
- Create Light object in the aforementioned point.
- 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.
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())
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.
renderer_context.get_node("light_LIGHT_NUL").translation = [2, 2, 10]
scene_composer.visualize(scene_composer.get_render())
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.
renderer_context.get_node("light_LIGHT_NUL").translation = [2, 2, 1]
scene_composer.visualize(scene_composer.get_render())
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())
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())
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())
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.