From 786555ea5cdff0a39e1499370e962890ea7629a9 Mon Sep 17 00:00:00 2001 From: Kevin Levron Date: Sun, 2 May 2021 01:07:31 +0200 Subject: [PATCH] vanruesc/postprocessing wip --- .../postprocessing/vanruesc/Effect.ts | 80 +++++++++++++++++++ .../postprocessing/vanruesc/EffectComposer.ts | 49 ++++++++++++ .../postprocessing/vanruesc/EffectPass.ts | 75 +++++++++++++++++ .../postprocessing/vanruesc/Pass.ts | 61 ++++++++++++++ .../postprocessing/vanruesc/index.ts | 4 + 5 files changed, 269 insertions(+) create mode 100644 src/components/postprocessing/vanruesc/Effect.ts create mode 100644 src/components/postprocessing/vanruesc/EffectComposer.ts create mode 100644 src/components/postprocessing/vanruesc/EffectPass.ts create mode 100644 src/components/postprocessing/vanruesc/Pass.ts create mode 100644 src/components/postprocessing/vanruesc/index.ts diff --git a/src/components/postprocessing/vanruesc/Effect.ts b/src/components/postprocessing/vanruesc/Effect.ts new file mode 100644 index 0000000..0613ce8 --- /dev/null +++ b/src/components/postprocessing/vanruesc/Effect.ts @@ -0,0 +1,80 @@ +import { defineComponent, inject, onUnmounted, PropType } from 'vue' +import { LoadingManager } from 'three' +// @ts-ignore +import * as PP from 'postprocessing' +// import { RendererInterface } from '../../../build/trois' +import { RendererInterface } from '../../../export' +import { EffectPassInjectionKey } from './EffectPass' + +type EffectTypes = 'bloom' | 'dof' | 'smaa' + +export default defineComponent({ + props: { + type: { type: String as PropType, required: true }, + options: { type: Object, default: () => ({}) }, + }, + setup(props) { + const effectPass = inject(EffectPassInjectionKey) + if (!effectPass) { + console.error('EffectPass not found') + return + } + + let effect: undefined | PP.Effect // not useful + const effectIndex = effectPass.getEffectIndex() + // console.log(effectIndex) + + const initEffect = (params: any = undefined) => { + effect = createEffect(effectPass.composer.renderer, props.type, props.options, params) + if (!effect) { + console.error('Invalid effect type') + return + } + effectPass.addEffect(effect, effectIndex) + } + + onUnmounted(() => { + if (effect) { + effectPass.removeEffect(effect) + effect.dispose() + } + }) + + if (props.type === 'smaa') { + const smaaImageLoader = new PP.SMAAImageLoader(new LoadingManager()) + smaaImageLoader.load(([search, area]: [HTMLImageElement, HTMLImageElement]) => { + initEffect({ smaaSearch: search, smaaArea: area }) + }) + } else { + initEffect() + } + }, + render() { return [] }, +}) + +function createEffect( + renderer: RendererInterface, + type: string, + options: Record, + assets: any = undefined +): PP.Effect { + let effect + switch (type) { + case 'bloom' : + effect = new PP.BloomEffect(options) + break + case 'dof' : + effect = new PP.DepthEffect(renderer, options) + break + case 'smaa' : + effect = createSmaaEffect(options, assets) + break + } + return effect +} + +function createSmaaEffect(options: Record, assets: any): PP.Pass { + const { smaaSearch, smaaArea } = assets + // TODO : options + return new PP.SMAAEffect(smaaSearch, smaaArea) +} diff --git a/src/components/postprocessing/vanruesc/EffectComposer.ts b/src/components/postprocessing/vanruesc/EffectComposer.ts new file mode 100644 index 0000000..7b0f74d --- /dev/null +++ b/src/components/postprocessing/vanruesc/EffectComposer.ts @@ -0,0 +1,49 @@ +import { defineComponent, inject, InjectionKey, onUnmounted, provide } from 'vue' +import { Clock } from 'three' +// @ts-ignore +import * as PP from 'postprocessing' +// import { RendererInjectionKey, RendererInterface } from '../../../build/trois' +import { RendererInjectionKey, RendererInterface } from '../../../export' + +export interface EffectComposerInterface { + renderer: RendererInterface + composer: PP.EffectComposer + getPassIndex: {(): number} +} + +export const ComposerInjectionKey: InjectionKey = Symbol('Composer') + +export default defineComponent({ + setup() { + const renderer = inject(RendererInjectionKey) + if (!renderer) { + console.error('Renderer not found') + return + } + + const composer = new PP.EffectComposer(renderer.renderer) + const clock = new Clock() + const render = () => { composer.render(clock.getDelta()) } + const setSize = () => { composer.setSize(renderer.size.width, renderer.size.height) } + + let passIndex = 0 + const getPassIndex = () => { return passIndex++ } + + renderer.onInit(() => { + renderer.renderer.autoClear = false + renderer.renderFn = render + setSize() + renderer.onResize(setSize) + }) + + onUnmounted(() => { + renderer.offResize(setSize) + composer.dispose() + }) + + provide(ComposerInjectionKey, { renderer, composer, getPassIndex }) + }, + render() { + return this.$slots.default ? this.$slots.default() : [] + }, +}) diff --git a/src/components/postprocessing/vanruesc/EffectPass.ts b/src/components/postprocessing/vanruesc/EffectPass.ts new file mode 100644 index 0000000..6b4bc67 --- /dev/null +++ b/src/components/postprocessing/vanruesc/EffectPass.ts @@ -0,0 +1,75 @@ +import { defineComponent, inject, InjectionKey, onUnmounted, provide } from 'vue' +// @ts-ignore +import * as PP from 'postprocessing' +import { ComposerInjectionKey, EffectComposerInterface } from './EffectComposer' + +export interface EffectPassInterface { + composer: EffectComposerInterface + effectPass: PP.EffectPass + effects: Array + getEffectIndex: {(): number} + addEffect: {(effect: PP.Effect, index: number): number} + removeEffect: {(effect: PP.Effect): number} +} + +export const EffectPassInjectionKey: InjectionKey = Symbol('Composer') + +export default defineComponent({ + props: { + // needsSwap: { type: Boolean, default: false }, + renderToScreen: { type: Boolean, default: false }, + }, + setup() { + const composer = inject(ComposerInjectionKey) + if (!composer) { + console.error('Composer not found') + return {} + } + + const passIndex = composer.getPassIndex() + let effectPass: PP.EffectPass + + const effects: Array = [] + let effectIndex = 0 + const getEffectIndex = () => { return effectIndex++ } + + const refreshEffectPass = () => { + // we have to recreate EffectPass (modifying effectPass.effects don't work) + if (effectPass) { + composer.composer.removePass(effectPass) + effectPass.dispose() + } + effectPass = new PP.EffectPass(composer.renderer.camera, ...effects) + composer.composer.addPass(effectPass, passIndex) + } + + const addEffect = (effect: PP.Effect, index: number) => { + effects.splice(index, 1, effect) + refreshEffectPass() + } + + const removeEffect = (effect: PP.Effect) => { + const index = effects.indexOf(effect) + if (index >= 0) { + effects.splice(index, 1) + refreshEffectPass() + } + } + + onUnmounted(() => { + if (effectPass) { + composer.composer.removePass(effectPass) + effectPass.dispose() + } + }) + + provide(EffectPassInjectionKey, { + composer, + effectPass, + effects, + getEffectIndex, + addEffect, removeEffect, + }) + }, + render() { return this.$slots.default ? this.$slots.default() : [] }, +}) diff --git a/src/components/postprocessing/vanruesc/Pass.ts b/src/components/postprocessing/vanruesc/Pass.ts new file mode 100644 index 0000000..61b3d21 --- /dev/null +++ b/src/components/postprocessing/vanruesc/Pass.ts @@ -0,0 +1,61 @@ +import { defineComponent, inject, onUnmounted, PropType } from 'vue' +// @ts-ignore +import * as PP from 'postprocessing' +import { ComposerInjectionKey } from './EffectComposer' +// import { RendererInterface } from '../../../build/trois' +import { RendererInterface } from '../../../export' + +type PassTypes = 'render' + +export default defineComponent({ + props: { + type: { type: String as PropType, required: true }, + options: { type: Object, default: () => ({}) }, + // needsSwap: { type: Boolean, default: false }, + renderToScreen: { type: Boolean, default: false }, + }, + setup(props) { + const composer = inject(ComposerInjectionKey) + if (!composer || !composer.renderer) { + console.error('Composer/Renderer not found') + return {} + } + + let pass: undefined | PP.Pass + const passIndex = composer.getPassIndex() + + const initPass = (params: any = undefined) => { + pass = createPass(composer.renderer, props.type, props.options) + if (!pass) { + console.error('Invalid pass type') + return + } + pass.renderToScreen = props.renderToScreen + composer.composer.addPass(pass, passIndex) + } + + onUnmounted(() => { + if (pass) { + composer.composer.removePass(pass) + pass.dispose() + } + }) + + initPass() + }, + render() { return [] }, +}) + +function createPass( + renderer: RendererInterface, + type: string, + options: Record +): PP.Pass { + let pass + switch (type) { + case 'render' : + pass = new PP.RenderPass(renderer.scene, renderer.camera) + break + } + return pass +} diff --git a/src/components/postprocessing/vanruesc/index.ts b/src/components/postprocessing/vanruesc/index.ts new file mode 100644 index 0000000..98b1c8e --- /dev/null +++ b/src/components/postprocessing/vanruesc/index.ts @@ -0,0 +1,4 @@ +export { default as EffectComposer } from './EffectComposer' +export { default as Pass } from './Pass' +export { default as EffectPass } from './EffectPass' +export { default as Effect } from './Effect'