Skip to content

Commit de407c4

Browse files
feat: add texture effect (demo, source code)
1 parent 0a89733 commit de407c4

File tree

8 files changed

+381
-0
lines changed

8 files changed

+381
-0
lines changed

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export default defineConfig({
6161
{ text: 'Hue & Saturation', link: '/guide/pmndrs/hue-saturation' },
6262
{ text: 'Lens Distortion', link: '/guide/pmndrs/lens-distortion' },
6363
{ text: 'Grid', link: '/guide/pmndrs/grid' },
64+
{ text: 'Texture', link: '/guide/pmndrs/texture' },
6465
{ text: 'FXAA', link: '/guide/pmndrs/fxaa' },
6566
{ text: 'SMAA', link: '/guide/pmndrs/smaa' },
6667
{ text: 'Kuwahara', link: '/guide/pmndrs/kuwahara' },
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<script setup lang="ts">
2+
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas, useTexture } from '@tresjs/core'
4+
import { TresLeches, useControls } from '@tresjs/leches'
5+
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
6+
import { BlendFunction, ColorChannel, ToneMappingMode } from 'postprocessing'
7+
import { ref, watch } from 'vue'
8+
import { EffectComposerPmndrs, TexturePmndrs, ToneMappingPmndrs } from '@tresjs/post-processing'
9+
import type { Ref } from 'vue'
10+
import type { EffectPass, TextureEffect } from 'postprocessing'
11+
12+
import '@tresjs/leches/styles'
13+
14+
const gl = {
15+
clearColor: '#ffffff',
16+
toneMapping: NoToneMapping,
17+
toneMappingExposure: 2,
18+
}
19+
20+
const textureEffectRef: Ref<{ pass: EffectPass, effect: TextureEffect } | null> = ref(null)
21+
22+
const texture = await useTexture(['https://raw.githubusercontent.com/Tresjs/assets/main/textures/dirt/color.jpg'])
23+
texture.colorSpace = SRGBColorSpace
24+
texture.wrapS = texture.wrapT = RepeatWrapping
25+
26+
const { blendFunction, rotation, opacity } = useControls({
27+
blendFunction: {
28+
options: Object.keys(BlendFunction).map(key => ({
29+
text: key,
30+
value: BlendFunction[key as keyof typeof BlendFunction],
31+
})),
32+
value: BlendFunction.MULTIPLY,
33+
},
34+
opacity: {
35+
value: 1,
36+
min: 0,
37+
max: 1,
38+
step: 0.01,
39+
},
40+
repeat: texture.repeat,
41+
offset: texture.offset,
42+
center: texture.center,
43+
rotation: {
44+
value: texture.rotation,
45+
min: 0,
46+
max: 2 * Math.PI,
47+
step: 0.001,
48+
},
49+
mixColors: {
50+
label: 'Mix colors (RGB)',
51+
type: 'button',
52+
onClick: () => {
53+
textureEffectRef.value?.effect.setTextureSwizzleRGBA(
54+
ColorChannel.GREEN,
55+
ColorChannel.BLUE,
56+
ColorChannel.RED,
57+
ColorChannel.ALPHA,
58+
)
59+
},
60+
size: 'md',
61+
},
62+
resetColors: {
63+
label: 'Reset colors (RGB)',
64+
type: 'button',
65+
onClick: () => {
66+
textureEffectRef.value?.effect.setTextureSwizzleRGBA(
67+
ColorChannel.RED,
68+
ColorChannel.GREEN,
69+
ColorChannel.BLUE,
70+
ColorChannel.ALPHA,
71+
)
72+
},
73+
size: 'md',
74+
},
75+
})
76+
77+
watch(rotation, () => {
78+
texture.rotation = rotation.value
79+
})
80+
81+
// watch(
82+
// () => textureEffectRef.value?.effect,
83+
// () => {
84+
// if (!textureEffectRef.value?.effect) { return}
85+
86+
// use setTextureSwizzleRGBA() from TextureEffect (https://pmndrs.github.io/postprocessing/public/docs/file/src/effects/TextureEffect.js.html#lineNumber192)
87+
// textureEffectRef.value?.effect.setTextureSwizzleRGBA()
88+
// },
89+
// )
90+
</script>
91+
92+
<template>
93+
<div class="aspect-16/9">
94+
<TresCanvas
95+
v-bind="gl"
96+
>
97+
<TresPerspectiveCamera
98+
:position="[5, 7, 5]"
99+
:look-at="[0, 0, 0]"
100+
/>
101+
<OrbitControls auto-rotate />
102+
103+
<TresMesh :position="[0, .5, 0]">
104+
<TresBoxGeometry :args="[2, 2, 2]" />
105+
<TresMeshBasicMaterial color="white" />
106+
</TresMesh>
107+
108+
<ContactShadows
109+
:opacity="1"
110+
:position-y="-.5"
111+
/>
112+
113+
<Suspense>
114+
<Environment background :blur="0.1" preset="dawn" />
115+
</Suspense>
116+
117+
<Suspense>
118+
<EffectComposerPmndrs>
119+
<TexturePmndrs
120+
ref="textureEffectRef"
121+
:blendFunction="Number(blendFunction)"
122+
:texture="texture"
123+
:opacity="opacity"
124+
/>
125+
<ToneMappingPmndrs :mode="ToneMappingMode.AGX" />
126+
</EffectComposerPmndrs>
127+
</Suspense>
128+
</TresCanvas>
129+
</div>
130+
<TresLeches :float="false" />
131+
</template>

docs/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ declare module 'vue' {
4242
ShockWaveDemo: typeof import('./.vitepress/theme/components/pmdrs/ShockWaveDemo.vue')['default']
4343
SMAADemo: typeof import('./.vitepress/theme/components/pmdrs/SMAADemo.vue')['default']
4444
SMAAThreeDemo: typeof import('./.vitepress/theme/components/three/SMAAThreeDemo.vue')['default']
45+
TextureDemo: typeof import('./.vitepress/theme/components/pmdrs/TextureDemo.vue')['default']
4546
TiltShiftDemo: typeof import('./.vitepress/theme/components/pmdrs/TiltShiftDemo.vue')['default']
4647
ToneMappingDemo: typeof import('./.vitepress/theme/components/pmdrs/ToneMappingDemo.vue')['default']
4748
UnrealBloomThreeDemo: typeof import('./.vitepress/theme/components/three/UnrealBloomThreeDemo.vue')['default']

docs/guide/pmndrs/texture.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Texture
2+
3+
<DocsDemoGUI>
4+
<TextureDemo />
5+
</DocsDemoGUI>
6+
7+
<details>
8+
<summary>Demo code</summary>
9+
10+
<<< @/.vitepress/theme/components/pmdrs/TextureDemo.vue{0}
11+
</details>
12+
13+
The `TextureEffect` component is part of the [`postprocessing`](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/TextureEffect.js~TextureEffect.html) package.
14+
It allows rendering a texture with customizable options to create various visual effects.
15+
16+
## Usage
17+
18+
The `<TexturePmndrs>` component is easy to use and provides customizable options to suit different visual styles.
19+
20+
:::info
21+
This component is designed to work with a provided texture and **does not** include built-in functionality to modify the texture itself. <br><br>
22+
If you need to adjust properties such as **rotation**, **repeat**, or **other attributes**, you should modify them directly the texture *(See usage example below)* that you pass to the `<TexturePmndrs />` component.
23+
:::
24+
25+
```vue{2,16-20,41-45}
26+
<script setup lang="ts">
27+
import { EffectComposerPmndrs, TexturePmndrs } from '@tresjs/post-processing/pmndrs'
28+
import { TresCanvas, useTexture } from '@tresjs/core'
29+
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
30+
import { BlendFunction, ColorChannel } from 'postprocessing'
31+
32+
const gl = {
33+
toneMapping: NoToneMapping,
34+
}
35+
36+
const effectProps = {
37+
blendFunction: BlendFunction.OVERLAY,
38+
opacity: 0.65
39+
}
40+
41+
const texture = await useTexture(['your-path-to-texture'])
42+
texture.colorSpace = SRGBColorSpace
43+
texture.wrapS = texture.wrapT = RepeatWrapping
44+
texture.rotation = Math.PI / 2
45+
texture.repeat.set( 2, 2 );
46+
47+
function setTextureSwizzleRGBA(red, green, blue, alpha) {
48+
// This is an example of using a function belonging to the TextureEffect class.
49+
// https://pmndrs.github.io/postprocessing/public/docs/file/src/effects/TextureEffect.js.html#lineNumber192
50+
textureEffectRef.value?.effect.setTextureSwizzleRGBA(red, green, blue, alpha)
51+
}
52+
53+
// Example how to mix texture's color channels.
54+
setTextureSwizzleRGBA(ColorChannel.GREEN, ColorChannel.BLUE, ColorChannel.RED, ColorChannel.ALPHA)
55+
56+
// Example how to reset the texture's color channels (default).
57+
setTextureSwizzleRGBA(ColorChannel.RED, ColorChannel.BLUE, ColorChannel.GREEN, ColorChannel.ALPHA)
58+
</script>
59+
60+
<template>
61+
<TresCanvas v-bind="gl">
62+
<TresPerspectiveCamera :position="[5, 5, 5]" />
63+
64+
<!-- Your Scene -->
65+
66+
<Suspense>
67+
<EffectComposerPmndrs>
68+
<TexturePmndrs v-bind="effectProps" :texture="texture" />
69+
</EffectComposerPmndrs>
70+
</Suspense>
71+
</TresCanvas>
72+
</template>
73+
```
74+
75+
## Props
76+
77+
| Prop | Description | Default |
78+
| ------------- | ----------------------------------------------------------------------------------------------- | --------------------------- |
79+
| blendFunction | Defines how the effect blends with the original scene. See the [`BlendFunction`](https://pmndrs.github.io/postprocessing/public/docs/variable/index.html#static-variable-BlendFunction) options. | `BlendFunction.NORMAL` |
80+
| texture | The texture used for the effect. See the [`Texture`](https://threejs.org/docs/#api/en/textures/Texture) documentation | `null` |
81+
| opacity | The opacity of the texture. | `1.0` |
82+
83+
## Further Reading
84+
85+
For more details, see the [TextureEffect documentation](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/TextureEffect.js~TextureEffect.html).
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<script setup lang="ts">
2+
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas, useTexture } from '@tresjs/core'
4+
import { TresLeches, useControls } from '@tresjs/leches'
5+
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
6+
import { BlendFunction } from 'postprocessing'
7+
import { ref, watch } from 'vue'
8+
import { EffectComposerPmndrs, TexturePmndrs } from '@tresjs/post-processing'
9+
import type { Ref } from 'vue'
10+
import type { EffectPass, TextureEffect } from 'postprocessing'
11+
12+
import '@tresjs/leches/styles'
13+
14+
const gl = {
15+
clearColor: '#ffffff',
16+
toneMapping: NoToneMapping,
17+
}
18+
19+
const textureEffectRef: Ref<{ pass: EffectPass, effect: TextureEffect } | null> = ref(null)
20+
21+
const texture = await useTexture(['https://raw.githubusercontent.com/Tresjs/assets/main/textures/dirt/color.jpg'])
22+
texture.colorSpace = SRGBColorSpace
23+
texture.wrapS = texture.wrapT = RepeatWrapping
24+
25+
const { blendFunction, rotation, opacity } = useControls({
26+
blendFunction: {
27+
options: Object.keys(BlendFunction).map(key => ({
28+
text: key,
29+
value: BlendFunction[key as keyof typeof BlendFunction],
30+
})),
31+
value: BlendFunction.PIN_LIGHT,
32+
},
33+
opacity: {
34+
value: 1,
35+
min: 0,
36+
max: 1,
37+
step: 0.01,
38+
},
39+
repeat: texture.repeat,
40+
offset: texture.offset,
41+
center: texture.center,
42+
rotation: {
43+
value: texture.rotation,
44+
min: 0,
45+
max: 2 * Math.PI,
46+
step: 0.001,
47+
},
48+
})
49+
50+
watch(rotation, () => {
51+
texture.rotation = rotation.value
52+
})
53+
54+
// watch(
55+
// () => textureEffectRef.value?.effect,
56+
// () => {
57+
// if (!textureEffectRef.value?.effect) { return}
58+
59+
// use setTextureSwizzleRGBA() from TextureEffect (https://pmndrs.github.io/postprocessing/public/docs/file/src/effects/TextureEffect.js.html#lineNumber192)
60+
// textureEffectRef.value?.effect.setTextureSwizzleRGBA()
61+
// },
62+
// )
63+
</script>
64+
65+
<template>
66+
<TresLeches />
67+
68+
<TresCanvas
69+
v-bind="gl"
70+
>
71+
<TresPerspectiveCamera
72+
:position="[5, 5, 5]"
73+
:look-at="[0, 0, 0]"
74+
/>
75+
<OrbitControls auto-rotate />
76+
77+
<TresMesh :position="[0, .5, 0]">
78+
<TresBoxGeometry :args="[2, 2, 2]" />
79+
<TresMeshPhysicalMaterial color="black" :roughness=".25" />
80+
</TresMesh>
81+
82+
<ContactShadows
83+
:opacity="1"
84+
:position-y="-.5"
85+
/>
86+
87+
<Suspense>
88+
<Environment background :blur=".5" preset="snow" />
89+
</Suspense>
90+
91+
<Suspense>
92+
<EffectComposerPmndrs>
93+
<TexturePmndrs
94+
ref="textureEffectRef"
95+
:blendFunction="Number(blendFunction)"
96+
:texture="texture"
97+
:opacity="opacity"
98+
/>
99+
</EffectComposerPmndrs>
100+
</Suspense>
101+
</TresCanvas>
102+
</template>

playground/src/router.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const postProcessingRoutes = [
5252
makeRoute('Scanline', '📽️', false),
5353
makeRoute('Color Depth', '🔳', false),
5454
makeRoute('Grid', '#️⃣', false),
55+
makeRoute('Texture', '🧩', false),
5556
makeRoute('SMAA', '📐', false),
5657
makeRoute('FXAA', '📐', false),
5758
makeRoute('Shock Wave', '🌊', false),

0 commit comments

Comments
 (0)