UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

165 lines (153 loc) 4.97 kB
/** * Adds volumetric god rays from a light source. * * */ import {TypeAssert} from './../../poly/Assert'; import {PerspectiveCamera} from 'three'; import {PolyScene} from './../../scene/PolyScene'; import {Vector2, PointLight, DirectionalLight, Object3D} from 'three'; import {TypedPostNode, TypedPostNodeContext, PostParamOptions} from './_Base'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; // import {GodraysPass} from 'three-good-godrays/src/index'; import {GodraysPass} from './utils/GodRaysVolumetric/GodRaysPass'; import {GodRaysVolumetricAcceptedLightType} from './utils/GodRaysVolumetric/utils/AcceptedLightType'; const tmpPointLight = new PointLight(); const tmpDirectionalLight = new DirectionalLight(); for (const l of [tmpPointLight, tmpDirectionalLight]) { l.intensity = 0.0001; l.castShadow = true; l.shadow.mapSize.width = 1024; l.shadow.mapSize.height = 1024; l.shadow.autoUpdate = true; l.shadow.camera.near = 0.1; l.shadow.camera.far = 1000; l.shadow.camera.updateProjectionMatrix(); } enum LightType { POINT = 'point', DIRECTIONAL = 'directional', } const LIGHT_TYPES: LightType[] = [LightType.DIRECTIONAL, LightType.POINT]; function _findLightSource(scene: PolyScene, objectMask: string, lightType: LightType) { let foundLigthObject: GodRaysVolumetricAcceptedLightType | undefined = undefined; const objects: Object3D[] = scene.objectsByMask(objectMask) as Object3D[]; function _isExpectedLightType(object: Object3D): boolean { switch (lightType) { case LightType.POINT: { return (object as PointLight as any).isPointLight; } case LightType.DIRECTIONAL: { return (object as DirectionalLight as any).isDirectionalLight; } } TypeAssert.unreachable(lightType); } for (const object of objects) { if (_isExpectedLightType(object)) { foundLigthObject = object as PointLight; break; } } return foundLigthObject; } class GodRaysVolumetricPostParamsConfig extends NodeParamsConfig { /** @param light type */ lightType = ParamConfig.INTEGER(LIGHT_TYPES.indexOf(LightType.POINT), { menu: { entries: LIGHT_TYPES.map((name, value) => ({name, value})), }, }); /** @param light to emit godrays from. Note that while the mask can resolve multiple objects, only the first light will be used */ lightMask = ParamConfig.STRING('*Light*', { objectMask: true, ...PostParamOptions, }); /** @param color */ color = ParamConfig.COLOR([1, 1, 1], { ...PostParamOptions, }); /** @param density */ density = ParamConfig.FLOAT(0.006, { range: [0, 1], rangeLocked: [true, true], ...PostParamOptions, }); /** @param maxDensity */ maxDensity = ParamConfig.FLOAT(0.67, { range: [0, 1], rangeLocked: [true, true], ...PostParamOptions, }); /** @param distanceAttenuation */ distanceAttenuation = ParamConfig.FLOAT(2, { range: [0, 10], rangeLocked: [true, false], ...PostParamOptions, }); /** @param edgeStrength */ edgeStrength = ParamConfig.INTEGER(2, { range: [0, 10], rangeLocked: [true, false], ...PostParamOptions, }); /** @param edgeRadius */ edgeRadius = ParamConfig.INTEGER(2, { range: [0, 10], rangeLocked: [true, false], ...PostParamOptions, }); } const ParamsConfig = new GodRaysVolumetricPostParamsConfig(); export class GodRaysVolumetricPostNode extends TypedPostNode<GodraysPass, GodRaysVolumetricPostParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return 'godRaysVolumetric'; } private _rendererSize = new Vector2(); override createPass(context: TypedPostNodeContext) { context.renderer.getSize(this._rendererSize); this.scene().threejsScene().add(this._tmpLightSource()); const pass = new GodraysPass(this._tmpLightSource(), context.camera as PerspectiveCamera, { color: this.pv.color, density: this.pv.density, maxDensity: this.pv.maxDensity, distanceAttenuation: this.pv.distanceAttenuation, edgeStrength: this.pv.edgeStrength, edgeRadius: this.pv.edgeRadius, }); this.updatePass(pass); return pass; } override updatePass(pass: GodraysPass) { pass.setParams({ color: this.pv.color, density: this.pv.density, maxDensity: this.pv.maxDensity, distanceAttenuation: this.pv.distanceAttenuation, edgeStrength: this.pv.edgeStrength, edgeRadius: this.pv.edgeRadius, }); // this._tmpLightSource().position.copy(this.pv.center); const lightType = LIGHT_TYPES[this.pv.lightType]; const lightSource = _findLightSource(this.scene(), this.pv.lightMask, lightType); if (lightSource) { lightSource.add(this._tmpLightSource()); // (pass as any).light = lightSource; } else { (pass as any).light = this._tmpLightSource(); } } private _tmpLightSource() { const lightType = LIGHT_TYPES[this.pv.lightType]; switch (lightType) { case LightType.POINT: { return tmpPointLight; } case LightType.DIRECTIONAL: { return tmpDirectionalLight; } } TypeAssert.unreachable(lightType); } }