Reputation: 11
I want to port shader from shadertoy to my React-Vite app using Three.js and @react-three/fiber. Exactly this shader; https://www.shadertoy.com/view/DlySDD I surfed Google for a long time, but I couldn't find an exact answer. There are some posts on porting shadertoy shader on Stack Overflow, but their example shader is a bit different. As I was trying to port it, I got a problem. I think I don't get an idea of BufferShader. Below I attached my code snippet. Please check and give me an idea on what could be wrong.
import { Fragment, useRef, useMemo } from "react";
import * as THREE from "three";
import { Canvas, useFrame, useLoader, useThree } from "@react-three/fiber";
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const Buffer_A_Frag = `
uniform vec2 iResolution;
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Time varying pixel color
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
// Output to screen
fragColor = vec4(col, 1.0);
}
void main() {
vec4 fragColor;
mainImage(fragColor, gl_FragCoord.xy);
gl_FragColor = fragColor;
}
`;
const fragmentShader = `
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform float iTime; // shader playback time (in seconds)
void mainImage(out vec4 color, vec2 coord)
{
//Resolution for scaling
vec3 res = iResolution,
//Compute ray direction ranging from -1 to 1 (aspect corrected, with +z forward)
dir = vec3(res.xy-coord*2.,res.x) / res.x,
//Sample position
pos;
//Use RG channels of last frame for the GB (brightened by /.7)
//This produces a simple chromatic aberration effect!
color = vec4(0, texture(iChannel0,coord/res.xy)/.7);
//Loop 100 times
for(float i = 0.; i<1e2; i++)
//Compute sample point along ray direction plus an arbitrary offset
//Starts at random point between 0 and 0.1 and increments 0.1 each step
pos = dir*(texture(iChannel2,coord/1e3).r+i)*.1 + 9.,
//Rotate about the y-axis
pos.xz *= mat2(cos(iTime*.1+asin(vec4(0,1,-1,0)))),
//Add sample point, decreasing intensity with each interation (down to 0)
color.r += (1e2-i) / 1e5 /
//Attenuate from the absolute distance to the noise + gyroid shape
abs(texture(iChannel1,pos*.1).r + dot(sin(pos),cos(pos.yzx)));
}
void main() {
vec4 fragColor;
mainImage(fragColor, gl_FragCoord.xy);
gl_FragColor = fragColor;
}
`;
const OceanShaderMesh = () => {
const materialRef = useRef(null);
const { size, gl } = useThree();
gl.setPixelRatio(1);
useFrame(({ clock }) => {
if (materialRef.current) {
const material = materialRef.current;
material.uniforms.iTime.value = clock.getElapsedTime();
material.uniforms.iResolution.value.set(size.width, size.height);
gl.setSize(size.width, size.height);
}
});
const uniforms = useMemo(() => {
return {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector2(size.width, size.height) },
iChannel0: {
value: Buffer_A_Frag,
},
iChannel1: { value: useLoader(THREE.TextureLoader, "buffer1.png") },
iChannel2: {
value: useLoader(THREE.TextureLoader, "noise.png"),
},
};
}, []);
return (
<mesh scale={[size.width / size.height, 1, 1]}>
<planeGeometry args={[size.width, size.height]} />
<shaderMaterial
ref={materialRef}
uniforms={uniforms}
vertexShader={vertexShader}
fragmentShader={fragmentShader}
/>
</mesh>
);
};
const Background = () => (
<Fragment>
<Canvas
orthographic
camera={{ zoom: 100 }}
style={{
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0, 0, 0, 0.5)",
}}
>
<OceanShaderMesh />
</Canvas>
</Fragment>
);
export default Background;
I made that React component which includes Shader, but it has errors in compiling.
Here is an error log: enter image description here
And I put FragmentShader into the iChannel0 of uniform, but I think it's not correct and even stupid.
Any idea?
Upvotes: 0
Views: 82
Reputation: 1131
If you read your error message, you can see that the texture
function is not defined.
This is because ShaderToy is by default in OpenGL ES latest version.
And for three.js
WebGL shaders are by default in GLSL1
.
There are little differences in naming of default functions.
texture
in older versions would be texture2D
.
Not sure if there is a well documented "migration" page somewhere but you could compare from this example page that compares version 100 and 330 (but note that three.js
uses version 100 and 300).
Alternatively, if you want to use equivalent OpenGL ES
more recent versions with three.js
, you can set material version to THREE.GLSL3
(Material's constants end of page)
Upvotes: 0