Lights & Shadows

Minimum usage example

The following shows a minimum setup to add lights & shadows to a scene which does not contain an explicit ground mesh.

{
scene: {
parameters: {
// Creates a simple ground which automatically receives shadows
[Parameter.ENVIRONMENT_USEDEFAULT]: true,
},
// ...
},
setup: { /* ... */ },
variants: {
CupToGo: {
glTF: 'https://url.to/3d-file.glb',
lights: {
// Add a simple directional light to the scene
MainLight: {
type: 'directional',
shadowGenerator: { mapSize: 1024 },
},
},
parameters: {
// Set some basic properties for the light
[`MainLight.${Parameter.POSITION}`]: '(1, 1, 0.5)',
[`MainLight.${Parameter.DIRECTION}`]: '(-1, -1, -0.5)',

// Set the "Main" element as a shadow caster
[`Main.${Parameter.CAST_SHADOW}`]: true,
},
elements: {
Main: ['ToGoCup'],
},
},
}
}

Lights & shadows parameter overview

Here is an overview of all parameters which can be used to setup & adjust ViewerLights & shadows:

What is a ViewerLight?

A ViewerLight is a wrapper for Babylon.js lights in context of a Variant to provide all functionalities of a VariantParameterizable. Let's have a look at the following example.

{
scene: { /* ... */ },
setup: {
instances: [
{
name: 'MyStreetInstance',
variant: 'MyStreet',
lazy: false,
parameters: {
[Parameter.VISIBLE]: true,
},
},
],
/* ... */
},
variants: {
'MyStreet': {
/* ... */
lights: {
// fully fledged definition
'MyPoint': {
type: 'baked',
path: 'Point',
},
// shorthand definition for type 'baked'
// Note: 'mini' is the path to the light
'MySpot': 'mini',
},
parameters: {
`MyPoint.${Parameter.VISIBLE}`: true,
`MySpot.${Parameter.VISIBLE}`: false,
},
/* ... */
},
}
}

API usage & parameters

You are able to commit the appropriate Parameters to every light via its Variant.

const my_instance = await viewer.variantInstances.get('MyStreetInstance');
const my_street_variant = my_instance.variant;
const my_point_light = await my_street_variant.getViewerLight('MyPoint');
// Turn off the light!
my_point_light.commitParameter(Parameter.VISIBLE, false);

There's also the possibility to create lights & adjust Parameters via the spec. See the type property of the LightDefinition for available types. Behavior for parameters like DIRECTION, for the HemisphericLight in the following example, are defined by the underlying Babylon.js class. See Parameters with scope ViewerLight for detailed info about available parameters per type.

{
scene: { /* ... */ },
setup: { /* ... */ },
variants: {
'MyStreet': {
/* ... */
lights: {
'MyHemispheric': {
type: 'hemispheric',
},
},
parameters: {
`MyHemispheric.${Parameter.VISIBLE}`: true,
`MyHemispheric.${Parameter.DIRECTION}`: '(0, 1, 0)',
},
/* ... */
},
}
}

Shadow generator

You can pass a ShadowGenerator to each light in the spec. All attributes besides the mandatory mapSize are passed to the constructor of the Babylon.js implementation.

{
scene: { /* ... */ },
setup: { /* ... */ },
variants: {
'MyStreet': {
/* ... */
lights: {
'MySpot': {
type: 'spot',
shadowGenerator: {
mapSize: 1024, // mandatory
usePoissonSampling: true // optional
},
},
},
parameters: {
`MySpot.${Parameter.VISIBLE}`: true,
`MySpot.${Parameter.INTENSITY}`: 5000,
`MySpot.${Parameter.POSITION}`: '(4.5, 5, 3)',
`MySpot.${Parameter.DIRECTION}`: '(-0.5, -1, 0)',
`MySpot.${Parameter.ANGLE}`: Math.PI, // in radians
`MySpot.${Parameter.EXPONENT}`: 1,
},
/* ... */
},
}
}

Casting shadows

Any mesh used in any asset can be defined to cast a shadow from the defined generators. The parameter CAST_SHADOW for each Variant and Element is repsonsible for this feature. Have a look at the following example.

Spec

{
scene: { /* ... */ },
setup: { /* ... */ },
variants: {
'MyStreet': {
elements: {
'MyShadowCaster': ['__root__.some_other_path'],
},
parameters: {
`MyShadowCaster.${Parameter.CAST_SHADOW}`: true,
},
/* ... */
}
}
}

API usage

const my_instance = await viewer.variantInstances.get('MyStreetInstance');
const my_street_variant = my_instance.variant;
const my_element = await my_street_variant.getElement('MyShadowCaster');
my_element.commitParameter(Parameter.CAST_SHADOW, true);

Receiving shadows

Now we have a shadow caster, we need meshes to receive those shadows. The parameter RECEIVE_SHADOWS for each Variant and Element does the trick for you. In the following example, each mesh defined in the Element MyFancyElement will receive shadows from all generators.

Spec

{
scene: { /* ... */ },
setup: { /* ... */ },
variants: {
'MyStreet': {
elements: {
'MyFancyElement': ['__root__.some_path'],
'MyShadowCaster': ['__root__.some_other_path'],
},
parameters: {
`MyFancyElement.${Parameter.RECEIVE_SHADOWS}`: true,
`MyShadowCaster.${Parameter.CAST_SHADOW}`: true,
},
/* ... */
}
}
}

API usage

const my_instance = await viewer.variantInstances.get('MyStreetInstance');
const my_street_variant = my_instance.variant;
const my_element = await my_street_variant.getElement('MyFancyElement');
my_element.commitParameter(Parameter.RECEIVE_SHADOWS, true);

Light sources

By default, defined Elements like above will cast shadows from all defined ViewerLights in the same Variant (naturally taking inheritance into account). To overrule that, you can define the light sources for each shadow casting Element by yourself. Just append the optional fromLights Parameter to the castShadow Parameter (or use CAST_SHADOW_FROM_LIGHTS) with the names of the ViewerLights (comma separated) to the Element like follows.

{
scene: { /* ... */ },
setup: { /* ... */ },
variants: {
'MyStreet': {
lights: {
'MySpot': {
type: 'spot',
shadowGenerator: {
mapSize: 1024,
useBlurExponentialShadowMap: true,
},
},
'MySecondSpot': {
type: 'spot',
shadowGenerator: {
mapSize: 1024,
useBlurExponentialShadowMap: true,
},
},
},
elements: {
'MyFancyElement': ['__root__.some_path'],
'MyShadowCaster': ['__root__.some_other_path'],
},
parameters: {
/* MySpot */
`MySpot.${Parameter.VISIBLE}`: true,
`MySpot.${Parameter.INTENSITY}`: 3000,
`MySpot.${Parameter.POSITION}`: '(4.5, 7, -7)',
`MySpot.${Parameter.DIRECTION}`: '(-90, -180, 120)',
`MySpot.${Parameter.ANGLE}`: 90,
`MySpot.${Parameter.EXPONENT}`: 1,
/* MySecondSpot */
`MySecondSpot.${Parameters.INTENSITY}`: 5000,
`MySecondSpot.${Parameters.POSITION}`: '(4.5, 5, 3)',
`MySecondSpot.${Parameters.DIRECTION}`: '(-90, -180, 0)',
`MySecondSpot.${Parameters.ANGLE}`: 90,
`MySecondSpot.${Parameters.EXPONENT}`: 1,
/* Elements */
`MyFancyElement.${Parameter.RECEIVE_SHADOWS}`: true,
`MyShadowCaster.${Parameter.CAST_SHADOW}`: true,
`MyShadowCaster.${Parameter.CAST_SHADOW_FROM_LIGHTS}`: 'MySpot, MySecondSpot',
},
/* ... */
}
}
}

Generated using TypeDoc