radialGradient.ts
1 /** 2 * Radial Gradient Shader 3 * 4 * Creates a circular fade-to-black effect at the edges of the DreamTalk. 5 * Used for WebGL-native DreamTalk rendering. 6 */ 7 8 import * as THREE from 'three'; 9 import { shaderMaterial } from '@react-three/drei'; 10 11 // Vertex shader - standard UV pass-through 12 export const radialGradientVertexShader = /* glsl */ ` 13 varying vec2 vUv; 14 15 void main() { 16 vUv = uv; 17 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 18 } 19 `; 20 21 // Fragment shader - applies radial gradient and circular clipping 22 export const radialGradientFragmentShader = /* glsl */ ` 23 uniform sampler2D mediaTexture; 24 uniform float fadeStart; 25 uniform float fadeEnd; 26 uniform vec3 fadeColor; 27 uniform float opacity; 28 29 varying vec2 vUv; 30 31 void main() { 32 // Sample the media texture 33 vec4 mediaColor = texture2D(mediaTexture, vUv); 34 35 // Calculate distance from center (0 at center, 1 at edge) 36 // UV coords are 0-1, so center is at (0.5, 0.5) 37 vec2 center = vec2(0.5, 0.5); 38 float dist = length(vUv - center) * 2.0; // 0 at center, 1 at edge 39 40 // Discard pixels outside the circle 41 if (dist > 1.0) { 42 discard; 43 } 44 45 // Calculate fade factor using smoothstep 46 // fadeStart: where fade begins (e.g., 0.7 = 70% from center) 47 // fadeEnd: where fade completes (e.g., 1.0 = at edge) 48 float fadeAlpha = smoothstep(fadeStart, fadeEnd, dist); 49 50 // Mix media color with fade color (typically black) 51 vec3 finalColor = mix(mediaColor.rgb, fadeColor, fadeAlpha); 52 53 // Output with opacity 54 gl_FragColor = vec4(finalColor, mediaColor.a * opacity); 55 } 56 `; 57 58 // Default uniforms for the shader 59 export const radialGradientUniforms = { 60 mediaTexture: { value: null as THREE.Texture | null }, 61 fadeStart: { value: 0.7 }, // Fade starts at 70% from center 62 fadeEnd: { value: 1.0 }, // Fade completes at edge 63 fadeColor: { value: new THREE.Color(0x000000) }, // Fade to black 64 opacity: { value: 1.0 } 65 }; 66 67 // Create a reusable shader material using drei's shaderMaterial helper 68 export const RadialGradientMaterial = shaderMaterial( 69 { 70 mediaTexture: null, 71 fadeStart: 0.7, 72 fadeEnd: 1.0, 73 fadeColor: new THREE.Color(0x000000), 74 opacity: 1.0 75 }, 76 radialGradientVertexShader, 77 radialGradientFragmentShader 78 ); 79 80 // Extend JSX namespace for TypeScript 81 import type { JSX as ReactJSX } from 'react'; 82 declare global { 83 namespace JSX { 84 interface IntrinsicElements { 85 radialGradientMaterial: ReactJSX.IntrinsicElements['meshBasicMaterial'] & { 86 mediaTexture?: THREE.Texture | null; 87 fadeStart?: number; 88 fadeEnd?: number; 89 fadeColor?: THREE.Color; 90 opacity?: number; 91 transparent?: boolean; 92 side?: THREE.Side; 93 }; 94 } 95 } 96 } 97 98 export default RadialGradientMaterial;