Skip to content

Commit

Permalink
feat: add chromatic aberration effect (#149)
Browse files Browse the repository at this point in the history
* add chromatic aberration

* add chromatic aberration

* improve doc and demo

* fix <suspense> doc

* revert outlineDemo deleted by mistake

* update for package v2

---------

Co-authored-by: Alvaro Saburido <[email protected]>
  • Loading branch information
damienmontastier and alvarosabu authored Jan 4, 2025
1 parent f5a699f commit 8fc0321
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default defineConfig({
{ text: 'Glitch', link: '/guide/pmndrs/glitch' },
{ text: 'Noise', link: '/guide/pmndrs/noise' },
{ text: 'Outline', link: '/guide/pmndrs/outline' },
{ text: 'Chromatic Aberration', link: '/guide/pmndrs/chromatic-aberration' },
{ text: 'Scanline', link: '/guide/pmndrs/scanline' },
{ text: 'Pixelation', link: '/guide/pmndrs/pixelation' },
{ text: 'Vignette', link: '/guide/pmndrs/vignette' },
Expand Down
80 changes: 80 additions & 0 deletions docs/.vitepress/theme/components/pmdrs/ChromaticAberrationDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script setup lang="ts">
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { TresLeches, useControls } from '@tresjs/leches'
import { NoToneMapping, Vector2 } from 'three'
import { shallowRef, watchEffect } from 'vue'
import { ChromaticAberrationPmndrs, EffectComposerPmndrs } from '@tresjs/post-processing'
import '@tresjs/leches/styles'
const gl = {
clearColor: '#ffffff',
toneMapping: NoToneMapping,
multisampling: 8,
}
const chromaticAberrationRef = shallowRef(null)
const { offsetX, offsetY, radialModulation, modulationOffset } = useControls({
offsetX: { value: 0.070, step: 0.001, max: 0.5 },
offsetY: { value: 0.070, step: 0.001, max: 0.5 },
radialModulation: true,
modulationOffset: { value: 0, step: 0.01 },
})
watchEffect(() => {
modulationOffset.value.visible = radialModulation.value.value
})
</script>

<template>
<TresLeches style="left: initial;right:10px; top:10px;" />

<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera
:position="[5, 5, 5]"
:look-at="[0, 0, 0]"
/>
<OrbitControls auto-rotate />

<template
v-for="i in 4"
:key="i"
>
<TresMesh
:position="[((i - 1) - (4 - 1) / 2) * 1.5, 0, 0]"
>
<TresBoxGeometry
:width="4"
:height="4"
:depth="4"
/>
<TresMeshStandardMaterial color="#1C1C1E" />
</TresMesh>
</template>

<TresAmbientLight color="#ffffff" />

<TresDirectionalLight />

<ContactShadows
:opacity="1"
:position-y="-.5"
:scale="20"
:blur=".85"
/>

<Suspense>
<EffectComposerPmndrs>
<ChromaticAberrationPmndrs ref="chromaticAberrationRef" :offset="new Vector2(offsetX.value, offsetY.value)" :radial-modulation="radialModulation.value" :modulation-offset="modulationOffset.value" />
</EffectComposerPmndrs>
</Suspense>

<Suspense>
<Environment :intensity="2" :blur="0" preset="snow" />
</Suspense>
</TresCanvas>
</template>
2 changes: 2 additions & 0 deletions docs/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare module 'vue' {
export interface GlobalComponents {
BlenderCube: typeof import('./.vitepress/theme/components/BlenderCube.vue')['default']
BloomDemo: typeof import('./.vitepress/theme/components/pmdrs/BloomDemo.vue')['default']
ChromaticAberrationDemo: typeof import('./.vitepress/theme/components/pmdrs/ChromaticAberrationDemo.vue')['default']
DepthOfFieldDemo: typeof import('./.vitepress/theme/components/pmdrs/DepthOfFieldDemo.vue')['default']
DocsDemo: typeof import('./.vitepress/theme/components/DocsDemo.vue')['default']
Ducky: typeof import('./.vitepress/theme/components/Ducky.vue')['default']
Expand All @@ -24,6 +25,7 @@ declare module 'vue' {
PixelationThreeDemo: typeof import('./.vitepress/theme/components/three/PixelationThreeDemo.vue')['default']
ScanlineDemo: typeof import('./.vitepress/theme/components/pmdrs/ScanlineDemo.vue')['default']
SMAAThreeDemo: typeof import('./.vitepress/theme/components/three/SMAAThreeDemo.vue')['default']
ToneMappingDemo: typeof import('./.vitepress/theme/components/pmdrs/ToneMappingDemo.vue')['default']
UnrealBloomThreeDemo: typeof import('./.vitepress/theme/components/three/UnrealBloomThreeDemo.vue')['default']
VignetteDemo: typeof import('./.vitepress/theme/components/pmdrs/VignetteDemo.vue')['default']
}
Expand Down
78 changes: 78 additions & 0 deletions docs/guide/pmndrs/chromatic-aberration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Chromatic Aberration

<DocsDemo>
<ChromaticAberrationDemo />
</DocsDemo>

The `ChromaticAberration` effect is part of the [`postprocessing`](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/ChromaticAberrationEffect.js~ChromaticAberrationEffect.html) package. It simulates the dispersion of light as it passes through a lens, creating subtle or dramatic color fringing effects at the edges of objects. This effect can enhance the visual appeal of your scene by adding a realistic lens effect or a stylized touch.

## Usage

The `<ChromaticAberrationPmndrs>` component is easy to use and provides customizable options to suit different visual styles.

```vue{2,38-46}
<script setup lang="ts">
import { EffectComposerPmndrs, ChromaticAberrationPmndrs } from '@tresjs/post-processing/pmndrs'
import { Vector2 } from 'three'
const gl = {
toneMapping: NoToneMapping,
multisampling: 8,
}
const offset = new Vector2(0.07, 0.07)
</script>
<template>
<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera
:position="[5, 5, 5]"
:look-at="[0, 0, 0]"
/>
<template
v-for="i in 4"
:key="i"
>
<TresMesh
:position="[((i - 1) - (4 - 1) / 2) * 1.5, 0, 0]"
>
<TresBoxGeometry
:width="4"
:height="4"
:depth="4"
/>
<TresMeshStandardMaterial color="#1C1C1E" />
</TresMesh>
</template>
<Suspense>
<EffectComposerPmndrs>
<ChromaticAberrationPmndrs
:offset
radial-modulation
:modulation-offset="0"
/>
</EffectComposerPmndrs>
</Suspense>
</TresCanvas>
</template>
```

## Props

| Prop | Description | Default |
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------- |
| blendFunction | Defines the [`BlendFunction`](https://pmndrs.github.io/postprocessing/public/docs/variable/index.html#static-variable-BlendFunction) used for the effect. | `BlendFunction.SRC` |
| offset | The color offset vector determining the intensity and direction of chromatic aberration. | `Vector2(0.01, 0.01)` |
| radialModulation | Enables radial modulation to vary the effect intensity based on distance from the center. | `false` |
| modulationOffset | Specifies the modulation offset when `radialModulation` is **enabled**. | `0.15` |

::: info
The `modulationOffset` property is functional only when `radialModulation` is enabled.
:::

## Further Reading
see [postprocessing docs](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/ToneMappingEffect.js~ToneMappingEffect.html)
66 changes: 66 additions & 0 deletions playground/src/pages/postprocessing/chromatic-aberration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'

Check failure on line 2 in playground/src/pages/postprocessing/chromatic-aberration.vue

View workflow job for this annotation

GitHub Actions / Lint (20)

'Environment' is defined but never used
import { TresCanvas } from '@tresjs/core'
import { TresLeches, useControls } from '@tresjs/leches'
import { NoToneMapping, Vector2 } from 'three'
import { watchEffect } from 'vue'
import { BlendFunction } from 'postprocessing'
import { ChromaticAberrationPmndrs, EffectComposerPmndrs } from '@tresjs/post-processing'
import '@tresjs/leches/styles'
const gl = {
clearColor: '#ffffff',
toneMapping: NoToneMapping,
multisampling: 8,
envMapIntensity: 10,
}
const { offsetX, offsetY, radialModulation, modulationOffset, blendFunction } = useControls({
offsetX: { value: 0.085, step: 0.001, max: 0.5 },
offsetY: { value: 0.0, step: 0.001, max: 0.5 },
radialModulation: false,
modulationOffset: { value: 0, step: 0.01 },
blendFunction: {
options: Object.keys(BlendFunction).map(key => ({
text: key,
value: BlendFunction[key],
})),
value: BlendFunction.SRC,
},
})
watchEffect(() => {
modulationOffset.value.visible = radialModulation.value.value
})
</script>

<template>
<TresLeches />

<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera
:position="[5, 5, 5]"
:look-at="[0, 0, 0]"
/>
<OrbitControls auto-rotate />

<TresMesh :position="[0, .5, 0]">
<TresBoxGeometry :args="[2, 2, 2]" />
<TresMeshPhysicalMaterial color="#82DBC5" :roughness=".25" />
</TresMesh>

<ContactShadows
:opacity="1"
:position-y="-.5"
/>

<Suspense>
<EffectComposerPmndrs>
<ChromaticAberrationPmndrs :offset="new Vector2(offsetX.value, offsetY.value)" :radial-modulation="radialModulation.value" :modulation-offset="modulationOffset.value" :blendFunction="Number(blendFunction.value)" />
</EffectComposerPmndrs>
</Suspense>
</TresCanvas>
</template>
1 change: 1 addition & 0 deletions playground/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const postProcessingRoutes = [
makeRoute('Pixelation', '👾', false),
makeRoute('Bloom', '🌼', false),
makeRoute('Noise', '📟', false),
makeRoute('Chromatic Aberration', '🌈', false),
makeRoute('Scanline', '📺', false),
makeRoute('Vignette', '🕶️', false),
makeRoute('On-demand', '🔄', false),
Expand Down
53 changes: 53 additions & 0 deletions src/core/pmndrs/ChromaticAberration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script lang="ts" setup>
import type { BlendFunction } from 'postprocessing'
import { ChromaticAberrationEffect } from 'postprocessing'
import { Vector2 } from 'three'
import { makePropWatchers } from '../../util/prop'
import { useEffectPmndrs } from './composables/useEffectPmndrs'
export interface ChromaticAberrationPmndrsProps {
/**
* The blend function.
*/
blendFunction?: BlendFunction
/**
* The color offset.
*/
offset?: Vector2
/**
* Whether the effect should be modulated with a radial gradient.
*/
radialModulation?: boolean
/**
* The modulation offset, applicable if radial modulation is enabled.
*/
modulationOffset?: number
}
const props = withDefaults(
defineProps<ChromaticAberrationPmndrsProps>(),
{
offset: () => new Vector2(0.01, 0.01),
radialModulation: false,
modulationOffset: 0.15,
},
)
const { pass, effect } = useEffectPmndrs(() => new ChromaticAberrationEffect(props), props)
defineExpose({ pass, effect })
makePropWatchers(
[
[() => props.blendFunction, 'blendMode.blendFunction'],
[() => props.offset, 'offset'],
[() => props.radialModulation, 'radialModulation'],
[() => props.modulationOffset, 'modulationOffset'],
],
effect,
() => new ChromaticAberrationEffect(),
)
</script>
3 changes: 3 additions & 0 deletions src/core/pmndrs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import NoisePmndrs, { type NoisePmndrsProps } from './NoisePmndrs.vue'
import OutlinePmndrs, { type OutlinePmndrsProps } from './OutlinePmndrs.vue'
import PixelationPmndrs, { type PixelationPmndrsProps } from './PixelationPmndrs.vue'
import VignettePmndrs, { type VignettePmndrsProps } from './VignettePmndrs.vue'
import ChromaticAberrationPmndrs, { type ChromaticAberrationPmndrsProps } from './ChromaticAberration.vue'
import HueSaturationPmndrs, { type HueSaturationPmndrsProps } from './HueSaturationPmndrs.vue'
import ScanlinePmndrs, { type ScanlinePmndrsProps } from './ScanlinePmndrs.vue'

Expand All @@ -22,6 +23,7 @@ export {
PixelationPmndrs,
useEffectPmndrs,
VignettePmndrs,
ChromaticAberrationPmndrs,
HueSaturationPmndrs,
ScanlinePmndrs,
BloomPmndrsProps,
Expand All @@ -32,6 +34,7 @@ export {
OutlinePmndrsProps,
PixelationPmndrsProps,
VignettePmndrsProps,
ChromaticAberrationPmndrsProps,
HueSaturationPmndrsProps,
ScanlinePmndrsProps,
}

0 comments on commit 8fc0321

Please sign in to comment.