-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b8513c0
commit 0c02ac8
Showing
11 changed files
with
440 additions
and
59 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
example/src/components/GenThreeShader/GenThreeShader.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
"use client"; | ||
import clsx from "clsx"; | ||
import { useEffect, useMemo, useRef } from "react"; | ||
import * as THREE from "three"; | ||
|
||
/** | ||
* Configuration object for defining shader properties | ||
* @type ShaderConfig | ||
*/ | ||
type ShaderConfig = { | ||
/** Optional vertex shader code. If not provided, a default vertex shader will be used */ | ||
vertexShader?: string; | ||
/** Required fragment shader code */ | ||
fragmentShader: string; | ||
/** Optional additional uniforms to pass to the shader */ | ||
uniforms?: Record<string, THREE.IUniform>; | ||
}; | ||
|
||
/** | ||
* Props for the GenThreeShader component | ||
* @type GenThreeShaderProps | ||
*/ | ||
type GenThreeShaderProps = { | ||
/** Optional CSS class name to apply to the container */ | ||
className?: string; | ||
/** Shader configuration object */ | ||
shaderConfig: ShaderConfig; | ||
}; | ||
|
||
const defaultVertexShader = ` | ||
varying vec2 vUv; | ||
void main() { | ||
vUv = uv; | ||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); | ||
} | ||
`; | ||
|
||
export const GenThreeShader = ({ | ||
className, | ||
shaderConfig, | ||
}: GenThreeShaderProps) => { | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
const sceneRef = useRef<THREE.Scene | null>(null); | ||
const rendererRef = useRef<THREE.WebGLRenderer | null>(null); | ||
const mouseRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); | ||
const materialRef = useRef<THREE.ShaderMaterial | null>(null); | ||
const sizeRef = useRef<{ width: number; height: number }>({ | ||
width: 0, | ||
height: 0, | ||
}); | ||
|
||
const baseUniforms = useMemo( | ||
() => ({ | ||
resolution: { value: new THREE.Vector2() }, | ||
mouse: { value: new THREE.Vector2() }, | ||
time: { value: 0 }, | ||
noiseScale: { value: 3.0 }, | ||
colorSpeed: { value: 1.0 }, | ||
kaleidoSegments: { value: 6.0 }, | ||
patternScale: { value: 1.0 }, | ||
colorIntensity: { value: 0.7 }, | ||
motionSpeed: { value: 1.0 }, | ||
warpIntensity: { value: 0.5 }, | ||
}), | ||
[], | ||
); | ||
|
||
useEffect(() => { | ||
if (!containerRef.current) return; | ||
|
||
const scene = new THREE.Scene(); | ||
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); | ||
const container = containerRef.current; | ||
sceneRef.current = scene; | ||
|
||
// Initialize renderer | ||
const renderer = new THREE.WebGLRenderer({ alpha: true }); | ||
rendererRef.current = renderer; | ||
container.appendChild(renderer.domElement); | ||
|
||
// Setup geometry and material | ||
const geometry = new THREE.PlaneGeometry(2, 2); | ||
const material = new THREE.ShaderMaterial({ | ||
uniforms: { | ||
...baseUniforms, | ||
...shaderConfig.uniforms, | ||
}, | ||
vertexShader: shaderConfig.vertexShader || defaultVertexShader, | ||
fragmentShader: shaderConfig.fragmentShader, | ||
}); | ||
|
||
materialRef.current = material; | ||
const mesh = new THREE.Mesh(geometry, material); | ||
scene.add(mesh); | ||
|
||
// Sizing with ResizeObserver | ||
const resizeObserver = new ResizeObserver((entries) => { | ||
const entry = entries[0]; | ||
if (entry) { | ||
sizeRef.current = { | ||
width: entry.contentRect.width, | ||
height: entry.contentRect.height, | ||
}; | ||
renderer.setSize(sizeRef.current.width, sizeRef.current.height); | ||
material.uniforms.resolution.value.set( | ||
sizeRef.current.width, | ||
sizeRef.current.height, | ||
); | ||
renderer.render(scene, camera); | ||
} | ||
}); | ||
|
||
resizeObserver.observe(container); | ||
|
||
// Mouse handling | ||
const handleMouseMove = (event: MouseEvent) => { | ||
const bounds = container.getBoundingClientRect(); | ||
mouseRef.current = { | ||
x: event.clientX - bounds.left, | ||
y: event.clientY - bounds.top, | ||
}; | ||
}; | ||
|
||
// Animation frame management | ||
let animationFrameId: number; | ||
const startTime = performance.now(); | ||
|
||
const animate = () => { | ||
material.uniforms.time.value = (performance.now() - startTime) * 0.001; | ||
material.uniforms.mouse.value.set(mouseRef.current.x, mouseRef.current.y); | ||
renderer.render(scene, camera); | ||
animationFrameId = requestAnimationFrame(animate); | ||
}; | ||
|
||
// Start animation | ||
animate(); | ||
|
||
return () => { | ||
resizeObserver.disconnect(); | ||
cancelAnimationFrame(animationFrameId); | ||
renderer.dispose(); | ||
scene.clear(); | ||
container.removeChild(renderer.domElement); | ||
container.removeEventListener("mousemove", handleMouseMove); | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps -- Remove onDownload from deps | ||
}, [shaderConfig, baseUniforms]); | ||
|
||
return ( | ||
<div | ||
ref={containerRef} | ||
className={clsx("relative w-full h-full", className)} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# GenThreeShader Component | ||
|
||
A React component that renders customizable WebGL shaders using Three.js. | ||
|
||
## Usage | ||
|
||
```tsx | ||
import { GenThreeShader } from './GenThreeShader'; | ||
import { kaleidoscopeShader } from './shaders/kaleidoscopeShader'; | ||
|
||
const MyComponent = () => { | ||
const shaderConfig = { | ||
fragmentShader: kaleidoscopeShader, | ||
uniforms: { | ||
patternScale: { value: 1.0 }, | ||
kaleidoSegments: { value: 6.0 }, | ||
colorIntensity: { value: 0.7 }, | ||
}, | ||
}; | ||
|
||
return ( | ||
<GenThreeShader | ||
shaderConfig={shaderConfig} | ||
className="w-full h-full" | ||
/> | ||
); | ||
}; | ||
``` | ||
|
||
## Props | ||
|
||
- `className?: string` - Optional CSS class name for the container | ||
- `shaderConfig: ShaderConfig` - Configuration object for the shader | ||
- `onDownload: (ref: () => void) => void` - Callback to receive the download function | ||
|
||
### ShaderConfig Interface | ||
|
||
```typescript | ||
type ShaderConfig = { | ||
vertexShader?: string; // Optional custom vertex shader | ||
fragmentShader: string; // Required fragment shader code | ||
uniforms?: Record<string, THREE.IUniform>; // Optional uniforms | ||
}; | ||
``` | ||
|
||
## Base Uniforms | ||
|
||
The component automatically provides these uniforms to all shaders: | ||
|
||
- `resolution` - Screen resolution (vec2) | ||
- `mouse` - Mouse position (vec2) | ||
- `time` - Elapsed time (float) | ||
- `noiseScale` - Base noise scale (float) | ||
- `colorSpeed` - Color animation speed (float) | ||
|
||
## Example with Custom Uniforms | ||
|
||
```tsx | ||
const customConfig = { | ||
vertexShader: ` | ||
void main() { | ||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); | ||
} | ||
`, | ||
fragmentShader: myCustomShader, | ||
uniforms: { | ||
customValue: { value: 1.0 }, | ||
customColor: { value: new THREE.Color(1, 0, 0) }, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { GenThreeShader } from "./GenThreeShader"; | ||
export { genShaders } from "./shaders"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { ShaderVariant } from "./types"; | ||
import { wavesShader } from "./wavesShader"; | ||
|
||
export const genShaders: Record<ShaderVariant, string> = { | ||
wavesShader, | ||
}; | ||
|
||
export type { ShaderVariant } from "./types"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type ShaderVariant = "wavesShader"; |
Oops, something went wrong.