GLB + Shader + R3F

Hi, I have a small problem with glb, i mean. it displays strangely… after implementing the shader.

import React, { useEffect, useRef } from "react";
import * as THREE from "three";
import { extend } from "@react-three/fiber";
import { shaderMaterial, useGLTF } from "@react-three/drei";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

const beltColor = shaderMaterial(
  { progress: 0 },
   `
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
  `,
  `
  uniform float progress;
  void main() {
    gl_FragColor = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 1.0, 1.0), progress);
  }
  `
);
extend({ beltColor });

export default function Model(props) {
  const modelRef = useRef();

  const { nodes, materials } = useGLTF("bjj_belt.glb");

  useEffect(() => {
    gsap.to(modelRef.current.rotation, {
      scrollTrigger: {
        trigger: ".section1",
        endTrigger: ".section5",
        markers: true,
        scrub: 1,
        start: "top top",
        end: "bottom top",
      },
      y: (index) => Math.PI * (2 + 0.6 * index),
      x: (index) => Math.PI * (2 + 0.6 * index),
      z: (index) => Math.PI * (2 + 0.6 * index),
    });
    // gsap.to(beltColor.uniforms.progress, {
    //   value: 1,
    //   scrollTrigger: {
    //     trigger: ".section1",
    //     endTrigger: ".section5",
    //     markers: true,
    //     scrub: 1,
    //     start: "top top",
    //     end: "bottom top",
    //   },
    // });
  }, []);

  return (
    <group {...props} dispose={null}>
      <mesh
        ref={modelRef} // Assign the ref to the mesh
        castShadow
        receiveShadow
        geometry={nodes.BJJ_BELT.geometry}
        material={materials[beltColor]}
        position={[0, 0, 3]}
        rotation={[0.189, 0, 0]}
      />
    </group>
  );
}

useGLTF.preload("bjj_belt.glb");

White version - shader.
Black version - standard.


Some ideas?

What is your issue exactly? Are you sure materials[beltColor] is not undefined?

looks like THREE.DoubleSide issue, but when I set that, glb disappearing

you’re not using a custom shader in that example. you’re giving it undefined, so that’s a meshbasicmaterial in threejs.

beltColor is a reference to a shader, you can’t use that as a key, materials[beltColor] doesn’t exist.

it should be

const beltColorMaterial = shaderMaterial(...)
extend({ beltColorMaterial })
...
<mesh geometry={...} ...>
  <beltColorMaterial />
</mesh>

you also don’t need ref.uniforms.progress.value = foo later on, this is why you use shaderMaterial because it creates auto setter/getters: ref.progress = foo

import React, { useEffect, useRef } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import * as THREE from "three";
import { shaderMaterial, useGLTF } from "@react-three/drei";
import { useFrame, extend } from "@react-three/fiber";
import beltlVertexShader from "./vertex.glsl";
import beltFragmentShader from "./fragment.glsl";

gsap.registerPlugin(ScrollTrigger);

export default function Model(props) {
  const beltMaterial = useRef();
  const modelRef = useRef();

  const { nodes } = useGLTF("newBelt.glb");

  useEffect(() => {
    gsap.to(modelRef.current.rotation, {
      scrollTrigger: {
        trigger: ".section1",
        endTrigger: ".section5",
        markers: true, // You can remove this in production
        scrub: 1,
        start: "top top",
        end: "bottom top",
      },
      y: (index) => Math.PI * (2 + 0.6 * index),
      // x: (index) => Math.PI * (2 + 0.6 * index),
      // z: (index) => Math.PI * (2 + 0.6 * index),
    });
  }, []);

  return (
    <group {...props} dispose={null}>
      <mesh
        ref={modelRef}
        position={[0, -1, 2]}
        geometry={nodes.Belt_Leather.geometry}
      >
        <beltMaterial ref={beltMaterial} />
      </mesh>
    </group>
  );
}

const BeltMaterial = shaderMaterial(
  {
    progress: 0,
  },

  beltlVertexShader,
  beltFragmentShader
);
extend({ BeltMaterial });

I made some corrections, now shader is on glb, but I think that is “sides” problem, because definitely is something wrong with backside…

<beltMaterial ref={beltMaterial} side={THREE.DoubleSide} />

the root of the issue is that the normals as flipped. if you fix this in blender it would be the cleanest. if this is a single walled mesh then doubleside is correct, but that can also be set in blender imo.

Yep, thank you so much Paul. I am still confused with syntax and “injecting” code in right place in R3F.

fiber semantics aren’t that many but important to know if you want to express three in react. this one page is practically all you need to know React Three Fiber Documentation if you go through it all once it should be fine.

1 Like