Skip to content

Commit

Permalink
feat(post-processing): refactor of existing effects (#64)
Browse files Browse the repository at this point in the history
* chore: refactored depth of field

* feat: made effects react to main camera changes

* chore: refactored glitch effect

* chore: added comments for util functions

* chore: refactored bloom effect component

* chore: removed prop that is not reactive from bloom demo

* chore: refactored outline effect component

* chore: removed obsolete todo comment

---------

Co-authored-by: Tino Koch <tino.koch@xpoli.eu>
  • Loading branch information
Tinoooo and Tino Koch committed Oct 12, 2023
1 parent 9264231 commit a2afd3f
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 228 deletions.
2 changes: 1 addition & 1 deletion docs/guide/effects/bloom.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { EffectComposer, Bloom } from '@tresjs/post-processing'
| `kernelSize` | The kernel size. | `KernelSize.LARGE` |
| `luminanceThreshold` | The luminance threshold. Raise this value to mask out darker elements in the scene. Range is [0, 1]. | `0.9` |
| `luminanceSmoothing` | Controls the smoothness of the luminance threshold. Range is [0, 1]. | `0.025` |
| `mipMapBlur` | Enables mip map blur. (UnrealBloom) | `false` |
| `mipMapBlur` | Enables mip map blur (UnrealBloom). This prop is not reactive. | `false` |

## Further Reading
see [postprocessing docs](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/BloomEffect.js~BloomEffect.html)
7 changes: 4 additions & 3 deletions playground/src/components/UnrealBloom.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const gl = {
const bloomParams = reactive({
luminanceThreshold: 0.2,
luminanceSmoothing: 0.3,
mipmapBlur: true,
intensity: 4.0,
blendFunction: BlendFunction.ADD,
})
Expand All @@ -26,7 +25,6 @@ const { pane } = useTweakPane()
pane.addInput(bloomParams, 'luminanceThreshold', { min: 0, max: 1 })
pane.addInput(bloomParams, 'luminanceSmoothing', { min: 0, max: 1 })
pane.addInput(bloomParams, 'mipmapBlur')
pane.addInput(bloomParams, 'intensity', { min: 0, max: 10 })
const materialRef = ref(null)
Expand Down Expand Up @@ -77,7 +75,10 @@ onMounted(() => {
/>
<Suspense>
<EffectComposer :depth-buffer="true">
<Bloom v-bind="bloomParams" />
<Bloom
v-bind="bloomParams"
mipmap-blur
/>
</EffectComposer>
</Suspense>
</TresCanvas>
Expand Down
44 changes: 44 additions & 0 deletions src/core/composables/effect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Effect } from 'postprocessing'
import { EffectPass } from 'postprocessing'
import { inject, onUnmounted, shallowRef, watchEffect, defineExpose } from 'vue'
import { useTresContext } from '@tresjs/core'
import { effectComposerInjectionKey } from '../injectionKeys'

export const useEffect = <T extends Effect>( newEffectFunction: () => T) => {
const composer = inject(effectComposerInjectionKey)
const pass = shallowRef<EffectPass | null>(null)
const effect = shallowRef<T | null>(null)

const { scene, camera } = useTresContext()

watchEffect(() => {
if (!camera.value || !effect?.value) return

effect.value.mainCamera = camera.value
})

let unwatch = () => {} // seperate declaration prevents error in HMR

unwatch = watchEffect(() => {
if (!camera.value || !composer?.value || !scene.value) return

unwatch()
if (effect.value) return

effect.value = newEffectFunction()
pass.value = new EffectPass(camera.value, effect.value)

composer.value.addPass(pass.value)
})

onUnmounted(() => {
if (pass.value) composer?.value?.removePass(pass.value)
effect.value?.dispose()
pass.value?.dispose()
})

return {
pass,
effect,
}
}
64 changes: 21 additions & 43 deletions src/core/effects/Bloom.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<script setup lang="ts">
import { useTresContext } from '@tresjs/core'
import { h, inject, onUnmounted, shallowRef, watchEffect } from 'vue'
import { BloomEffect } from 'postprocessing'
import type { KernelSize, BlendFunction } from 'postprocessing'
import { EffectPass, BloomEffect } from 'postprocessing'
import { effectComposerInjectionKey } from '../injectionKeys'
import { useEffect } from '../composables/effect'
import { makePropWatchers } from '../../util/prop'
export interface BloomProps {
/**
Expand Down Expand Up @@ -59,49 +58,28 @@ export interface BloomProps {
mipmapBlur?: boolean
}
const props = defineProps<BloomProps>()
const props = withDefaults(
defineProps<BloomProps>(),
{
mipmapBlur: undefined,
},
)
const composer = inject(effectComposerInjectionKey)
const pass = shallowRef<EffectPass | null>(null)
const effect = shallowRef<BloomEffect | null>(null)
const { pass, effect } = useEffect(() => new BloomEffect(props))
defineExpose({ pass, effect }) // to allow users to modify pass and effect via template ref
const { camera } = useTresContext()
const unwatch = watchEffect(() => {
if (!camera.value || !composer?.value) return
unwatch?.()
if (effect.value) return
effect.value = new BloomEffect(props)
pass.value = new EffectPass(camera.value, effect.value)
composer.value.addPass(pass.value)
})
watchEffect(() => {
if (!effect.value) return
const plainEffectPass = new BloomEffect()
// blendFunction is not updated, because it has no setter in BloomEffect
effect.value.intensity = props.intensity !== undefined ? props.intensity : plainEffectPass.intensity
effect.value.kernelSize = props.kernelSize !== undefined ? props.kernelSize : plainEffectPass.kernelSize
effect.value.luminanceMaterial.smoothing
= props.luminanceSmoothing !== undefined ? props.luminanceSmoothing : plainEffectPass.luminanceMaterial.smoothing
effect.value.luminanceMaterial.threshold
= props.luminanceThreshold !== undefined ? props.luminanceThreshold : plainEffectPass.luminanceMaterial.threshold
plainEffectPass.dispose()
})
onUnmounted(() => {
if (pass.value) composer?.value?.removePass(pass.value)
effect.value?.dispose()
pass.value?.dispose()
})
makePropWatchers(
[
// blendFunction is not updated, because it has no setter in BloomEffect
[() => props.intensity, 'intensity'],
[() => props.kernelSize, 'kernelSize'],
[() => props.luminanceSmoothing, 'luminanceMaterial.smoothing'],
[() => props.luminanceThreshold, 'luminanceMaterial.threshold'],
],
effect,
() => new BloomEffect(),
)
</script>

<template></template>
34 changes: 5 additions & 29 deletions src/core/effects/DepthOfField.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<script lang="ts" setup>
import type { BlendFunction } from 'postprocessing'
import { useTresContext } from '@tresjs/core'
import { EffectPass, DepthOfFieldEffect } from 'postprocessing'
import { inject, onUnmounted, shallowRef, watchEffect } from 'vue'
import { DepthOfFieldEffect } from 'postprocessing'
import { makePropWatchers } from '../../util/prop'
import { effectComposerInjectionKey } from '../injectionKeys'
import { useEffect } from '../composables/effect'
export interface DepthOfFieldProps {
/**
Expand Down Expand Up @@ -43,27 +42,10 @@ export interface DepthOfFieldProps {
}
const props = defineProps<DepthOfFieldProps>()
const composer = inject(effectComposerInjectionKey)
const pass = shallowRef<EffectPass | null>(null)
const effect = shallowRef<DepthOfFieldEffect | null>(null)
defineExpose({ pass, effect }) // to allow users to modify pass and effect via template ref
const { camera } = useTresContext()
const unwatch = watchEffect(() => {
if (!camera.value || !composer?.value) return
unwatch?.()
if (effect.value) return
effect.value = new DepthOfFieldEffect(camera.value, props)
pass.value = new EffectPass(camera.value, effect.value)
composer?.value?.addPass(pass.value)
})
const { pass, effect } = useEffect(() => new DepthOfFieldEffect(camera.value, props))
defineExpose({ pass, effect }) // to allow users to modify pass and effect via template ref
makePropWatchers(
[
Expand All @@ -80,12 +62,6 @@ makePropWatchers(
effect,
() => new DepthOfFieldEffect(),
)
onUnmounted(() => {
if (pass.value) composer?.value?.removePass(pass.value)
effect.value?.dispose()
pass.value?.dispose()
})
</script>

<template></template>
65 changes: 18 additions & 47 deletions src/core/effects/Glitch.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<script setup lang="ts">
import { GlitchMode, GlitchEffect } from 'postprocessing'
import { watchEffect } from 'vue'
import type { BlendFunction } from 'postprocessing'
import { GlitchMode, EffectPass, GlitchEffect } from 'postprocessing'
import { inject, onUnmounted, shallowRef, watchEffect } from 'vue'
import type { Vector2, Texture } from 'three'
import { useTresContext } from '@tresjs/core'
import { effectComposerInjectionKey } from '../injectionKeys'
import { omit } from '../../util/object'
import { useEffect } from '../composables/effect'
import { makePropWatchersUsingAllProps } from '../../util/prop'
export interface GlitchProps {
blendFunction?: BlendFunction
Expand Down Expand Up @@ -99,57 +98,29 @@ export interface GlitchProps {
const props = defineProps<GlitchProps>()
const composer = inject(effectComposerInjectionKey)
const pass = shallowRef<EffectPass | null>(null)
const effect = shallowRef<GlitchEffect | null>(null)
const { pass, effect } = useEffect(() => new GlitchEffect(props))
defineExpose({ pass, effect }) // to allow users to modify pass and effect via template ref
const { camera } = useTresContext()
const unwatch = watchEffect(() => {
if (!camera.value || !composer?.value) return
unwatch?.()
if (effect.value) return
effect.value = new GlitchEffect(props)
pass.value = new EffectPass(camera.value, effect.value)
composer.value.addPass(pass.value)
})
watchEffect(() => {
if (!effect.value) return
const plainEffectPass = new GlitchEffect()
// blendFunction and dtSize are not updated, because it has no setter in BloomEffect
const getMode = () => {
if (props.mode !== undefined) return props.active === false ? GlitchMode.DISABLED : props.mode
const plainEffectPass = new GlitchEffect()
const defaultMode = plainEffectPass.mode
plainEffectPass.dispose()
return plainEffectPass.mode
return defaultMode
}
effect.value.mode = getMode()
effect.value.ratio = props.ratio !== undefined ? props.ratio : plainEffectPass.ratio
effect.value.delay = props.delay !== undefined ? props.delay : plainEffectPass.delay
effect.value.columns = props.columns !== undefined ? props.columns : plainEffectPass.columns
effect.value.duration = props.duration !== undefined ? props.duration : plainEffectPass.duration
effect.value.strength = props.strength !== undefined ? props.strength : plainEffectPass.strength
effect.value.perturbationMap
= props.perturbationMap !== undefined ? props.perturbationMap : plainEffectPass.perturbationMap
effect.value.chromaticAberrationOffset
= props.chromaticAberrationOffset !== undefined
? props.chromaticAberrationOffset
: plainEffectPass.chromaticAberrationOffset
if (effect.value)
effect.value.mode = getMode()
})
onUnmounted(() => {
if (pass.value) composer?.value?.removePass(pass.value)
effect.value?.dispose()
pass.value?.dispose()
})
makePropWatchersUsingAllProps(
omit(props, ['active', 'mode', 'blendFunction']),
effect,
() => new GlitchEffect(),
)
</script>

<template></template>
Loading

0 comments on commit a2afd3f

Please sign in to comment.