Skip to content

Commit 03f4986

Browse files
feat: color average (#166)
* init color average * add color average component, doc, demo * final doc * fix code for reviews --------- Co-authored-by: Tino Koch <17991193+Tinoooo@users.noreply.github.com>
1 parent 78b0bb7 commit 03f4986

File tree

8 files changed

+283
-1
lines changed

8 files changed

+283
-1
lines changed

docs/.vitepress/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,14 @@ export default defineConfig({
5151
{ text: 'Barrel blur', link: '/guide/pmndrs/barrel-blur' },
5252
{ text: 'Bloom', link: '/guide/pmndrs/bloom' },
5353
{ text: 'Chromatic Aberration', link: '/guide/pmndrs/chromatic-aberration' },
54+
{ text: 'Color Average', link: '/guide/pmndrs/color-average' },
5455
{ text: 'Depth of Field', link: '/guide/pmndrs/depth-of-field' },
5556
{ text: 'Dot Screen', link: '/guide/pmndrs/dot-screen' },
5657
{ text: 'Glitch', link: '/guide/pmndrs/glitch' },
5758
{ text: 'Hue & Saturation', link: '/guide/pmndrs/hue-saturation' },
5859
{ text: 'Lens Distortion', link: '/guide/pmndrs/lens-distortion' },
5960
{ text: 'Noise', link: '/guide/pmndrs/noise' },
60-
{ text: 'Outline', link: '/guide/pmndrs/outline' },
61+
{ text: 'Outline', link: '/guide/pmndrs/outline' },
6162
{ text: 'Pixelation', link: '/guide/pmndrs/pixelation' },
6263
{ text: 'Scanline', link: '/guide/pmndrs/scanline' },
6364
{ text: 'Sepia', link: '/guide/pmndrs/sepia' },
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<script setup lang="ts">
2+
import { Environment, OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas } from '@tresjs/core'
4+
import { TresLeches, useControls } from '@tresjs/leches'
5+
import type { Mesh } from 'three'
6+
import { NoToneMapping } from 'three'
7+
import { BlendFunction } from 'postprocessing'
8+
import { ColorAveragePmndrs, EffectComposerPmndrs } from '@tresjs/post-processing'
9+
import { gsap } from 'gsap'
10+
import { onUnmounted, ref, watch } from 'vue'
11+
12+
import '@tresjs/leches/styles'
13+
14+
const gl = {
15+
clearColor: '#ffffff',
16+
toneMapping: NoToneMapping,
17+
multisampling: 8,
18+
envMapIntensity: 10,
19+
}
20+
21+
const ctx = gsap.context(() => {})
22+
23+
const meshRef = ref<Mesh | null>(null)
24+
25+
const { blendFunction, opacity } = useControls({
26+
blendFunction: {
27+
options: Object.keys(BlendFunction).map(key => ({
28+
text: key,
29+
value: BlendFunction[key],
30+
})),
31+
value: BlendFunction.NORMAL,
32+
},
33+
opacity: {
34+
value: 1,
35+
min: 0,
36+
max: 1,
37+
},
38+
})
39+
40+
function onUpdateTimeline(e) {
41+
const progress = 1 - e.progress()
42+
opacity.value.value = progress
43+
}
44+
45+
watch(meshRef, () => {
46+
if (!meshRef.value) { return }
47+
48+
ctx.add(() => {
49+
gsap.timeline({
50+
repeat: -1,
51+
yoyo: true,
52+
onUpdate() {
53+
onUpdateTimeline(this)
54+
},
55+
})
56+
.to(meshRef.value.position, { y: -3.5, duration: 2 })
57+
})
58+
})
59+
60+
onUnmounted(() => {
61+
ctx.revert()
62+
})
63+
</script>
64+
65+
<template>
66+
<TresLeches style="left: initial;right:10px; top:10px;" />
67+
68+
<TresCanvas
69+
v-bind="gl"
70+
>
71+
<TresPerspectiveCamera
72+
:position="[5, 2, 15]"
73+
:look-at="[0, 0, 0]"
74+
/>
75+
<OrbitControls auto-rotate />
76+
77+
<TresMesh ref="meshRef" :position="[0, 3.5, 0]">
78+
<TresBoxGeometry :args="[2, 2, 2]" />
79+
<TresMeshPhysicalMaterial color="#8B0000" :roughness=".25" />
80+
</TresMesh>
81+
82+
<Suspense>
83+
<Environment background preset="shangai" />
84+
</Suspense>
85+
86+
<Suspense>
87+
<EffectComposerPmndrs>
88+
<ColorAveragePmndrs :blendFunction="Number(blendFunction.value)" :opacity="opacity.value" />
89+
</EffectComposerPmndrs>
90+
</Suspense>
91+
</TresCanvas>
92+
</template>

docs/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ declare module 'vue' {
1111
BlenderCube: typeof import('./.vitepress/theme/components/BlenderCube.vue')['default']
1212
BloomDemo: typeof import('./.vitepress/theme/components/pmdrs/BloomDemo.vue')['default']
1313
ChromaticAberrationDemo: typeof import('./.vitepress/theme/components/pmdrs/ChromaticAberrationDemo.vue')['default']
14+
ColorAverageDemo: typeof import('./.vitepress/theme/components/pmdrs/ColorAverageDemo.vue')['default']
1415
DepthOfFieldDemo: typeof import('./.vitepress/theme/components/pmdrs/DepthOfFieldDemo.vue')['default']
1516
DocsDemo: typeof import('./.vitepress/theme/components/DocsDemo.vue')['default']
1617
DotScreenDemo: typeof import('./.vitepress/theme/components/pmdrs/DotScreenDemo.vue')['default']

docs/guide/pmndrs/color-average.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Color Average
2+
3+
<DocsDemo>
4+
<ColorAverageDemo />
5+
</DocsDemo>
6+
7+
The `ColorAverage` effect is part of the [`postprocessing`](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/ColorAverageEffect.js~ColorAverageEffect.html) package. It averages the colors of the scene, creating a unique visual effect. This effect can be used to achieve a variety of artistic styles.
8+
9+
## Usage
10+
11+
The `<ColorAveragePmndrs>` component is easy to use and provides customizable options to suit different visual styles.
12+
13+
```vue{6,15-18,40-44}
14+
<script setup lang="ts">
15+
import { Environment, OrbitControls } from '@tresjs/cientos'
16+
import { TresCanvas } from '@tresjs/core'
17+
import { NoToneMapping } from 'three'
18+
import { BlendFunction } from 'postprocessing'
19+
import { ColorAveragePmndrs, EffectComposerPmndrs } from '@tresjs/post-processing'
20+
21+
const gl = {
22+
clearColor: '#ffffff',
23+
toneMapping: NoToneMapping,
24+
multisampling: 8,
25+
envMapIntensity: 10,
26+
}
27+
28+
const effectProps = reactive({
29+
blendFunction: BlendFunction.NORMAL,
30+
opacity: 0.5
31+
})
32+
</script>
33+
34+
<template>
35+
<TresCanvas
36+
v-bind="gl"
37+
>
38+
<TresPerspectiveCamera
39+
:position="[5, 2, 15]"
40+
:look-at="[0, 0, 0]"
41+
/>
42+
<OrbitControls auto-rotate />
43+
44+
<TresMesh :position="[0, 3.5, 0]">
45+
<TresBoxGeometry :args="[2, 2, 2]" />
46+
<TresMeshPhysicalMaterial color="#8B0000" :roughness=".25" />
47+
</TresMesh>
48+
49+
<Suspense>
50+
<Environment background preset="shangai" />
51+
</Suspense>
52+
53+
<Suspense>
54+
<EffectComposerPmndrs>
55+
<ColorAveragePmndrs v-bind="effectProps" />
56+
</EffectComposerPmndrs>
57+
</Suspense>
58+
</TresCanvas>
59+
</template>
60+
```
61+
62+
## Props
63+
64+
| Prop | Description | Default |
65+
| ----------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------- |
66+
| blendFunction | Defines the [`BlendFunction`](https://pmndrs.github.io/postprocessing/public/docs/variable/index.html#static-variable-BlendFunction) used for the effect. | `BlendFunction.NORMAL` |
67+
| opacity | Sets the opacity of the color average effect. | `1` |
68+
69+
## Further Reading
70+
For more details, see the [ColorAverage documentation](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/ColorAverageEffect.js~ColorAverageEffect.html)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<script setup lang="ts">
2+
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas } from '@tresjs/core'
4+
import { TresLeches, useControls } from '@tresjs/leches'
5+
import { NoToneMapping } from 'three'
6+
import { BlendFunction } from 'postprocessing'
7+
import { ColorAveragePmndrs, EffectComposerPmndrs } from '@tresjs/post-processing'
8+
9+
import '@tresjs/leches/styles'
10+
11+
const gl = {
12+
clearColor: '#ffffff',
13+
toneMapping: NoToneMapping,
14+
multisampling: 8,
15+
envMapIntensity: 10,
16+
}
17+
18+
const { blendFunction, opacity } = useControls({
19+
blendFunction: {
20+
options: Object.keys(BlendFunction).map(key => ({
21+
text: key,
22+
value: BlendFunction[key],
23+
})),
24+
value: BlendFunction.NORMAL,
25+
},
26+
opacity: {
27+
value: 1,
28+
min: 0,
29+
max: 1,
30+
},
31+
})
32+
</script>
33+
34+
<template>
35+
<TresLeches />
36+
37+
<TresCanvas
38+
v-bind="gl"
39+
>
40+
<TresPerspectiveCamera
41+
:position="[5, 5, 5]"
42+
:look-at="[0, 0, 0]"
43+
/>
44+
<OrbitControls auto-rotate />
45+
46+
<TresMesh :position="[0, .5, 0]">
47+
<TresBoxGeometry :args="[2, 2, 2]" />
48+
<TresMeshPhysicalMaterial color="#8B0000" :roughness=".25" />
49+
</TresMesh>
50+
51+
<ContactShadows
52+
:opacity="1"
53+
:position-y="-.5"
54+
/>
55+
56+
<Suspense>
57+
<Environment background preset="snow" />
58+
</Suspense>
59+
60+
<Suspense>
61+
<EffectComposerPmndrs>
62+
<ColorAveragePmndrs :blendFunction="Number(blendFunction.value)" :opacity="opacity.value" />
63+
</EffectComposerPmndrs>
64+
</Suspense>
65+
</TresCanvas>
66+
</template>

playground/src/router.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const postProcessingRoutes = [
4343
makeRoute('Bloom', '🌼', false),
4444
makeRoute('Noise', '📟', false),
4545
makeRoute('Chromatic Aberration', '🌈', false),
46+
makeRoute('Color Average', '🎞️', false),
4647
makeRoute('Lens Distortion', '🔍', false),
4748
makeRoute('Sepia', '🌅', false),
4849
makeRoute('Scanline', '📺', false),
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<script lang="ts" setup>
2+
import type { BlendFunction } from 'postprocessing'
3+
import { ColorAverageEffect } from 'postprocessing'
4+
import { makePropWatcher, makePropWatchers } from '../../util/prop'
5+
import { useEffectPmndrs } from './composables/useEffectPmndrs'
6+
import { watch } from 'vue'
7+
8+
export interface ColorAveragePmndrsProps {
9+
/**
10+
* The blend function.
11+
*/
12+
blendFunction?: BlendFunction
13+
14+
/**
15+
* The opacity of the color Average.
16+
*/
17+
opacity?: number
18+
}
19+
20+
const props = defineProps<ColorAveragePmndrsProps>()
21+
22+
const { pass, effect } = useEffectPmndrs(() => new ColorAverageEffect(props.blendFunction), props)
23+
24+
defineExpose({ pass, effect })
25+
26+
makePropWatcher(
27+
() => props.blendFunction,
28+
effect,
29+
'blendMode.blendFunction',
30+
() => new ColorAverageEffect(),
31+
)
32+
33+
watch(
34+
[effect, () => props.opacity],
35+
() => {
36+
if (!effect.value) { return }
37+
38+
if (props.opacity !== undefined) {
39+
effect.value?.blendMode.setOpacity(props.opacity)
40+
}
41+
else {
42+
const plainEffect = new ColorAverageEffect()
43+
effect.value?.blendMode.setOpacity(plainEffect.blendMode.getOpacity())
44+
plainEffect.dispose()
45+
}
46+
},
47+
)
48+
</script>

src/core/pmndrs/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import ToneMappingPmndrs, { type ToneMappingPmndrsProps } from './ToneMappingPmn
1414
import ChromaticAberrationPmndrs, { type ChromaticAberrationPmndrsProps } from './ChromaticAberrationPmndrs.vue'
1515
import HueSaturationPmndrs, { type HueSaturationPmndrsProps } from './HueSaturationPmndrs.vue'
1616
import ScanlinePmndrs, { type ScanlinePmndrsProps } from './ScanlinePmndrs.vue'
17+
import ColorAveragePmndrs, { type ColorAveragePmndrsProps } from './ColorAveragePmndrs.vue'
1718
import LensDistortionPmndrs, { type LensDistortionPmndrsProps } from './LensDistortionPmndrs.vue'
1819
import ShockWavePmndrs, { type ShockWavePmndrsProps } from './ShockWavePmndrs.vue'
1920
import DepthPickingPassPmndrs, { type DepthPickingPassPmndrsProps } from './DepthPickingPassPmndrs.vue'
@@ -38,6 +39,7 @@ export {
3839
ChromaticAberrationPmndrs,
3940
HueSaturationPmndrs,
4041
ScanlinePmndrs,
42+
ColorAveragePmndrs,
4143
LensDistortionPmndrs,
4244
ShockWavePmndrs,
4345
DepthPickingPassPmndrs,
@@ -58,6 +60,7 @@ export {
5860
ChromaticAberrationPmndrsProps,
5961
HueSaturationPmndrsProps,
6062
ScanlinePmndrsProps,
63+
ColorAveragePmndrsProps,
6164
LensDistortionPmndrsProps,
6265
ShockWavePmndrsProps,
6366
DepthPickingPassPmndrsProps,

0 commit comments

Comments
 (0)