Search code examples
javascriptreactjsthree.jsglslreact-three-fiber

How to resolve a shader error in a React with react-three-fiber when using a custom shader to create a hover state for an image component?


I am trying to create an image component with a hover state using shaders with react-three-fiber.

The shader was originally created by TheFrost and can be found at https://codepen.io/frost084/full/OKZNRm.

Unfortunately, I come across an error that I don't understand. Error:

REE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false

RAGMENT

Program Info Log: Fragment shader is not compiled.

ERROR: 0:106: 'texture' : function name expected
ERROR: 0:106: '=' : dimension mismatch
ERROR: 0:106: '=' : cannot convert from 'const mediump float' to 'highp 4-component vector of float'
ERROR: 0:111: 'texture' : function name expected
ERROR: 0:111: '=' : dimension mismatch
ERROR: 0:111: 'assign' : cannot convert from 'const mediump float' to 'highp 4-component vector of float'
ERROR: 0:113: 'texture' : function name expected
ERROR: 0:113: 'r' :  field selection requires structure, vector, or interface block on left hand side
ERROR: 0:114: 'texture' : function name expected
ERROR: 0:114: 'g' :  field selection requires structure, vector, or interface block on left hand side

 101:         float zoomLevel = .2;
  102:         float hoverLevel = exponentialInOut(min(1., (distance(vec2(.5), uv) * hover) + hover));
  103:         uv *= 1. - zoomLevel * hoverLevel;
  104:         uv += zoomLevel / 2. * hoverLevel;
  105:         uv = clamp(uv, 0., 1.);
> 106:         vec4 color = texture2D(texture, uv);
  107:         if(hoverLevel > 0.) {
  108:           hoverLevel = 1.-abs(hoverLevel-.5)*2.;
  109:           //Pixel displace
  110:           uv.y += color.r * hoverLevel * .05;
  111:           color = texture2D(texture, uv);
  112:           // RGBshift
o

The Image Component code:

import { useState, useMemo } from 'react';
import { TextureLoader } from 'three';
import { Canvas } from '@react-three/fiber';
import { useSpring, animated, config } from '@react-spring/three';

import { HoverImageShader } from 'shader/HoverImageShader';

const AnimatedImage = ({ src, width = '100%', height = '100%' }) => {
  const [hovered, setHover] = useState(false);

  const imgTexture = useMemo(() => {
    const loader = new TextureLoader();
    return loader.load(src);
  }, [src]);

  const { hoverValue } = useSpring({
    hoverValue: hovered ? 1 : 0,
    config: config.molasses,
  });

  return (
    <Canvas
      pixelratio={window.devicePixelRatio || 1}
      style={{ background: 'pink', width, height }}
      camera={{ fov: 75, position: [0, 0, 7] }}
    >
      <animated.mesh
        onPointerOver={() => setHover(true)}
        onPointerOut={() => setHover(false)}
      >
        <planeBufferGeometry attach='geometry' args={[10.7, 10.7]} />
        <animated.shaderMaterial
          attach='material'
          transparent
          args={[HoverImageShader]}
          uniforms-texture-value={imgTexture}
          uniforms-hover-value={hoverValue}
        />
      </animated.mesh>
    </Canvas>
  );
};

export default AnimatedImage;

Besides the error my website works fine, only the image is not rendered. I have confirmed that the image is successfully passed through props. However, the image is not being rendered on the canvas. The canvas is rendering correctly with a pink background.

How to resolve this shader error?


Solution

  • The name of the texture sampler uniform can't be texture, because texture is the name of built-in function in GLSL ES 3.00. I suggest to use tDiffuse as it is common in threejs.

    Change setting of the uniform variable:

    uniforms-texture-value={imgTexture}

    uniforms-tDiffuse-value={imgTexture}
    

    Change the fragment shader:

    precision highp float; 
    
    uniform sampler2D tDiffuse;
    uniform float imageAspectRatio;
    uniform float aspectRatio;
    uniform float opacity;
    uniform float hover;
    varying vec2 vUv;
    
    float exponentialInOut(float t) {
        return t == 0.0 || t == 1.0 
        ? t 
        : t < 0.5
            ? +0.5 * pow(2.0, (20.0 * t) - 10.0)
            : -0.5 * pow(2.0, 10.0 - (t * 20.0)) + 1.0;
    } 
    
    void main() {
        vec2 uv = vUv;
    
        // fix aspectRatio
        float u = imageAspectRatio/aspectRatio;
        if(imageAspectRatio > aspectRatio) {
        u = 1. / u;
        }
    
        uv.y *= u;
        uv.y -= (u)/2.-.5;
    
        // hover effect
        float zoomLevel = .2;
        float hoverLevel = exponentialInOut(min(1., (distance(vec2(.5), uv) * hover) + hover));
        uv *= 1. - zoomLevel * hoverLevel;
        uv += zoomLevel / 2. * hoverLevel;
        uv = clamp(uv, 0., 1.);
        vec4 color = texture2D(tDiffuse, uv);
        if(hoverLevel > 0.) {
        hoverLevel = 1.-abs(hoverLevel-.5)*2.;
        //Pixel displace
        uv.y += color.r * hoverLevel * .05;
        color = texture2D(tDiffuse, uv);
        // RGBshift
        color.r = texture2D(tDiffuse, uv+(hoverLevel)*0.01).r;
        color.g = texture2D(tDiffuse, uv-(hoverLevel)*0.01).g;
        }
    
        gl_FragColor = mix(vec4(1.,1.,1.,opacity), color, opacity);
    }