1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

Merge pull request #53 from troisjs/vanruesc/postprocessing

vanruesc/postprocessing wip
This commit is contained in:
Kevin LEVRON 2021-05-06 21:23:31 +02:00 committed by GitHub
commit 0c7627abb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 300 additions and 0 deletions

View File

@ -0,0 +1,103 @@
import { defineComponent, inject, onMounted, onUnmounted, PropType } from 'vue'
import { LoadingManager } from 'three'
// @ts-ignore
import * as PP from 'postprocessing'
import { EffectPassInjectionKey, EffectPassInterface } from './EffectPass'
type EffectTypes = 'bloom' | 'dof' | 'godrays' | 'smaa'
export default defineComponent({
props: {
type: { type: String as PropType<EffectTypes>, required: true },
options: { type: Object, default: () => ({}) },
onReady: Function,
},
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()
const initEffect = (params: any = undefined) => {
effect = createEffect(effectPass, props.type, props.options, params)
if (!effect) {
console.error('Invalid effect type')
return
}
props.onReady?.(effect)
effectPass.addEffect(effect, effectIndex)
}
onMounted(() => {
if (props.type === 'smaa') {
const smaaImageLoader = new PP.SMAAImageLoader(new LoadingManager())
smaaImageLoader.load(([search, area]: [HTMLImageElement, HTMLImageElement]) => {
initEffect({ smaaSearch: search, smaaArea: area })
})
} else {
initEffect()
}
})
onUnmounted(() => {
if (effect) {
effectPass.removeEffect(effect)
effect.dispose()
}
})
},
render() { return [] },
})
function createEffect(
effectPass: EffectPassInterface,
type: string,
options: Record<string, any>,
assets: any = undefined
): PP.Effect {
let effect
switch (type) {
case 'bloom' :
effect = new PP.BloomEffect(options)
break
case 'dof' :
effect = new PP.DepthOfFieldEffect(effectPass.composer.renderer, options)
break
case 'godrays' :
effect = createGodraysEffect(effectPass, options)
break
case 'smaa' :
effect = createSmaaEffect(options, assets)
break
}
return effect
}
function createSmaaEffect(options: Record<string, any>, assets: any): PP.Pass {
const { smaaSearch, smaaArea } = assets
// TODO : options
const params = [options.preset ?? PP.SMAAPreset.HIGH, options.edgeDetectionMode ?? PP.EdgeDetectionMode.COLOR]
return new PP.SMAAEffect(smaaSearch, smaaArea, ...params)
}
function createGodraysEffect(effectPass: EffectPassInterface, options: Record<string, any>): PP.Pass {
const opts = { ...options }
const { lightSource } = options
if (typeof lightSource !== 'string') {
console.error('Invalid lightSource')
return
}
delete opts.lightSource
const lightSourceComp = effectPass.renderer.$root?.$refs[lightSource] as any
if (!lightSourceComp) {
console.error('Invalid lightSource ref')
return
}
return new PP.GodRaysEffect(effectPass.composer.renderer.camera, lightSourceComp.mesh, opts)
}

View File

@ -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, RendererPublicInterface } from '../../../build/trois'
import { RendererInjectionKey, RendererPublicInterface } from '../../../export'
export interface EffectComposerInterface {
renderer: RendererPublicInterface
composer: PP.EffectComposer
getPassIndex: {(): number}
}
export const ComposerInjectionKey: InjectionKey<EffectComposerInterface> = 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() : []
},
})

View File

@ -0,0 +1,78 @@
import { defineComponent, inject, InjectionKey, onUnmounted, provide } from 'vue'
// @ts-ignore
import * as PP from 'postprocessing'
import { ComposerInjectionKey, EffectComposerInterface } from './EffectComposer'
import { RendererPublicInterface } from '../../../export'
export interface EffectPassInterface {
composer: EffectComposerInterface
renderer: RendererPublicInterface
effectPass: PP.EffectPass
effects: Array<PP.Effect>
getEffectIndex: {(): number}
addEffect: {(effect: PP.Effect, index: number): number}
removeEffect: {(effect: PP.Effect): number}
}
export const EffectPassInjectionKey: InjectionKey<EffectPassInterface> = 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<PP.Effect> = []
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,
renderer: composer.renderer,
effectPass,
effects,
getEffectIndex,
addEffect, removeEffect,
})
},
render() { return this.$slots.default ? this.$slots.default() : [] },
})

View File

@ -0,0 +1,66 @@
import { defineComponent, inject, onUnmounted, PropType } from 'vue'
// @ts-ignore
import * as PP from 'postprocessing'
import { ComposerInjectionKey } from './EffectComposer'
// import { RendererPublicInterface } from '../../../build/trois'
import { RendererPublicInterface } from '../../../export'
type PassTypes = 'render' | 'blur'
export default defineComponent({
props: {
type: { type: String as PropType<PassTypes>, required: true },
options: { type: Object, default: () => ({}) },
// needsSwap: { type: Boolean, default: false },
renderToScreen: { type: Boolean, default: false },
onReady: Function,
},
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
props.onReady?.(pass)
composer.composer.addPass(pass, passIndex)
}
onUnmounted(() => {
if (pass) {
composer.composer.removePass(pass)
pass.dispose()
}
})
initPass()
},
render() { return [] },
})
function createPass(
renderer: RendererPublicInterface,
type: string,
options: Record<string, any>
): PP.Pass {
let pass
switch (type) {
case 'render' :
pass = new PP.RenderPass(renderer.scene, renderer.camera)
break
case 'blur' :
pass = new PP.BlurPass(options)
break
}
return pass
}

View File

@ -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'