Reputation: 1234
I have simple Points mesh with custom shader and buffer geometry.
The geometry has position, size and color attributes. On pointer hover, the hovered vertex turns into red color.
So far so good.
Now I would like to animate the change of color of the hovered vertex.
Here is the code snippet for the Points mesh:
const Points = (
props = { hoveredIndex: null, initialCameraZ: 0, array: [], sizes: [] }
) => {
const [_, setHover] = useState(false);
const vertices = new Float32Array(props.array);
const sizes = new Float32Array(props.sizes);
const _colors = genColors(props.sizes.length); // returns [] of numbers.
const uniforms = useMemo(() => {
return {
time: { type: "f", value: 0.0 },
cameraZ: { type: "f", value: props.initialCameraZ }
};
}, [props.initialCameraZ]);
// trying to use react-spring here
const [animProps, setAnimProps] = useSpring(() => ({
colors: _colors
}));
const geometry = useUpdate(
(geo) => {
if (props.hoveredIndex !== null) {
const i = props.hoveredIndex * 3;
const cols = [..._colors];
cols[i] = 1.0;
cols[i + 1] = 0.0;
cols[i + 2] = 0.0;
geo.setAttribute(
"color",
new THREE.BufferAttribute(new Float32Array(cols), 3)
);
setAnimProps({ colors: cols });
} else {
geo.setAttribute(
"color",
new THREE.BufferAttribute(new Float32Array(_colors), 3)
);
setAnimProps({ colors: _colors });
}
},
[props.hoveredIndex]
);
return (
<a.points
onPointerOver={(e) => setHover(true)}
onPointerOut={(e) => setHover(false)}
>
<a.bufferGeometry attach="geometry" ref={geometry}>
<bufferAttribute
attachObject={["attributes", "position"]}
count={vertices.length / 3}
array={vertices}
itemSize={3}
/>
<bufferAttribute
attachObject={["attributes", "size"]}
count={sizes.length}
array={sizes}
itemSize={1}
/>
<a.bufferAttribute
attachObject={["attributes", "color"]}
count={_colors.length}
array={new Float32Array(_colors)}
// array={animProps.colors} // this does not work
itemSize={3}
/>
</a.bufferGeometry>
<shaderMaterial
attach="material"
uniforms={uniforms}
vertexShader={PointsShader.vertexShader}
fragmentShader={PointsShader.fragmentShader}
vertexColors={true}
/>
</a.points>
);
};
Full code and example is available on codesandbox
When I try to use animProps.colors
for color in bufferAttribute it fails to change the color.
What am i doing wrong? How to make it right?
I know I could create start and target color attributes, pass them to the shader and interpolate there but that would beat the purpose of using react-three-fiber.
Is there a way animating buffer attributes in react-three-fiber?
Upvotes: 6
Views: 2275
Reputation: 5827
This likely isn't working as expected because changes made to buffer attributes need to be explicitly flagged for sending to the GPU. I suppose that react-spring
does not do this out of the box.
Here's a vanilla example, note the usage of needsUpdate
:
import { useFrame } from '@react-three/fiber'
import React, { useRef } from 'react'
import { BufferAttribute, DoubleSide } from 'three'
const positions = new Float32Array([
1, 0, 0,
0, 1, 0,
-1, 0, 0,
0, -1, 0
])
const indices = new Uint16Array([
0, 1, 3,
2, 3, 1,
])
const Comp = () => {
const positionsRef = useRef<BufferAttribute>(null)
useFrame(() => {
const x = 1 + Math.sin(performance.now() * 0.01) * 0.5
positionsRef.current.array[0] = x
positionsRef.current.needsUpdate = true
})
return <mesh>
<bufferGeometry>
<bufferAttribute
ref={positionsRef}
attach='attributes-position'
array={positions}
count={positions.length / 3}
itemSize={3}
/>
<bufferAttribute
attach="index"
array={indices}
count={indices.length}
itemSize={1}
/>
</bufferGeometry>
<meshBasicMaterial
color={[0, 1, 1]}
side={DoubleSide}
/>
</mesh>
}
For further reading check out this tutorial.
Upvotes: 1