Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow Catcher Script #7299

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
108 changes: 108 additions & 0 deletions scripts/esm/shadow-catcher.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
Script,
Entity,
StandardMaterial,
BLEND_NORMAL,
SHADOW_VSM_16F,
SHADOWUPDATE_REALTIME,
CHUNKAPI_1_70,
Vec2
} from 'playcanvas';

/** @import { AppBase, Entity, Light } from 'playcanvas' */

const endPS = `
litArgs_opacity = mix(light0_shadowIntensity, 0.0, shadow0);
gl_FragColor.rgb = vec3(0.0);
`;

/**
* This script generates a simple shadow plane for any render-able objects within
* it's hierarchy. This means you can attach it to a parent entity and quickly get
* visible ground plane, which helps to stage models.
*/
class ShadowCatcherScript extends Script {
/**
* The shadow distance of the shadow catcher light.
* @type {number}
* @attribute
*/
shadowDistance = 16;

/**
* The VSM blur size of the shadow catcher light.
* @type {number}
* @attribute
*/
blurSize = 32;

/**
* The size of the shadow catcher.
* @type {Vec2}
* @attribute
*/
size = new Vec2(1, 1);

/**
* @type {pc.Entity}
*/
_plane;

/**
* @type {pc.Entity}
*/
_light;

initialize() {
// create shadow catcher material
const material = new StandardMaterial();
material.useSkybox = false;
material.blendType = BLEND_NORMAL;
material.chunks = {
APIVersion: CHUNKAPI_1_70,
endPS: endPS
};
material.update();

// create shadow catcher geometry
this._plane = new Entity('ShadowPlane');
this._plane.addComponent('render', {
type: 'plane',
castShadows: false,
material
});
this._plane.setLocalScale(this.size.x, 1, this.size.y);

// create shadow catcher light
this._light = new Entity('ShadowLight');
this._light.addComponent('light', {
type: 'directional',
castShadows: true,
normalOffsetBias: 0,
shadowBias: 0,
shadowDistance: this.shadowDistance,
shadowResolution: 1024,
shadowType: SHADOW_VSM_16F,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few/most of the light properties should be exposed as attributes, allowing user control. Lets say I want soft shadows.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep valid point

shadowUpdateMode: SHADOWUPDATE_REALTIME,
vsmBlurSize: this.blurSize,
shadowIntensity: 1,
intensity: 0
});

this.app.root.addChild(this._plane);
this.app.root.addChild(this._light);

this.on('destroy', () => {
this._plane.destroy();
this._light.destroy();
material.destroy();
});
}

update() {
this._plane.setLocalScale(this.size.x, 1, this.size.y ?? this.size.x);
this._light.light.vsmBlurSize = this.blurSize;
}
}

export { ShadowCatcherScript };
Loading