@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
154 lines (144 loc) • 4.76 kB
text/typescript
/**
* Adds god rays from a source object.
*
*
*/
import {isArray} from './../../../core/Type';
import {PolyScene} from './../../scene/PolyScene';
import {Points} from 'three';
import {BLEND_FUNCTION_MENU_OPTIONS} from './../../../core/post/BlendFunction';
import {Vector2, Mesh, BufferGeometry, MeshBasicMaterial, Object3D, Material, Group} from 'three';
import {TypedPostNode, TypedPostNodeContext, PostParamOptions} from './_Base';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {BlendFunction, GodRaysEffect, EffectPass, KernelSize} from 'postprocessing';
import {KERNEL_SIZES, KERNEL_SIZE_MENU_OPTIONS} from '../../../core/post/KernelSize';
const tmpParent = new Group();
const tmpLightSource = new Mesh(new BufferGeometry(), new MeshBasicMaterial());
tmpParent.add(tmpLightSource);
function _updateLightSourceMaterial(material: Material) {
material.depthWrite = false;
material.transparent = true;
}
function _findLightSource(scene: PolyScene, objectMask: string) {
let foundObject: Mesh | Points | undefined = undefined;
const objects: Object3D[] = scene.objectsByMask(objectMask) as Object3D[];
for (const object of objects) {
if ((object as Mesh).isMesh || (object as Points).isPoints) {
foundObject = object as Mesh;
break;
}
}
return foundObject;
}
class GodRaysPostParamsConfig extends NodeParamsConfig {
/** @param objects to emit godrays from. Note that while the mask can resolve multiple objects, only the first mesh or points will be used */
objectMask = ParamConfig.STRING('*geo1*', {
objectMask: true,
});
/** @param samples */
samples = ParamConfig.INTEGER(60, {
range: [1, 128],
rangeLocked: [true, false],
...PostParamOptions,
});
/** @param density */
density = ParamConfig.FLOAT(0.96, {
range: [0, 1],
rangeLocked: [true, true],
...PostParamOptions,
});
/** @param decay */
decay = ParamConfig.FLOAT(0.9, {
range: [0, 1],
rangeLocked: [true, true],
...PostParamOptions,
});
/** @param weight */
weight = ParamConfig.FLOAT(0.4, {
range: [0, 1],
rangeLocked: [true, true],
...PostParamOptions,
});
/** @param exposure */
exposure = ParamConfig.FLOAT(0.6, {
range: [0, 1],
rangeLocked: [true, true],
...PostParamOptions,
});
/** @param blur */
blur = ParamConfig.BOOLEAN(1, {
...PostParamOptions,
});
/** @param kernel size */
kernelSize = ParamConfig.INTEGER(KernelSize.LARGE, {
visibleIf: {blur: 1},
...PostParamOptions,
...KERNEL_SIZE_MENU_OPTIONS,
});
/** @param resolutionScale */
resolutionScale = ParamConfig.FLOAT(0.5, {
...PostParamOptions,
});
/** @param effect opacity */
opacity = ParamConfig.FLOAT(1, {
range: [0, 1],
rangeLocked: [true, false],
...PostParamOptions,
});
/** @param render mode */
blendFunction = ParamConfig.INTEGER(BlendFunction.SCREEN, {
...PostParamOptions,
...BLEND_FUNCTION_MENU_OPTIONS,
});
}
const ParamsConfig = new GodRaysPostParamsConfig();
export class GodRaysPostNode extends TypedPostNode<EffectPass, GodRaysPostParamsConfig> {
override paramsConfig = ParamsConfig;
static override type() {
return 'godRays';
}
private _rendererSize = new Vector2();
override createPass(context: TypedPostNodeContext) {
context.renderer.getSize(this._rendererSize);
const effect = new GodRaysEffect(context.camera, tmpLightSource, {
blendFunction: BlendFunction.SCREEN,
kernelSize: KERNEL_SIZES[this.pv.kernelSize],
blur: this.pv.blur,
samples: this.pv.samples,
density: this.pv.density,
decay: this.pv.decay,
weight: this.pv.weight,
exposure: this.pv.exposure,
});
const pass = new EffectPass(context.camera, effect);
this.updatePass(pass);
return pass;
}
override updatePass(pass: EffectPass) {
const effect = (pass as any).effects[0] as GodRaysEffect;
effect.godRaysMaterial.samples = this.pv.samples;
effect.godRaysMaterial.density = this.pv.density;
effect.godRaysMaterial.decay = this.pv.decay;
effect.godRaysMaterial.weight = this.pv.weight;
effect.godRaysMaterial.exposure = this.pv.exposure;
effect.blur = this.pv.blur;
(effect.blurPass.blurMaterial as any).kernelSize = KERNEL_SIZES[this.pv.kernelSize];
effect.blurPass.resolution.scale = this.pv.resolutionScale;
effect.blendMode.opacity.value = this.pv.opacity;
effect.blendMode.blendFunction = this.pv.blendFunction;
const lightSource = _findLightSource(this.scene(), this.pv.objectMask);
if (lightSource) {
(effect as any).lightSource = lightSource;
const material = lightSource.material;
if (isArray(material)) {
for (const m of material) {
_updateLightSourceMaterial(m);
}
} else {
_updateLightSourceMaterial(material);
}
} else {
(effect as any).lightSource = tmpLightSource;
}
}
}