UstymUkhman/vite-plugin-glsl

Seeking Help: Manual Refresh Required for GLSL Code Updates in Vite/React-Three-Fiber

Closed this issue · 2 comments

Hey there! First off thank you for taking the time to write this plugin and share it!

I am having troubling getting Hot Module Reloading to work when editing glsl code in my Vite / React-Three-Fiber setup. I can get everything to compile and render just fine, but in order to see any updates to my shader I have the manually refresh the browser each time, which is annoying. In my hunt for a solution I came across your plugin, but despite installing it and getting things running I'm still having to manually refresh the page each time I make a change to glsl whether it's inline JS as a string or in a separate .glsl file.

Here's a github repo with my setup: https://github.com/klufkin/r3f-vite-shaders-test
I am using node.js v16.9.1 and npm v7.21.1 locally.

Any ideas as to what may be the issue would be greatly appreciated! Thank you!

Hi @klufkin! Thank you for using this plugin, opening this issue and creating a repo. I've checked it and even though I'm not familiar with react-three-fiber, it looks to me like this problem is caused by how shaderMaterial is expecting fragment & vertext strings. Here's what I did in order to fix it:

// import { OrbitControls } from '@react-three/drei';
import { Canvas, useFrame } from '@react-three/fiber';
import { useMemo, useRef } from 'react';
import glslify from 'glslify';
import frag from './frag.glsl';

const fragmentShader = frag;
// const fragmentShader = glslify`
// varying vec2 vUv;

// vec3 colorA = vec3(0.912,0.191,0.652);
// vec3 colorB = vec3(1.000,0.777,0.152);

// void main() {
//   // "Normalizing" with an arbitrary value
//   // We'll see a cleaner technique later :)
//   vec2 normalizedPixel = gl_FragCoord.xy/600.0;
//   vec3 color = mix(colorA, colorB, normalizedPixel.x);

//   gl_FragColor = vec4(color,1.0);
// }
// `;

const vertexShader = glslify`
varying vec2 vUv;

void main() {
  vUv = uv;
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  vec4 viewPosition = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPosition;

  gl_Position = projectedPosition;
}
`;

const CustomShader = {
  vertexShader,
  fragmentShader: frag
}

const Fragment = () => {
  // This reference will give us direct access to the mesh
  const mesh = useRef();

  return (
    <mesh ref={mesh} position={[0, 0, 0]} scale={1.0}>
      <planeGeometry args={[1, 1, 32, 32]} />
      <shaderMaterial
        // fragmentShader={fragmentShader}
        // vertexShader={vertexShader}
        args={[CustomShader]}
      />
    </mesh>
  );
};

const position = [0.0, 0.0, 0.7];

const Scene = () => {
  return (
    <>
      <Canvas camera={{ position }}>
        <Fragment />
      </Canvas>
    </>
  );
};

export default Scene;

By using CustomShader object, I was able to switch between colors in fragment shader without reloading the page manually:

vec3 colorA = vec3(0.912,0.191,0.652);
// vec3 colorA = vec3(0.0,0.0,0.0);
vec3 colorB = vec3(1.000,0.777,0.152);
// vec3 colorB = vec3(1.000,1.0,1.0);

This is a similar issue that's using this approach. As I said, I'm not quite familiar with react-three-fiber, so I'm not sure if this is a correct approach, but if you console.log your fragment shader after importing it like you did: import frag from './frag.glsl'; and then chage it's code, you may notice that the component will re-render with new, correct source code, so this behavior is not caused by the plugin or vite since it handles and injects latest shader changes into JS correctly.

If you've found this plugin any useful, please consider smashing a star button. :) Thanks again!

Hi @UstymUkhman thank you so much! That did the trick. That star button is definitely getting a smash 😆 .