polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
166 lines (152 loc) • 5.04 kB
text/typescript
/**
* Adds a depth of field effect
*
*
*/
import {TypedPostProcessNode, TypedPostNodeContext, PostParamOptions} from './_Base';
import {BokehPass2} from '../../../modules/core/post_process/BokehPass2';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {PerspectiveCamera} from 'three/src/cameras/PerspectiveCamera';
import {PerspectiveCameraObjNode} from '../obj/PerspectiveCamera';
import {CoreGraphNode} from '../../../core/graph/CoreGraphNode';
class DepthOfFieldPostParamsConfig extends NodeParamsConfig {
focalDepth = ParamConfig.FLOAT(10, {
range: [0, 50],
rangeLocked: [true, false],
step: 0.001,
...PostParamOptions,
});
fStep = ParamConfig.FLOAT(10, {
range: [0.1, 22],
rangeLocked: [true, true],
...PostParamOptions,
});
maxBlur = ParamConfig.FLOAT(2, {
range: [0, 10],
rangeLocked: [true, false],
...PostParamOptions,
});
vignetting = ParamConfig.BOOLEAN(0, {
...PostParamOptions,
});
depthBlur = ParamConfig.BOOLEAN(0, {
...PostParamOptions,
});
threshold = ParamConfig.FLOAT(0.5, {
range: [0, 1],
rangeLocked: [true, true],
step: 0.001,
...PostParamOptions,
});
gain = ParamConfig.FLOAT(1, {
range: [0, 100],
rangeLocked: [true, true],
step: 0.001,
...PostParamOptions,
});
bias = ParamConfig.FLOAT(1, {
range: [0, 3],
rangeLocked: [true, true],
step: 0.001,
...PostParamOptions,
});
fringe = ParamConfig.FLOAT(0.7, {
range: [0, 5],
rangeLocked: [true, false],
step: 0.001,
...PostParamOptions,
});
noise = ParamConfig.BOOLEAN(0, {
...PostParamOptions,
});
dithering = ParamConfig.FLOAT(0, {
range: [0, 0.001],
rangeLocked: [true, true],
step: 0.0001,
...PostParamOptions,
});
pentagon = ParamConfig.BOOLEAN(0, {
...PostParamOptions,
});
rings = ParamConfig.INTEGER(3, {
range: [1, 8],
rangeLocked: [true, true],
...PostParamOptions,
});
samples = ParamConfig.INTEGER(4, {
range: [1, 13],
rangeLocked: [true, true],
...PostParamOptions,
});
clearColor = ParamConfig.COLOR([1, 1, 1], {
...PostParamOptions,
});
}
const ParamsConfig = new DepthOfFieldPostParamsConfig();
export class DepthOfFieldPostNode extends TypedPostProcessNode<BokehPass2, DepthOfFieldPostParamsConfig> {
params_config = ParamsConfig;
static type() {
return 'depthOfField';
}
static saturate(x: number) {
return Math.max(0, Math.min(1, x));
}
static linearize(depth: number, near: number, far: number) {
var zfar = far;
var znear = near;
return (-zfar * znear) / (depth * (zfar - znear) - zfar);
}
static smoothstep(near: number, far: number, depth: number) {
var x = this.saturate((depth - near) / (far - near));
return x * x * (3 - 2 * x);
}
protected _create_pass(context: TypedPostNodeContext) {
const camera = context.camera;
if ((camera as PerspectiveCamera).isPerspectiveCamera) {
const camera_node = context.camera_node as PerspectiveCameraObjNode;
if (camera_node) {
const pass = new BokehPass2(this, context.scene, camera_node.object, context.resolution);
this.update_pass(pass);
// TODO: add a dispose to get rid of those connections when the node is deleted
// or when the camera is deleted
// and maybe the graph node should be on the pass itself?
// so that it can be called when we call .dispose() on it?
const core_graph_node = new CoreGraphNode(this.scene(), 'DOF');
core_graph_node.addGraphInput(camera_node.p.near);
core_graph_node.addGraphInput(camera_node.p.far);
core_graph_node.addGraphInput(camera_node.p.fov);
core_graph_node.addGraphInput(this.p.focalDepth);
core_graph_node.addPostDirtyHook('post/DOF', () => {
this.update_pass_from_camera_node(pass, camera_node);
});
return pass;
}
}
}
update_pass_from_camera_node(pass: BokehPass2, camera: PerspectiveCameraObjNode) {
pass.update_camera_uniforms_with_node(this, camera.object);
}
update_pass(pass: BokehPass2) {
pass.bokeh_uniforms['fstop'].value = this.pv.fStep;
pass.bokeh_uniforms['maxblur'].value = this.pv.maxBlur;
pass.bokeh_uniforms['threshold'].value = this.pv.threshold;
pass.bokeh_uniforms['gain'].value = this.pv.gain;
pass.bokeh_uniforms['bias'].value = this.pv.bias;
pass.bokeh_uniforms['fringe'].value = this.pv.fringe;
pass.bokeh_uniforms['dithering'].value = this.pv.dithering;
// booleans
pass.bokeh_uniforms['noise'].value = this.pv.noise ? 1 : 0;
pass.bokeh_uniforms['pentagon'].value = this.pv.pentagon ? 1 : 0;
pass.bokeh_uniforms['vignetting'].value = this.pv.vignetting ? 1 : 0;
pass.bokeh_uniforms['depthblur'].value = this.pv.depthBlur ? 1 : 0;
// debug
pass.bokeh_uniforms['shaderFocus'].value = 0;
pass.bokeh_uniforms['showFocus'].value = 0;
pass.bokeh_uniforms['manualdof'].value = 0;
pass.bokeh_uniforms['focusCoords'].value.set(0.5, 0.5);
pass.bokeh_material.defines['RINGS'] = this.pv.rings;
pass.bokeh_material.defines['SAMPLES'] = this.pv.samples;
pass.bokeh_material.needsUpdate = true;
pass.clear_color.copy(this.pv.clearColor);
}
}