Skip to content

Shadow

Shadow is a way to add depth and realism to a scene or model. Shadows can show the scale and position of objects, making them look more three-dimensional. In the engine, light sources can cast shadows of objects onto other parts of themselves or nearby scenes. These shadows can be real-time or static.

Shadow Configuration

Shadow is implemented by light sources. Currently, DirectLight, PointLight, and SpotLight in the engine can produce shadows. When rendering shadow effects, just set the castShadow property of the corresponding light source to true.

ts
let lightObj = new Object3D();
// Set the position of the light
lightObj.x = 0;
lightObj.y = 0;
lightObj.z = 0;
// Set the angle of the light and rotate 45 degrees around the X axis
lightObj.rotationX = 45;
lightObj.rotationY = 0;
lightObj.rotationZ = 0;
// Add a direct light component and enable shadows
let lc = lightObj.addComponent(DirectLight);
lc.castShadow = true; // 默认为 false
lc.intensity = 5;
scene.addChild(lightObj);

If you want the light to cast shadows on a certain object, you need to add a MeshRenderer component to the object, and set the castShadow property of the component to true.

ts
// Create a box to generate shadows
let castShadowObj = new Object3D();
let mr1 = castShadowObj.addComponent(MeshRenderer);
mr1.geometry = new BoxGeometry();
mr1.material = new LitMaterial();
mr1.castShadow = true
scene.addChild(castShadowObj);

Then you need to add a MeshRenderer component to the object that receives the shadow, and set the receiveShadow property of the component to true.

ts
// Create a plane to receive shadows
let receiveShadowObj = new Object3D();
let mr2 = receiveShadowObj.addComponent(MeshRenderer);
mr2.geometry  = new PlaneGeometry(1000,1000);
mr2.material =new LitMaterial();
mr2.receiveShadow = true;
scene.addChild(receiveShadowObj);

In this way, you can see a box in the scene, which casts a shadow under the parallel light and casts it on the plane:

Direct light shadow


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = 100;
Engine3D.setting.shadow.type = 'HARD';

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(-45, -45, 100, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//DirectLight
{
    let obj = new Object3D();
    obj.rotationX = 45;
    obj.rotationY = 0;
    obj.rotationZ = 0;
    let light = obj.addComponent(DirectLight);
    scene3D.addChild(obj);
    // enable light shadow
    light.castShadow = true;
    light.intensity = 30;
    scene3D.addChild(obj);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    castShadowObj.rotationY = 45;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

Point light shadow


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = 100;
Engine3D.setting.shadow.pointShadowBias = 0.0001;
Engine3D.setting.shadow.type = 'HARD';

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//PointLight
{
    let obj = new Object3D();
    let light = obj.addComponent(PointLight);
    scene3D.addChild(obj);
    obj.x = -15;
    obj.y = 30;
    obj.z = -20;
    obj.rotationX = 0;
    obj.rotationY = 0;
    obj.rotationZ = 0;
    light.intensity = 15;
    light.range = 100;
    // enable light shadow
    light.castShadow = true;
    light.debug();
    light.debugDraw(true);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

Spot light shadow


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = 100;
Engine3D.setting.shadow.pointShadowBias = 0.0001;
Engine3D.setting.shadow.type = 'HARD';

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -45, 150, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//SpotLight
{
    let obj = new Object3D();
    let light = obj.addComponent(SpotLight);
    scene3D.addChild(obj);
    obj.x = -30;
    obj.y = 15;
    obj.z = 40;
    obj.rotationX = 0;
    obj.rotationY = 145;
    obj.rotationZ = 0;
    light.intensity = 50;
    light.range = 150;
    light.outerAngle = 110;
    light.innerAngle = 30;
    // enable light shadow
    light.castShadow = true;
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

Shadow Type

You can determine the shadow performance by setting the shadow type.These are the types of shadows currently supported:

  • HARD: Hard shadow is a sharper shadow, usually used for shadows close to the object.
  • SOFT: Soft shadow is a softer, blurred shadow, and the edge of the shadow is blurred. It is usually used for shadows far from the object.
  • PCF: PCF (Percentage-Closer Filtering) is a common soft shadow processing algorithm. By sampling the current pixel and the surrounding shadow depth and averaging them by distance, a soft shadow effect is artificially forged. The engine currently uses this type of shadow by default.

Configuration shadow type:

ts
Engine3D.setting.shadow.type = 'HARD'; // PCF by default

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Color } from '@orillusion/core';
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry } from '@orillusion/core';
import * as dat from 'dat.gui';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = 100;
Engine3D.setting.shadow.pointShadowBias = 0.0001;
Engine3D.setting.shadow.type = sessionStorage._shadow_type || 'HARD';

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -45, 50, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//PointLight
{
    let obj = new Object3D();
    let light = obj.addComponent(PointLight);
    scene3D.addChild(obj);
    obj.x = -15;
    obj.y = 30;
    obj.z = -20;
    obj.rotationX = 0;
    obj.rotationY = 0;
    obj.rotationZ = 0;
    light.intensity = 30;
    light.radius = 1;
    light.range = 100;
    light.castShadow = true;
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

let gui = new dat.GUI();
gui.add(Engine3D.setting.shadow, 'type', ['PCF', 'SOFT', 'HARD']).onChange((v) => {
    sessionStorage._shadow_type = v;
    location.reload();
});

Shadow Bound

The engine's "setting" provides control parameters for the shadow size, which can control the texture size and area size used by the shadow in different scenes.

The relevant attributes are as follows:

AttributeTypeDescription
shadowBoundNumberShadow area range
shadowSizeNumberParallel light shadow map size, default 1024
pointShadowSizeNumberPoint light Shadow Map Size Size, default 1024

Shadow map size (shadowSize, pointShadowSize) directly affects the final shadow quality, and the smaller the value, the lower the performance overhead, and the more obvious the shadow jagged feeling.

The shadowBound parameter controls the size of the illuminated shadow area in the scene. The larger the area, the size of the shadow map should also be increased appropriately. When a large area is cast on a small shadow map, it will also cause a significant shadow jagging.

ts
Engine3D.setting.shadow.shadowBound = 100

Set the parallel light shadow map size:

ts
Engine3D.setting.shadow.shadowSize = 2048

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core';
import * as dat from 'dat.gui';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = sessionStorage._shadowBound || 100;
Engine3D.setting.shadow.type = 'HARD';
Engine3D.setting.shadow.shadowSize = sessionStorage._shadowSize || 512;

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(-45, -45, 50, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//DirectLight
{
    let obj = new Object3D();
    obj.rotationX = 45;
    obj.rotationY = 0;
    obj.rotationZ = 0;
    let light = obj.addComponent(DirectLight);
    scene3D.addChild(obj);
    // enable light shadow
    light.castShadow = true;
    light.intensity = 30;
    scene3D.addChild(obj);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    castShadowObj.rotationY = 45;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    castShadowObj.rotationY = 45;
    castShadowObj.x = 30;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(0, 1, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

let gui = new dat.GUI();
gui.add(Engine3D.setting.shadow, 'shadowBound', [30, 50, 100, 500]).onChange((v) => {
    sessionStorage._shadowBound = v;
    location.reload();
});
gui.add(Engine3D.setting.shadow, 'shadowSize', [128, 256, 512, 1024, 2048]).onChange((v) => {
    sessionStorage._shadowSize = v;
    location.reload();
});

Set point light shadow map size Size:

ts
Engine3D.setting.shadow.pointShadowSize = 2048

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, Vector3, AtmosphericComponent, View3D, LitMaterial, BoxGeometry, MeshRenderer, UnLitMaterial, SphereGeometry, DirectLight, PointLight, SpotLight, HoverCameraController, PlaneGeometry, Color } from '@orillusion/core';
import * as dat from 'dat.gui';

// shadow setting
Engine3D.setting.shadow.autoUpdate = true;
Engine3D.setting.shadow.shadowBound = sessionStorage._shadowBound || 100;
Engine3D.setting.shadow.type = 'HARD';
Engine3D.setting.shadow.pointShadowSize = sessionStorage._pointShadowSize || 512;
Engine3D.setting.shadow.pointShadowBias = 0.0001;

await Engine3D.init({
    canvasConfig: { devicePixelRatio: 1 }
});
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(-45, -45, 50, new Vector3(0, 0, 0));
scene3D.addChild(cameraObj);

//PointLight
{
    let obj = new Object3D();
    obj.y = 50;
    obj.z = -30;
    obj.rotationX = 45;
    obj.rotationY = 0;
    obj.rotationZ = 0;
    let light = obj.addComponent(PointLight);
    scene3D.addChild(obj);
    // enable light shadow
    light.castShadow = true;
    light.intensity = 30;
    scene3D.addChild(obj);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    castShadowObj.rotationY = 45;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(1, 0, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a box as shadow source
{
    let castShadowObj = new Object3D();
    castShadowObj.y = 5;
    castShadowObj.rotationY = 45;
    castShadowObj.x = 30;
    let mr = castShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(10, 10, 10);
    mr.material = new LitMaterial();
    mr.material.baseColor = new Color(0, 1, 0);
    mr.castShadow = true;
    scene3D.addChild(castShadowObj);
}

// create a plane to receive shadow
{
    let receiveShadowObj = new Object3D();
    let mr = receiveShadowObj.addComponent(MeshRenderer);
    mr.geometry = new BoxGeometry(2000, 1, 2000);
    mr.material = new LitMaterial();
    mr.receiveShadow = true;
    scene3D.addChild(receiveShadowObj);
}
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

let gui = new dat.GUI();
gui.add(Engine3D.setting.shadow, 'shadowBound', [30, 50, 100, 500]).onChange((v) => {
    sessionStorage._shadowBound = v;
    location.reload();
});
gui.add(Engine3D.setting.shadow, 'pointShadowSize', [128, 256, 512, 1024, 2048]).onChange((v) => {
    sessionStorage._pointShadowSize = v;
    location.reload();
});

Cascaded Shadow Maps (CSM)

Often used to support better shadow rendering effect in large scenes, the shadow in the cone is divided into four levels, and the appropriate shadow map is selected according to the current shadow level in the shading process stage, which can obtain a more accurate shadow effect covering the full scope of the cone. After the use of CSM shadow, it can solve the problem that the shadow Bound range is too large, and the pixel density of the shadow is not enough, resulting in too serious Mosaic; The scope is small, the shadow area is too small, and distant objects lose their shadows.

enable CSM shadow

ts
let mainCamera:Camera3D;
mainCamera.enableCSM = true;

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Scene3D, HoverCameraController, Engine3D, AtmosphericComponent, Object3D, Camera3D, Vector3, View3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, BoxGeometry, CameraUtil, SphereGeometry, Color, Object3DUtil, BlendMode } from '@orillusion/core';
import * as dat from 'dat.gui';

//sample of csm
class Sample_CSM {
    scene: Scene3D;
    view: View3D;
    light: DirectLight;
    boxRenderer: MeshRenderer;
    viewCamera: Camera3D;
    GUIHelp: dat.GUI;
    async run() {
        Engine3D.setting.shadow.autoUpdate = true;
        Engine3D.setting.shadow.shadowSize = 1024;
        await Engine3D.init({
            renderLoop: () => {
                this.loop();
            }
        });
        this.GUIHelp = new dat.GUI();

        this.scene = new Scene3D();
        let sky = this.scene.addComponent(AtmosphericComponent);

        // init camera3D
        let mainCamera = CameraUtil.createCamera3D(undefined, this.scene);
        mainCamera.perspective(60, Engine3D.aspect, 1, 5000.0);
        //set camera data
        mainCamera.object3D.z = -15;
        mainCamera.object3D.addComponent(HoverCameraController).setCamera(-15, -35, 200);

        sky.relativeTransform = this.initLight('mainLight', 30, 45);
        this.initLight('subLight', 10, 10);
        this.initScene();

        let view = new View3D();
        view.scene = this.scene;
        view.camera = mainCamera;
        this.view = view;
        this.viewCamera = mainCamera;

        mainCamera.enableCSM = true;

        let f = this.GUIHelp.addFolder('CSM');
        f.add(mainCamera, 'enableCSM');
        f.add(Engine3D.setting.shadow, 'csmScatteringExp', 0.5, 1.0, 0.01);
        f.add(Engine3D.setting.shadow, 'csmMargin', 0.01, 0.5, 0.01);
        f.add(Engine3D.setting.shadow, 'csmAreaScale', 0.1, 1, 0.01);
        f.open();
        Engine3D.startRenderView(view);
    }

    // create direction light
    private initLight(name: string, intensity: number, rotY: number) {
        let lightObj3D = new Object3D();
        lightObj3D.name = name;
        lightObj3D.rotationX = 46;
        lightObj3D.rotationY = 62 + rotY;
        lightObj3D.rotationZ = 0;
        let sunLight = lightObj3D.addComponent(DirectLight);
        sunLight.intensity = intensity;
        sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553);
        sunLight.castShadow = true;

        this.renderDirLight(sunLight, true, name);
        this.scene.addChild(lightObj3D);
        this.light = sunLight;
        return sunLight.transform;
    }

    public renderDirLight(light: DirectLight, open: boolean = true, name?: string) {
        name ||= 'DirectLight';
        let f = this.GUIHelp.addFolder(name);
        f.add(light, 'enable');
        f.open();
    }

    initScene() {
        {
            let obj = new Object3D();
            let mr = obj.addComponent(MeshRenderer);
            mr.geometry = new BoxGeometry(20, 100, 20);
            mr.material = new LitMaterial();
            this.scene.addChild(obj);
        }

        this.createBox();
        {
            let mat = new LitMaterial();
            mat.baseMap = Engine3D.res.grayTexture;
            let floor = new Object3D();
            let mr = floor.addComponent(MeshRenderer);
            mr.geometry = new BoxGeometry(10000, 1, 10000);
            mr.material = mat;
            this.scene.addChild(floor);
        }

        for (let i = 0; i < 1000; i++) {
            let item = Object3DUtil.GetSingleSphere(4, 0.6, 0.4, 0.2);
            let angle = (Math.PI * 4 * i) / 50;
            item.x = Math.sin(angle) * (50 + i ** 1.4);
            item.z = Math.cos(angle) * (50 + i ** 1.4);
            item.y = 4;
            let scale = (i ** 1.4 * 5 + 1000) / 1000;
            item.scaleX = item.scaleZ = scale;
            item.scaleY = scale * 5;
            this.scene.addChild(item);
        }
    }

    createBox() {
        let box = new Object3D();
        let geom = new BoxGeometry(1, 1, 1);
        let material = new LitMaterial();
        // material.transparent = true;
        // material.shaderState.depthWriteEnabled = false
        material.blendMode = BlendMode.NORMAL;
        material.cullMode = 'front';
        material.baseColor = new Color(0.2, 0.2, 0, 0.1);
        let renderer = box.addComponent(MeshRenderer);
        renderer.material = material;
        renderer.geometry = geom;
        // this.scene.addChild(box);
        this.boxRenderer = renderer;
    }

    private _shadowPos: Vector3 = new Vector3();
    private _shadowCameraTarget: Vector3 = new Vector3();
    loop() {
        let viewCamera = this.viewCamera;
        let light = this.light;
        let view = this.view;
        if (!this.boxRenderer || !this.viewCamera.csm) return;

        let csmBound = this.viewCamera.csm.children[0].bound;
        //update box
        let size = this.viewCamera.getCSMShadowWorldExtents(0) * 2;
        this.boxRenderer.object3D.scaleX = size;
        this.boxRenderer.object3D.scaleY = size;
        this.boxRenderer.object3D.scaleZ = this.viewCamera.csm.children[0].shadowCamera.far;

        this.boxRenderer.object3D.localRotation = light.transform.localRotation;
        this.boxRenderer.object3D.localPosition = csmBound.center;

        // light direction
        this._shadowPos.copy(light.direction).normalize(viewCamera.far);
        csmBound.center.add(this._shadowPos, this._shadowCameraTarget);
        csmBound.center.subtract(this._shadowPos, this._shadowPos);
        view.graphic3D.drawLines('shadowLine', [this._shadowPos, this._shadowCameraTarget], new Color(1, 1, 0, 1));
    }
}

new Sample_CSM().run();

Shadow Attribute

AttributeTypeDescription
enableBooleanWhether to enable shadow, default false
typeStringShadow type, default PCF
shadowQualityNumberShadow rendering quality
shadowBoundNumberShadow area range
pointShadowBiasNumberOffset value of point light and spotlight shadow
shadowSizeNumberSize of parallel light shadow map, default 2048, the smaller the number, the lower the performance cost, but the more obvious the shadow aliasing
pointShadowSizeNumberSize of point light shadow map, default 1024
autoUpdateBooleanWhether to automatically update the shadow, default false
csmMarginNumberSet the transition range for different levels of shadows and adjust it in the 0-1 range
csmScatteringExpNumberFine-tune the range of shades at each level to suit different scene needs
csmAreaScaleNumberFine-tune the maximum range that the shadow can cover, adjusted in the range of 0.0-1