@@ -8,22 +8,28 @@ import {
88 signal ,
99} from '@angular/core' ;
1010import * as THREE from 'three/webgpu' ;
11+ import { MeshStandardNodeMaterial } from 'three/webgpu' ;
1112import { SceneService } from '../../canvas/scene.service' ;
1213import { RenderLoopService } from '../../render-loop/render-loop.service' ;
1314import { TextSamplingService } from '../../services/text-sampling.service' ;
1415import { OBJECT_ID } from '../../tokens/object-id.token' ;
1516import { NG_3D_PARENT } from '../../types/tokens' ;
17+ import { tslFresnel , tslIridescence } from '../shaders/tsl-utilities' ;
18+ // eslint-disable-next-line @nx/enforce-module-boundaries
19+ import * as TSL from 'three/tsl' ;
20+
21+ const { float, vec3, color, mix } = TSL ;
1622
1723/**
1824 * BubbleTextComponent - 3D Bubble Text using Instanced Meshes
1925 *
2026 * Creates transparent bubble spheres that form text shape, inspired by
21- * Codrops bubble typer effect. Uses IcosahedronGeometry with custom
27+ * Codrops bubble typer effect. Uses IcosahedronGeometry with TSL-based
2228 * rim-lighting shader for realistic soap bubble appearance.
2329 *
2430 * Features:
2531 * - IcosahedronGeometry spheres (not flat planes)
26- * - Custom bubble shader (transparent center, opaque rim)
32+ * - TSL bubble material (transparent center, opaque rim) - WebGPU native
2733 * - Grow → Burst → Regrow lifecycle animation
2834 * - Optional flying bubbles that float upward
2935 * - Billboard-free (true 3D spheres)
@@ -89,7 +95,7 @@ export class BubbleTextComponent {
8995
9096 // Internal state
9197 private bubbleGeometry ?: THREE . IcosahedronGeometry ;
92- private bubbleMaterial ?: THREE . ShaderMaterial ;
98+ private bubbleMaterial ?: MeshStandardNodeMaterial ;
9399 private instancedMesh ?: THREE . InstancedMesh ;
94100 private dummy = new THREE . Object3D ( ) ;
95101
@@ -132,7 +138,7 @@ export class BubbleTextComponent {
132138 if ( this . instancedMesh && parent ) {
133139 parent . remove ( this . instancedMesh ) ;
134140 this . instancedMesh . geometry . dispose ( ) ;
135- ( this . instancedMesh . material as THREE . ShaderMaterial ) . dispose ( ) ;
141+ ( this . instancedMesh . material as MeshStandardNodeMaterial ) . dispose ( ) ;
136142 }
137143 if ( this . bubbleGeometry ) {
138144 this . bubbleGeometry . dispose ( ) ;
@@ -152,7 +158,7 @@ export class BubbleTextComponent {
152158 if ( this . instancedMesh && parent ) {
153159 parent . remove ( this . instancedMesh ) ;
154160 this . instancedMesh . geometry . dispose ( ) ;
155- ( this . instancedMesh . material as THREE . ShaderMaterial ) . dispose ( ) ;
161+ ( this . instancedMesh . material as MeshStandardNodeMaterial ) . dispose ( ) ;
156162 this . instancedMesh = undefined ;
157163 }
158164 this . bubbles = [ ] ;
@@ -243,18 +249,16 @@ export class BubbleTextComponent {
243249 if ( this . instancedMesh && parent ) {
244250 parent . remove ( this . instancedMesh ) ;
245251 this . instancedMesh . geometry . dispose ( ) ;
246- ( this . instancedMesh . material as THREE . ShaderMaterial ) . dispose ( ) ;
252+ ( this . instancedMesh . material as MeshStandardNodeMaterial ) . dispose ( ) ;
247253 }
248254
249255 // Create geometry (Icosahedron for spherical bubbles)
250256 if ( ! this . bubbleGeometry ) {
251257 this . bubbleGeometry = new THREE . IcosahedronGeometry ( 1 , 2 ) ; // Detail level 2
252258 }
253259
254- // Create bubble shader material
255- if ( ! this . bubbleMaterial ) {
256- this . bubbleMaterial = this . createBubbleShaderMaterial ( ) ;
257- }
260+ // Create TSL bubble material
261+ this . bubbleMaterial = this . createBubbleMaterial ( ) ;
258262
259263 // Create instanced mesh
260264 this . instancedMesh = new THREE . InstancedMesh (
@@ -273,59 +277,39 @@ export class BubbleTextComponent {
273277 }
274278
275279 /**
276- * Create bubble shader material with rim transparency effect
280+ * Create TSL-based bubble material with rim transparency effect
281+ * Uses native TSL nodes for WebGPU/WebGL compatibility
277282 */
278- private createBubbleShaderMaterial ( ) : THREE . ShaderMaterial {
279- const bubbleColor = new THREE . Color ( this . bubbleColor ( ) ) ;
280-
281- return new THREE . ShaderMaterial ( {
282- uniforms : {
283- uColor : { value : bubbleColor } ,
284- uOpacity : { value : this . opacity ( ) } ,
285- } ,
286- vertexShader : `
287- varying vec3 vNormal;
288- varying vec3 vViewPosition;
289-
290- void main() {
291- vNormal = normalize(normalMatrix * normal);
292- vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
293- vViewPosition = -mvPosition.xyz;
294- gl_Position = projectionMatrix * mvPosition;
295- }
296- ` ,
297- fragmentShader : `
298- uniform vec3 uColor;
299- uniform float uOpacity;
300-
301- varying vec3 vNormal;
302- varying vec3 vViewPosition;
303-
304- void main() {
305- // Fresnel-like rim effect
306- vec3 viewDir = normalize(vViewPosition);
307- float rim = 1.0 - abs(dot(vNormal, viewDir));
308- rim = pow(rim, 2.0);
309-
310- // Mix white center with colored rim (bubble refraction look)
311- vec3 centerColor = vec3(1.0, 1.0, 1.0);
312- vec3 rimColor = uColor;
313- vec3 finalColor = mix(centerColor, rimColor, rim * 0.7);
314-
315- // Add rainbow iridescence
316- float rainbow = sin(rim * 6.28) * 0.5 + 0.5;
317- finalColor += vec3(rainbow * 0.1, rainbow * 0.05, rainbow * 0.15);
318-
319- // Alpha: more transparent at center, more opaque at rim
320- float alpha = (0.2 + rim * 0.6) * uOpacity;
321-
322- gl_FragColor = vec4(finalColor, alpha);
323- }
324- ` ,
325- transparent : true ,
326- side : THREE . DoubleSide ,
327- depthWrite : false ,
328- } ) ;
283+ private createBubbleMaterial ( ) : MeshStandardNodeMaterial {
284+ const bubbleColorValue = new THREE . Color ( this . bubbleColor ( ) ) ;
285+ const opacityValue = this . opacity ( ) ;
286+
287+ // Create TSL color node
288+ const baseColor = color ( bubbleColorValue ) ;
289+
290+ // Calculate fresnel rim effect using TSL utilities
291+ const fresnelValue = tslFresnel ( float ( 2.0 ) , float ( 0.6 ) , float ( 0.2 ) ) ;
292+
293+ // Mix white center with colored rim (bubble refraction look)
294+ const centerColor = vec3 ( 1 , 1 , 1 ) ;
295+ const finalColor = mix ( centerColor , baseColor , fresnelValue . mul ( 0.7 ) ) ;
296+
297+ // Add rainbow iridescence using TSL utilities
298+ const iridescent = tslIridescence ( fresnelValue , float ( 0.1 ) ) ;
299+ const colorWithIridescence = finalColor . add ( iridescent ) ;
300+
301+ // Alpha: more transparent at center, more opaque at rim
302+ const alpha = float ( 0.2 ) . add ( fresnelValue . mul ( 0.6 ) ) . mul ( opacityValue ) ;
303+
304+ // Create MeshStandardNodeMaterial with TSL nodes
305+ const material = new MeshStandardNodeMaterial ( ) ;
306+ material . colorNode = colorWithIridescence ;
307+ material . opacityNode = alpha ;
308+ material . transparent = true ;
309+ material . side = THREE . DoubleSide ;
310+ material . depthWrite = false ;
311+
312+ return material ;
329313 }
330314
331315 /**
0 commit comments