Image rendered from useLoader @react-three/fiber using shaderMaterial has different color from its original color

978 views Asked by At

Here is my problem, my project is generated from create-react-app, followed by some @react-three/fiber and glsl dependencies, I render simple planeBufferGeometry and for the material I use shaderMaterial

here is code for planeBufferGeometry: (Geometries.jsx)

import React, { useRef } from 'react';
import * as THREE from 'three';
import { useFrame, useLoader } from '@react-three/fiber';
import './PlaneShaderMaterial';
import tiger from './images/tiger.jpg';

export const Plane = () => {
  const meshRef = useRef();
  const shaderRef = useRef();

  useFrame(() => {
    if (shaderRef.current) {
      shaderRef.current.uTime += 0.05;
    }
  });

  const [img] = useLoader(THREE.TextureLoader, [tiger]);

  return (
    <mesh ref={meshRef} position={[0, 0, 0]}>
      <planeBufferGeometry attach="geometry" args={[1, 1, 10, 10]} />
      {/* <meshBasicMaterial side={THREE.DoubleSide} map={img} attach="material" /> */}
      <planeShaderMaterial
        attach="material"
        ref={shaderRef}
        side={THREE.DoubleSide}
        uTexture={img}
      />
    </mesh>
  );
};

here is the code for the shaderMaterial: (PlaneShaderMaterial.jsx)

import * as THREE from 'three';
import { shaderMaterial } from '@react-three/drei';
import { extend } from '@react-three/fiber';
import glsl from 'babel-plugin-glsl/macro';

const uniform = {
  uColor: new THREE.Color(1.0, 0.0, 0.0),
  uTexture: new THREE.Texture(),
  uTime: 0,
  distanceFromCenter: 0,
};

const vertexShader = glsl`
  precision mediump float;
  varying vec2 vUv;
  uniform float uTime;

  void main() {

    vec3 pos = position;
    vUv = uv;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  }
`;

const fragmentShader = glsl`
  precision mediump float;
  uniform vec3 uColor;
  varying vec2 vUv;
  uniform float uTime;
  uniform sampler2D uTexture;

  void main() {
    vec4 t = texture2D(uTexture, vUv);

    gl_FragColor = vec4(t);
  }
`;

const PlaneShaderMaterial = shaderMaterial(
  // Uniform
  uniform,
  // vertex shader
  vertexShader,
  // fragment shader
  fragmentShader,
);

here is the code in App.jsx:

import React, { Suspense } from 'react';
import './App.css';
import CameraControls from './CameraControls.jsx';
import { Canvas } from '@react-three/fiber';
import { Plane } from './Geometries';

const Scene = () => {
  return (
    <Canvas
      gl={{ antialias: true, alpha: true }}
      camera={{
        fov: 70,
        aspect: window.innerWidth / window.innerHeight,
        near: 0.001,
        far: 1000,
        position: [0, 0, 2],
      }}
      style={{
        display: 'block',
        zIndex: '10',
      }}
    >
      <CameraControls />
      <Suspense fallback={null}>
        <Plane />
      </Suspense>
    </Canvas>
  );
};

function App() {
  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <Scene />
    </div>
  );
}

export default App;

This is the original image: tiger.jpg

This is the image after rendered with useLoader and textured with shaderMaterial: rederedImage

But when I tried to change the material to MeshBasicMaterial, the color was correct, I think that the problem has something to do with the shaderMaterial not the react-fiber useLoader which preLoads the image

So the rendered image has a little darker color than its original color. I've tried to change the SRGBEncoding but it doesn't work. Any idea how to make it the same color?

1

There are 1 answers

0
emot1onz On
void main() {
    float gamma = 2.2;
    vec3 texture = texture2D(uTexture, vUv).rgb;
    texture = pow(texture, vec3(1.0 / gamma));
    gl_FragColor = vec4(texture, 1.0);
}

I just had the same problem. The only thing that worked for me was a gamma correction.