Skip to content
This repository was archived by the owner on Feb 1, 2026. It is now read-only.
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 @@ -61,6 +61,7 @@ export default defineConfig({
{ text: 'Hue & Saturation', link: '/guide/pmndrs/hue-saturation' },
{ text: 'Lens Distortion', link: '/guide/pmndrs/lens-distortion' },
{ text: 'Grid', link: '/guide/pmndrs/grid' },
{ text: 'Texture', link: '/guide/pmndrs/texture' },
{ text: 'ASCII', link: '/guide/pmndrs/ascii' },
{ text: 'FXAA', link: '/guide/pmndrs/fxaa' },
{ text: 'SMAA', link: '/guide/pmndrs/smaa' },
Expand Down
121 changes: 121 additions & 0 deletions docs/.vitepress/theme/components/pmdrs/TextureDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<script setup lang="ts">
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
import { TresCanvas, useTexture } from '@tresjs/core'
import { TresLeches, useControls } from '@tresjs/leches'
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
import { BlendFunction, ColorChannel, ToneMappingMode } from 'postprocessing'
import { ref, watch } from 'vue'
import { EffectComposerPmndrs, TexturePmndrs, ToneMappingPmndrs } from '@tresjs/post-processing'
import type { Ref } from 'vue'
import type { EffectPass, TextureEffect } from 'postprocessing'

import '@tresjs/leches/styles'

const gl = {
clearColor: '#ffffff',
toneMapping: NoToneMapping,
toneMappingExposure: 2,
}

const textureEffectRef: Ref<{ pass: EffectPass, effect: TextureEffect } | null> = ref(null)

const texture = await useTexture(['https://raw.githubusercontent.com/Tresjs/assets/main/textures/dirt/color.jpg'])
texture.colorSpace = SRGBColorSpace
texture.wrapS = texture.wrapT = RepeatWrapping

const { blendFunction, rotation, opacity } = useControls({
blendFunction: {
options: Object.keys(BlendFunction).map(key => ({
text: key,
value: BlendFunction[key as keyof typeof BlendFunction],
})),
value: BlendFunction.MULTIPLY,
},
opacity: {
value: 1,
min: 0,
max: 1,
step: 0.01,
},
repeat: texture.repeat,
offset: texture.offset,
center: texture.center,
rotation: {
value: texture.rotation,
min: 0,
max: 2 * Math.PI,
step: 0.001,
},
mixColors: {
label: 'Mix colors (RGB)',
type: 'button',
onClick: () => {
textureEffectRef.value?.effect.setTextureSwizzleRGBA(
ColorChannel.GREEN,
ColorChannel.BLUE,
ColorChannel.RED,
ColorChannel.ALPHA,
)
},
size: 'md',
},
resetColors: {
label: 'Reset colors (RGB)',
type: 'button',
onClick: () => {
textureEffectRef.value?.effect.setTextureSwizzleRGBA(
ColorChannel.RED,
ColorChannel.GREEN,
ColorChannel.BLUE,
ColorChannel.ALPHA,
)
},
size: 'md',
},
})

watch(rotation, () => {
texture.rotation = rotation.value
})
</script>

<template>
<div class="aspect-16/9">
<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera
:position="[5, 7, 5]"
:look-at="[0, 0, 0]"
/>
<OrbitControls auto-rotate />

<TresMesh :position="[0, .5, 0]">
<TresBoxGeometry :args="[2, 2, 2]" />
<TresMeshBasicMaterial color="white" />
</TresMesh>

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

<Suspense>
<Environment background :blur="0.1" preset="dawn" />
</Suspense>

<Suspense>
<EffectComposerPmndrs>
<TexturePmndrs
ref="textureEffectRef"
:blendFunction="Number(blendFunction)"
:texture="texture"
:opacity="opacity"
/>
<ToneMappingPmndrs :mode="ToneMappingMode.AGX" />
</EffectComposerPmndrs>
</Suspense>
</TresCanvas>
</div>
<TresLeches :float="false" />
</template>
1 change: 1 addition & 0 deletions docs/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ declare module 'vue' {
ShockWaveDemo: typeof import('./.vitepress/theme/components/pmdrs/ShockWaveDemo.vue')['default']
SMAADemo: typeof import('./.vitepress/theme/components/pmdrs/SMAADemo.vue')['default']
SMAAThreeDemo: typeof import('./.vitepress/theme/components/three/SMAAThreeDemo.vue')['default']
TextureDemo: typeof import('./.vitepress/theme/components/pmdrs/TextureDemo.vue')['default']
TiltShiftDemo: typeof import('./.vitepress/theme/components/pmdrs/TiltShiftDemo.vue')['default']
ToneMappingDemo: typeof import('./.vitepress/theme/components/pmdrs/ToneMappingDemo.vue')['default']
UnrealBloomThreeDemo: typeof import('./.vitepress/theme/components/three/UnrealBloomThreeDemo.vue')['default']
Expand Down
85 changes: 85 additions & 0 deletions docs/guide/pmndrs/texture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Texture

<DocsDemoGUI>
<TextureDemo />
</DocsDemoGUI>

<details>
<summary>Demo code</summary>

<<< @/.vitepress/theme/components/pmdrs/TextureDemo.vue{0}
</details>

The `TextureEffect` component is part of the [`postprocessing`](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/TextureEffect.js~TextureEffect.html) package.
It allows rendering a texture with customizable options to create various visual effects.

## Usage

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

:::info
This component is designed to work with a provided texture and **does not** include built-in functionality to modify the texture itself. <br><br>
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.
:::

```vue{2,16-20,41-45}
<script setup lang="ts">
import { EffectComposerPmndrs, TexturePmndrs } from '@tresjs/post-processing/pmndrs'
import { TresCanvas, useTexture } from '@tresjs/core'
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
import { BlendFunction, ColorChannel } from 'postprocessing'

const gl = {
toneMapping: NoToneMapping,
}

const effectProps = {
blendFunction: BlendFunction.OVERLAY,
opacity: 0.65
}

const texture = await useTexture(['your-path-to-texture'])
texture.colorSpace = SRGBColorSpace
texture.wrapS = texture.wrapT = RepeatWrapping
texture.rotation = Math.PI / 2
texture.repeat.set( 2, 2 );

function setTextureSwizzleRGBA(red, green, blue, alpha) {
// This is an example of using a function belonging to the TextureEffect class.
// https://pmndrs.github.io/postprocessing/public/docs/file/src/effects/TextureEffect.js.html#lineNumber192
textureEffectRef.value?.effect.setTextureSwizzleRGBA(red, green, blue, alpha)
}

// Example how to mix texture's color channels.
setTextureSwizzleRGBA(ColorChannel.GREEN, ColorChannel.BLUE, ColorChannel.RED, ColorChannel.ALPHA)

// Example how to reset the texture's color channels (default).
setTextureSwizzleRGBA(ColorChannel.RED, ColorChannel.BLUE, ColorChannel.GREEN, ColorChannel.ALPHA)
</script>

<template>
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :position="[5, 5, 5]" />

<!-- Your Scene -->

<Suspense>
<EffectComposerPmndrs>
<TexturePmndrs v-bind="effectProps" :texture="texture" />
</EffectComposerPmndrs>
</Suspense>
</TresCanvas>
</template>
```

## Props

| Prop | Description | Default |
| ------------- | ----------------------------------------------------------------------------------------------- | --------------------------- |
| 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` |
| texture | The texture used for the effect. See the [`Texture`](https://threejs.org/docs/#api/en/textures/Texture) documentation | `null` |
| opacity | The opacity of the texture. | `1.0` |

## Further Reading

For more details, see the [TextureEffect documentation](https://pmndrs.github.io/postprocessing/public/docs/class/src/effects/TextureEffect.js~TextureEffect.html).
102 changes: 102 additions & 0 deletions playground/src/pages/postprocessing/texture.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script setup lang="ts">
import { ContactShadows, Environment, OrbitControls } from '@tresjs/cientos'
import { TresCanvas, useTexture } from '@tresjs/core'
import { TresLeches, useControls } from '@tresjs/leches'
import { NoToneMapping, RepeatWrapping, SRGBColorSpace } from 'three'
import { BlendFunction } from 'postprocessing'
import { ref, watch } from 'vue'
import { EffectComposerPmndrs, TexturePmndrs } from '@tresjs/post-processing'
import type { Ref } from 'vue'
import type { EffectPass, TextureEffect } from 'postprocessing'

import '@tresjs/leches/styles'

const gl = {
clearColor: '#ffffff',
toneMapping: NoToneMapping,
}

const textureEffectRef: Ref<{ pass: EffectPass, effect: TextureEffect } | null> = ref(null)

const texture = await useTexture(['https://raw.githubusercontent.com/Tresjs/assets/main/textures/dirt/color.jpg'])
texture.colorSpace = SRGBColorSpace
texture.wrapS = texture.wrapT = RepeatWrapping

const { blendFunction, rotation, opacity } = useControls({
blendFunction: {
options: Object.keys(BlendFunction).map(key => ({
text: key,
value: BlendFunction[key as keyof typeof BlendFunction],
})),
value: BlendFunction.PIN_LIGHT,
},
opacity: {
value: 1,
min: 0,
max: 1,
step: 0.01,
},
repeat: texture.repeat,
offset: texture.offset,
center: texture.center,
rotation: {
value: texture.rotation,
min: 0,
max: 2 * Math.PI,
step: 0.001,
},
})

watch(rotation, () => {
texture.rotation = rotation.value
})

// Example of using TextureEffect's setTextureSwizzleRGBA function
// () => textureEffectRef.value?.effect,
// () => {
// if (!textureEffectRef.value?.effect) { return}

// use setTextureSwizzleRGBA() from TextureEffect (https://pmndrs.github.io/postprocessing/public/docs/file/src/effects/TextureEffect.js.html#lineNumber192)
// textureEffectRef.value?.effect.setTextureSwizzleRGBA()
// },
// )
</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="black" :roughness=".25" />
</TresMesh>

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

<Suspense>
<Environment background :blur=".5" preset="snow" />
</Suspense>

<Suspense>
<EffectComposerPmndrs>
<TexturePmndrs
ref="textureEffectRef"
:blendFunction="Number(blendFunction)"
:texture="texture"
:opacity="opacity"
/>
</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 @@ -52,6 +52,7 @@ export const postProcessingRoutes = [
makeRoute('Scanline', '📽️', false),
makeRoute('Color Depth', '🔳', false),
makeRoute('Grid', '#️⃣', false),
makeRoute('Texture', '🧩', false),
makeRoute('ASCII', '🔡', false),
makeRoute('SMAA', '📐', false),
makeRoute('FXAA', '📐', false),
Expand Down
57 changes: 57 additions & 0 deletions src/core/pmndrs/TexturePmndrs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts" setup>
import type { BlendFunction } from 'postprocessing'
import { TextureEffect } from 'postprocessing'
import { makePropWatchers } from '../../util/prop'
import { useEffectPmndrs } from './composables/useEffectPmndrs'
import type { Texture } from 'three'
import { watch } from 'vue'

export interface TexturePmndrsProps {
/**
* The blend function.
*/
blendFunction?: BlendFunction
/**
* The texture.
*/
texture?: Texture
/**
* The opacity of the effect.
*/
opacity?: number
}

const props = defineProps<TexturePmndrsProps>()

const { pass, effect } = useEffectPmndrs(() => new TextureEffect(props), props)

defineExpose({
pass,
effect,
})

makePropWatchers(
[
[() => props.blendFunction, 'blendMode.blendFunction'],
],
effect,
() => new TextureEffect(),
)

watch(
[() => props.opacity],
() => {
if (props.opacity !== undefined) {
effect.value?.blendMode.setOpacity(props.opacity)
}
else {
const plainEffect = new TextureEffect()
effect.value?.blendMode.setOpacity(plainEffect.blendMode.getOpacity())
plainEffect.dispose()
}
},
{
immediate: true,
},
)
</script>
Loading