I am trying to figure out how to apply a chromatic aberration effect, combined with a halftone onto an image. Essentially, I have a logo that’s an SVG, and I’d like to load it in and change it so that the text goes from looking like image 1 to looking like image 2, with the aberration and a halftone texture. Is this possible, and is this even a good use case for Three? Thanks, all!
Plain i
With effect
You can render an svg like that in threejs… but it’s loading it, converting it to 2d/3d geometry, and then displaying it, at which point you can do things to it visually.. (but it won’t be an svg anymore. it will be a 3d model)
1 Like
Yeah, converting from an SVG isn’t a problem, but I’m looking for the right direction to achieve the effect itself; masking, blur, chromatic aberration etc.
Ahh great. Yes! You can use the postprocessing stack.
Scroll through the examples on the left to see some of them.
Here are the relevant imports in the repo:
Halftone is in there, but I don’t see one for chromatic aberration, but that’s a pretty straightforward one to implement… or crib from shadertoy or the community.
1 Like
@manthrax thanks for the assist! I’ve got something really nice going to achieve my effect, but the last thing I want to do is to try and make the Depth of Field/Blur effect cursor based; when the cursor is over a portion of the logo, the blur should go away. I’ve got all the effects in in my component (I’m using threlte, and have cut and pasted post-processing effects from threlte-postprocessing
as the installed npm package was bugged out in my app) but this last piece here isn’t work correctly. Any ideas? Found this as inspiration, pretty much exactly what I’m needing:
<script lang="ts">
import EffectComposer from '$lib/threlte/postprocessing/effect-composer/effect-composer.svelte';
import ChromaticAberration from '$lib/threlte/postprocessing/effects/chromatic-aberration.svelte';
import DepthOfField from '$lib/threlte/postprocessing/effects/depth-of-field.svelte';
import DotScreen from '$lib/threlte/postprocessing/effects/dot-screen.svelte';
import { T, useLoader, useTask, useThrelte } from '@threlte/core';
import { Box3, DoubleSide, Vector2, Vector3, type Group } from 'three';
import { SVGLoader } from 'three/addons/loaders/SVGLoader.js';
import { easing } from 'maath';
import { interactivity } from '@threlte/extras';
interface WordmarkProps {
height?: number;
width?: number;
}
const { renderer } = useThrelte();
const loader = useLoader(SVGLoader);
let svgGroup = $state<Group>();
const { width = window.innerWidth, height = 400 }: WordmarkProps = $props();
const dof = $state<Vector3>(new Vector3(0, 0, 0));
let pointer = $state<[x: number, y: number, z: number]>([0, 0, 0]);
$effect(() => {
renderer.setPixelRatio(2);
renderer.setSize(width, height);
if (!svgGroup) return;
const box = new Box3().setFromObject(svgGroup);
const center = new Vector3();
box.getCenter(center);
svgGroup.position.y = -center.y;
svgGroup.position.x -= center.x;
});
useTask((delta) => {
easing.damp3(dof, pointer, 0.2, delta);
});
interactivity();
$inspect(dof, pointer);
</script>
{#await loader.load('/wordmark.svg') then data}
<T.Group
scale={[0.215, -0.215, 0.5]}
bind:ref={svgGroup}
onpointermove={(e: any) => {
e.stopPropagation();
pointer = e.point.toArray();
}}
>
{#each data.paths as path}
{@const shapes = SVGLoader.createShapes(path)}
{#each shapes as shape}
<T.Mesh>
<T.ShapeGeometry args={[shape]} />
<T.MeshBasicMaterial
color={path.color}
side={DoubleSide}
transparent={true}
opacity={0.7}
/>
</T.Mesh>
{/each}
{/each}
</T.Group>
<EffectComposer enableNormalPass={false} multisampling={4}>
<DepthOfField
target={dof}
focusDistance={2}
resolutionScale={1}
bokehScale={30}
focusRange={0.5}
/>
<ChromaticAberration
offset={new Vector2(0, -0.004)}
radialModulation={false}
modulationOffset={0.25}
/>
<DotScreen angle={6} scale={10} />
</EffectComposer>
{/await}
Thanks!!
I haven’t used threlte, and my experience with r3f is rather limited. These abstraction layers over threejs introduce their own semantics which are rather opaque to me. For instance in the sample you linked.. (r3f) “dof” is a useRef which has current.target which is the actual vector3 containing the focal point in worldspace (presumably), and without knowing those semantics for threlte and how “isn’t work correctly” manifests in your app, I can’t do much informed speculation.
In vanilla threejs I would step through the constructor of the depth of field pass effect in the debugger, line by line, and watch how it initializes itself according to what I pass in, and make sure it looks similar what occurs in the threejs dof samples:
or
1 Like