Custom shader stops when using useState hook (r3f + drei)

Hi. I’m making some spheres with a custom shader. It works fine.

This is the Planet component:

import * as THREE from 'three';
import {shaderMaterial} from '@react-three/drei';
import {useFrame, extend} from '@react-three/fiber';
import {useRef, forwardRef} from 'react';
import planetVertexShader from './shaders/planet/vertex.glsl';
import planetFragmentShader from './shaders/planet/fragment.glsl';

const PlanetMaterial = shaderMaterial(
    uTime: 0,
    uColorStart: new THREE.Color('#ffffff'),
    uColorEnd: new THREE.Color('#000000'),


export const Planet = forwardRef(function Planeta(props, ref) {
  const planetMaterial = useRef();
  useFrame((state, delta) => {
    planetMaterial.current.uTime += delta * 0.7;

  return (
    <mesh {...props} ref={ref}>
      <sphereGeometry args={[1, 16, 8]} />
          uTime: {value: 0},
          uColorStart: {value: new THREE.Color('#ff7400')},
          uColorEnd: {value: new THREE.Color('#451439')},

And this is the scene:

export default function Experience() {
  const sunRef = useRef();
  const planet1Ref = useRef();

  const [test, setTest] = useState(0);

  useEffect(() => {
     setTest('test'); // <--------INCLUDING THIS LINE THE SHADER STOPS
  }, []);

  return (
      <Sun ref={sunRef} />
      <Planet scale={0.5} ref={planet1Ref} />

The problem is that when I use the useState hook in Experience, the shader animation stops.

That is, as soon as I include setTest('test') the shader animation stops.

Why can that happen?

uniforms are only applied once, by doing what you do there you would apply them every render, they would become stale. but drei/shaderMaterial is a helper that creates setter/getters, no need to grab into uniforms-foo-value. you also already gave it vertex and fragment shaders, you’re just overwriting stuff that shouldn’t be overwritten.

your animation stops because useFrame is incrementing a stale uTime, which you have overwritten, the new uTime (and the other uniforms) is not actually linked into the gpu shader uniform no more because uniforms are linked in the constructor only.

it is just this:

<planetMaterial ref={ref} uColorStart="red" uColorEnd="#dead00" />

here’s an example of how you should handle shaders:
ThreeJS Journey Portal - CodeSandbox (hint: the exact same as in vanilla threejs)

1 Like