Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add chromatic aberration effect #149

Merged
merged 8 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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'
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,
}
Loading