/ src / features / dreamnode / shaders / radialGradient.ts
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;