@firecms/neat 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/shaders.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export const vertexShaderSource = `void main() {
2
2
  vUv = uv;
3
+ vPosition = position;
3
4
 
4
5
  // SCROLLING LOGIC
5
6
  // Separate multipliers for wave, color, and flow offsets
@@ -41,11 +42,36 @@ export const vertexShaderSource = `void main() {
41
42
  // We take the computed flow UVs and apply the color offset
42
43
  // Scale by plane height to match wave offset speed (world space vs UV space)
43
44
  vec3 color = u_colors[0].color;
44
- // ...
45
- vec2 adjustedUv = flowUv;
46
- adjustedUv.y += colorOffset / u_plane_height; // Scroll the color mixing pattern
47
45
 
48
- vec2 noise_cord = adjustedUv * u_color_pressure;
46
+ vec3 distortedPos = position;
47
+ if (u_shape_type > 0.5) {
48
+ if (u_flow_enabled > 0.5) {
49
+ if (u_flow_ease > 0.0 || u_flow_distortion_a > 0.0) {
50
+ vec3 ppp = position / 25.0;
51
+ ppp.xyz += 0.1 * cos((1.5 * u_flow_scale) * ppp.yxz + 1.1 * u_time + vec3(0.1, 1.1, 2.1));
52
+ ppp.xyz += 0.1 * cos((2.3 * u_flow_scale) * ppp.zxy + 1.3 * u_time + vec3(3.2, 3.4, 1.2));
53
+ ppp.xyz += 0.1 * cos((2.2 * u_flow_scale) * ppp.yxz + 1.7 * u_time + vec3(1.8, 5.2, 3.1));
54
+ ppp.xyz += u_flow_distortion_a * cos((u_flow_distortion_b * u_flow_scale) * ppp.zxy + 1.4 * u_time + vec3(6.3, 3.9, 4.5));
55
+
56
+ float r = length(ppp);
57
+ distortedPos = mix(position, vec3(
58
+ position.x * (1.0 - u_flow_ease) + r * u_flow_ease * 25.0,
59
+ position.y,
60
+ position.z * (1.0 - u_flow_ease) + r * u_flow_ease * 25.0
61
+ ), u_flow_ease);
62
+ }
63
+ }
64
+ }
65
+
66
+ vec3 noise_cord;
67
+ if (u_shape_type > 0.5) {
68
+ noise_cord = vec3(distortedPos.x / 50.0, (distortedPos.y + colorOffset) / 50.0, distortedPos.z / 50.0);
69
+ } else {
70
+ vec2 adjustedUv = flowUv;
71
+ adjustedUv.y += colorOffset / u_plane_height;
72
+ noise_cord = vec3(adjustedUv, 0.0);
73
+ }
74
+
49
75
  const float minNoise = .0;
50
76
  const float maxNoise = .9;
51
77
 
@@ -56,11 +82,16 @@ export const vertexShaderSource = `void main() {
56
82
  float noiseSpeed = (1. + float(i)) * 0.11;
57
83
  float noiseSeed = 13. + float(i) * 7.;
58
84
 
85
+ float noise_z = u_time * noiseSpeed;
86
+ if (u_shape_type > 0.5) {
87
+ noise_z = noise_cord.z * u_color_pressure.x * u_color_pressure.x + u_time * noiseSpeed;
88
+ }
89
+
59
90
  float noise = snoise(
60
91
  vec3(
61
- noise_cord.x * u_color_pressure.x + u_time * noiseFlow * 2.,
62
- noise_cord.y * u_color_pressure.y,
63
- u_time * noiseSpeed
92
+ noise_cord.x * u_color_pressure.x * u_color_pressure.x + u_time * noiseFlow * 2.,
93
+ noise_cord.y * u_color_pressure.y * u_color_pressure.y,
94
+ noise_z
64
95
  ) + noiseSeed
65
96
  ) - (.1 * float(i)) + (.5 * u_color_blending);
66
97
 
@@ -72,9 +103,15 @@ export const vertexShaderSource = `void main() {
72
103
 
73
104
  v_color = color;
74
105
 
75
- // 4. VERTEX POSITION
106
+ // 4. FRESNEL (rim glow)
107
+ // (Calculated in fragment shader using displacement slope approximation)
108
+
109
+ // 5. VERTEX POSITION
76
110
  vec3 newPosition = position + normal * v_displacement_amount * u_wave_amplitude;
77
- gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
111
+ vec4 mvPosition = modelViewMatrix * vec4(newPosition, 1.0);
112
+ vViewPosition = mvPosition.xyz;
113
+ vNormal = normalize((modelViewMatrix * vec4(normal, 0.0)).xyz);
114
+ gl_Position = projectionMatrix * mvPosition;
78
115
  v_new_position = gl_Position;
79
116
  }
80
117
  `;
@@ -95,58 +132,182 @@ float fbm(vec3 x) {
95
132
  return value;
96
133
  }
97
134
 
135
+ // Branchless HSL to RGB for iridescence
136
+ vec3 hsl2rgb(float h, float s, float l) {
137
+ vec3 rgb = clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
138
+ return l + s * (rgb - 0.5) * (1.0 - abs(2.0 * l - 1.0));
139
+ }
140
+
98
141
  void main() {
99
142
  vec2 finalUv = vFlowUv;
100
143
 
101
144
  vec3 baseColor;
145
+ float texAlpha = 1.0;
102
146
 
103
147
  if (u_enable_procedural_texture > 0.5) {
104
- // Calculate flow field distance for ease effect
105
- vec2 ppp = -1.0 + 2.0 * finalUv;
106
- ppp += 0.1 * cos((1.5 * u_flow_scale) * ppp.yx + 1.1 * u_time + vec2(0.1, 1.1));
107
- ppp += 0.1 * cos((2.3 * u_flow_scale) * ppp.yx + 1.3 * u_time + vec2(3.2, 3.4));
108
- ppp += 0.1 * cos((2.2 * u_flow_scale) * ppp.yx + 1.7 * u_time + vec2(1.8, 5.2));
109
- ppp += u_flow_distortion_a * cos((u_flow_distortion_b * u_flow_scale) * ppp.yx + 1.4 * u_time + vec2(6.3, 3.9));
110
- float r = length(ppp); // Flow distance
111
-
112
- // Ease blending: 0 = topographic (flow), 1 = image (UV)
113
- float vx = (finalUv.x * u_texture_ease) + (r * (1.0 - u_texture_ease));
114
- float vy = (finalUv.y * u_texture_ease) + (0.0 * (1.0 - u_texture_ease));
115
- vec2 texUv = vec2(vx, vy);
116
-
117
- // PARALLAX SCROLLING
118
- // We manually apply a smaller offset here to make the texture lag behind
119
- float parallaxFactor = 0.25; // 25% speed of the color mixing
120
- texUv.y -= (u_y_offset * u_y_offset_color_multiplier / u_plane_height) * parallaxFactor;
121
-
122
- texUv *= 1.5; // Tiling scale
123
-
124
- vec4 texSample = texture2D(u_procedural_texture, texUv);
125
- baseColor = texSample.rgb;
148
+ if (u_shape_type > 0.5) {
149
+ float parallaxFactor = 0.25;
150
+ float scrollOffset = (u_y_offset * u_y_offset_color_multiplier) * parallaxFactor;
151
+ vec3 scrolledPos = vPosition;
152
+ scrolledPos.y -= scrollOffset;
153
+
154
+ vec3 p = (scrolledPos * 1.5) / 50.0;
155
+ vec2 uvX = p.yz + vec2(0.5);
156
+ vec2 uvY = p.zx + vec2(0.5);
157
+ vec2 uvZ = p.xy + vec2(0.5);
158
+
159
+ vec4 colX = texture2D(u_procedural_texture, fract(uvX));
160
+ vec4 colY = texture2D(u_procedural_texture, fract(uvY));
161
+ vec4 colZ = texture2D(u_procedural_texture, fract(uvZ));
162
+
163
+ vec3 n = normalize(vNormal);
164
+ vec3 blendWeights = abs(n);
165
+ blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z + 0.0001);
166
+
167
+ vec4 texSample = colX * blendWeights.x + colY * blendWeights.y + colZ * blendWeights.z;
168
+ baseColor = texSample.rgb;
169
+ if (u_transparent_texture_void > 0.5) {
170
+ texAlpha = texSample.a;
171
+ }
172
+ } else {
173
+ vec2 ppp = -1.0 + 2.0 * finalUv;
174
+ ppp += 0.1 * cos((1.5 * u_flow_scale) * ppp.yx + 1.1 * u_time + vec2(0.1, 1.1));
175
+ ppp += 0.1 * cos((2.3 * u_flow_scale) * ppp.yx + 1.3 * u_time + vec2(3.2, 3.4));
176
+ ppp += 0.1 * cos((2.2 * u_flow_scale) * ppp.yx + 1.7 * u_time + vec2(1.8, 5.2));
177
+ ppp += u_flow_distortion_a * cos((u_flow_distortion_b * u_flow_scale) * ppp.yx + 1.4 * u_time + vec2(6.3, 3.9));
178
+ float r = length(ppp);
179
+
180
+ float vx = (finalUv.x * u_texture_ease) + (r * (1.0 - u_texture_ease));
181
+ float vy = (finalUv.y * u_texture_ease) + (0.0 * (1.0 - u_texture_ease));
182
+ vec2 texUv = vec2(vx, vy);
183
+
184
+ float parallaxFactor = 0.25;
185
+ texUv.y -= (u_y_offset * u_y_offset_color_multiplier / u_plane_height) * parallaxFactor;
186
+ texUv *= 1.5;
187
+
188
+ vec4 texSample = texture2D(u_procedural_texture, fract(texUv));
189
+ baseColor = texSample.rgb;
190
+ if (u_transparent_texture_void > 0.5) {
191
+ texAlpha = texSample.a;
192
+ }
193
+ }
126
194
  } else {
127
195
  baseColor = v_color;
128
196
  }
129
197
 
130
198
  vec3 color = baseColor;
131
199
 
200
+ // === DOMAIN WARPING (simplified: 3 fbm calls instead of 5) ===
201
+ if (u_domain_warp_enabled > 0.5) {
202
+ vec3 p;
203
+ if (u_shape_type > 0.5) {
204
+ p = vec3((vPosition / 50.0 + vec3(0.5)) * u_domain_warp_scale);
205
+ p.z += u_time * 0.15;
206
+ } else {
207
+ p = vec3(finalUv * u_domain_warp_scale, u_time * 0.15);
208
+ }
209
+ vec2 q = vec2(fbm(p), fbm(p + vec3(5.2, 1.3, 0.0)));
210
+ float f = fbm(p + vec3(4.0 * q, 0.0));
211
+ vec3 warpColor = color * (1.0 + f * 0.8 * u_domain_warp_intensity);
212
+ float pattern = clamp(f * f * f + 0.6 * f * f + 0.5 * f, 0.0, 1.0);
213
+ color = mix(color, warpColor * (0.6 + pattern * 0.8), u_domain_warp_intensity * 0.7);
214
+ }
215
+
132
216
  // Post-processing
133
- color += v_displacement_amount * u_highlights;
134
- // Replace pow() with direct multiplication to avoid negative base undefined behavior in GLSL
135
- float shadowFactor = 1.0 - v_displacement_amount;
136
- color -= shadowFactor * shadowFactor * u_shadows;
217
+ // Compute dynamic pixel-perfect normal using smooth normal
218
+ vec3 normal = normalize(vNormal);
219
+ vec3 viewDir = vec3(0.0, 0.0, 1.0);
220
+ float ndotv = dot(normal, viewDir);
221
+
222
+ // Cull back-faces for closed 3D shapes (Sphere=1, Torus=2, Cylinder=3)
223
+ if (u_shape_type > 0.5 && u_shape_type < 3.5) {
224
+ if (ndotv < 0.0) {
225
+ discard;
226
+ }
227
+ } else {
228
+ // Double-sided shapes (Plane, Ribbon): flip normal if back-facing
229
+ if (ndotv < 0.0) {
230
+ normal = -normal;
231
+ ndotv = -ndotv;
232
+ }
233
+ }
234
+ vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
235
+ float diffuse = max(dot(normal, lightDir), 0.0);
236
+ vec3 halfDir = normalize(lightDir + viewDir);
237
+ float specular = pow(max(dot(normal, halfDir), 0.0), 32.0);
238
+
239
+ // Blend smooth 3D shading with smooth height-based wave shading
240
+ if (u_shape_type <= 0.5) {
241
+ // Original height-based wave shading
242
+ color += v_displacement_amount * u_highlights;
243
+ float heightShadow = 1.0 - v_displacement_amount;
244
+ color -= heightShadow * heightShadow * u_shadows;
245
+ } else {
246
+ // 3D shading
247
+ color += specular * u_highlights;
248
+ color += v_displacement_amount * u_highlights * 0.5;
249
+ float heightShadow = 1.0 - v_displacement_amount;
250
+ color -= heightShadow * heightShadow * u_shadows * 0.5;
251
+ color -= (1.0 - diffuse) * u_shadows * 0.5;
252
+ }
137
253
  color = saturation(color, 1.0 + u_saturation);
138
254
  color = color * u_brightness;
139
255
 
140
- // Grain
141
- vec2 noiseCoords = gl_FragCoord.xy / u_grain_scale;
256
+ // === IRIDESCENCE ===
257
+ if (u_iridescence_enabled > 0.5) {
258
+ float hue = fract(v_displacement_amount * 0.5 + 0.5 + u_time * u_iridescence_speed * 0.05);
259
+ vec3 iriColor = hsl2rgb(hue, 0.8, 0.6);
260
+ color = mix(color, iriColor, u_iridescence_intensity * abs(v_displacement_amount) * 0.6);
261
+ }
262
+
263
+ // === FRESNEL (Rim glow) ===
264
+ if (u_fresnel_enabled > 0.5) {
265
+ float slope = 1.0 - abs(v_displacement_amount);
266
+ float fresnel = pow(max(slope, 0.0), u_fresnel_power);
267
+ color += u_fresnel_color * fresnel * u_fresnel_intensity;
268
+ }
269
+
270
+ // === VIGNETTE ===
271
+ if (u_vignette_intensity > 0.0) {
272
+ vec2 vigUv = vUv;
273
+ if (u_shape_type > 0.5) {
274
+ vigUv = (v_new_position.xy / v_new_position.w) * 0.5 + vec2(0.5);
275
+ }
276
+ float dist = length(vigUv - vec2(0.5));
277
+ float vig = smoothstep(u_vignette_radius, u_vignette_radius * 0.3, dist);
278
+ color *= mix(1.0, vig, u_vignette_intensity);
279
+ }
280
+
281
+ // === FAKE BLOOM ===
282
+ if (u_bloom_intensity > 0.0) {
283
+ float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
284
+ float bloomMask = smoothstep(u_bloom_threshold, 1.0, luma);
285
+ color += color * bloomMask * u_bloom_intensity;
286
+ }
287
+
288
+ // === CHROMATIC ABERRATION ===
289
+ if (u_chromatic_aberration > 0.0) {
290
+ float caAmount = u_chromatic_aberration * 0.008;
291
+ vec2 caUv = vUv;
292
+ if (u_shape_type > 0.5) {
293
+ caUv = (v_new_position.xy / v_new_position.w) * 0.5 + vec2(0.5);
294
+ }
295
+ float dist = length(caUv - vec2(0.5));
296
+ float rShift = v_displacement_amount + caAmount * dist;
297
+ float bShift = v_displacement_amount - caAmount * dist;
298
+ color.r *= 1.0 + rShift * caAmount * 10.0;
299
+ color.b *= 1.0 - bShift * caAmount * 10.0;
300
+ }
301
+
302
+ // Grain (use cheap hash noise instead of expensive fbm when static)
142
303
  float grain = 0.0;
143
-
144
- // Completely bypass expensive noise generation if grain is disabled
145
304
  if (u_grain_intensity > 0.0) {
146
- if (u_grain_speed != 0.0) {
305
+ vec2 noiseCoords = gl_FragCoord.xy / u_grain_scale;
306
+ if (u_grain_speed != 0.0 || u_shape_type <= 0.5) {
147
307
  grain = fbm(vec3(noiseCoords, u_time * u_grain_speed));
148
308
  } else {
149
- grain = fbm(vec3(noiseCoords, 0.0));
309
+ // Static grain: use cheap hash instead of fbm
310
+ grain = random(noiseCoords) - 0.5;
150
311
  }
151
312
 
152
313
  grain = grain * 0.5 + 0.5;
@@ -157,7 +318,25 @@ void main() {
157
318
 
158
319
  color += vec3(grain);
159
320
 
160
- gl_FragColor = vec4(color, 1.0);
321
+ float edgeAlpha = 1.0;
322
+
323
+ // Silhouette falloff for 3D shapes
324
+ if (u_shape_type > 0.5) {
325
+ edgeAlpha = smoothstep(0.0, u_silhouette_fade, ndotv);
326
+ }
327
+
328
+ // UV boundary falloff for open shapes
329
+ if (u_shape_type == 3.0) { // Cylinder: fade top/bottom ends
330
+ float vFade = smoothstep(0.0, u_cylinder_fade, vUv.y) * smoothstep(1.0, 1.0 - u_cylinder_fade, vUv.y);
331
+ edgeAlpha *= vFade;
332
+ } else if (u_shape_type == 4.0) { // Ribbon: fade all 4 borders
333
+ float uFade = smoothstep(0.0, u_ribbon_fade, vUv.x) * smoothstep(1.0, 1.0 - u_ribbon_fade, vUv.x);
334
+ float vFade = smoothstep(0.0, u_ribbon_fade, vUv.y) * smoothstep(1.0, 1.0 - u_ribbon_fade, vUv.y);
335
+ edgeAlpha *= uFade * vFade;
336
+ }
337
+
338
+ edgeAlpha *= texAlpha;
339
+ gl_FragColor = vec4(color, edgeAlpha);
161
340
  }
162
341
  `;
163
342
 
@@ -176,6 +355,9 @@ varying vec2 vFlowUv;
176
355
  varying vec4 v_new_position;
177
356
  varying vec3 v_color;
178
357
  varying float v_displacement_amount;
358
+ varying vec3 vViewPosition;
359
+ varying vec3 vNormal;
360
+ varying vec3 vPosition;
179
361
 
180
362
  uniform float u_time;
181
363
  uniform vec2 u_resolution;
@@ -206,6 +388,14 @@ uniform float u_flow_distortion_b;
206
388
  uniform float u_flow_scale;
207
389
  uniform float u_flow_ease;
208
390
  uniform float u_flow_enabled;
391
+
392
+ // Fresnel uniforms
393
+ uniform float u_fresnel_enabled;
394
+ uniform float u_fresnel_power;
395
+ uniform float u_fresnel_intensity;
396
+ uniform vec3 u_fresnel_color;
397
+
398
+ uniform float u_shape_type;
209
399
  `;
210
400
  }
211
401
 
@@ -214,10 +404,15 @@ export function buildFragUniforms(): string {
214
404
 
215
405
  varying vec2 vUv;
216
406
  varying vec2 vFlowUv;
407
+ varying vec4 v_new_position;
217
408
  varying vec3 v_color;
218
409
  varying float v_displacement_amount;
410
+ varying vec3 vViewPosition;
411
+ varying vec3 vNormal;
412
+ varying vec3 vPosition;
219
413
 
220
414
  uniform float u_time;
415
+ uniform vec2 u_resolution;
221
416
  uniform float u_plane_height;
222
417
 
223
418
  uniform float u_shadows;
@@ -241,6 +436,40 @@ uniform float u_flow_scale;
241
436
  uniform sampler2D u_procedural_texture;
242
437
  uniform float u_enable_procedural_texture;
243
438
  uniform float u_texture_ease;
439
+
440
+ // Domain warping uniforms
441
+ uniform float u_domain_warp_enabled;
442
+ uniform float u_domain_warp_intensity;
443
+ uniform float u_domain_warp_scale;
444
+
445
+ // Vignette uniforms
446
+ uniform float u_vignette_intensity;
447
+ uniform float u_vignette_radius;
448
+
449
+ // Fresnel uniforms (fragment side)
450
+ uniform float u_fresnel_enabled;
451
+ uniform float u_fresnel_power;
452
+ uniform float u_fresnel_intensity;
453
+ uniform vec3 u_fresnel_color;
454
+
455
+
456
+
457
+ // Iridescence uniforms
458
+ uniform float u_iridescence_enabled;
459
+ uniform float u_iridescence_intensity;
460
+ uniform float u_iridescence_speed;
461
+
462
+ // Bloom uniforms
463
+ uniform float u_bloom_intensity;
464
+ uniform float u_bloom_threshold;
465
+
466
+ // Chromatic aberration
467
+ uniform float u_chromatic_aberration;
468
+ uniform float u_shape_type;
469
+ uniform float u_transparent_texture_void;
470
+ uniform float u_silhouette_fade;
471
+ uniform float u_cylinder_fade;
472
+ uniform float u_ribbon_fade;
244
473
  `;
245
474
  }
246
475
 
package/src/types.ts CHANGED
@@ -41,6 +41,62 @@ export type NeatConfig = {
41
41
  textureShapeCircles?: number;
42
42
  textureShapeBars?: number;
43
43
  textureShapeSquiggles?: number;
44
+
45
+ // Domain warping
46
+ domainWarpEnabled?: boolean;
47
+ domainWarpIntensity?: number;
48
+ domainWarpScale?: number;
49
+
50
+ // Vignette
51
+ vignetteIntensity?: number;
52
+ vignetteRadius?: number;
53
+
54
+ // Fresnel (rim glow)
55
+ fresnelEnabled?: boolean;
56
+ fresnelPower?: number;
57
+ fresnelIntensity?: number;
58
+ fresnelColor?: string;
59
+
60
+ // Iridescence
61
+ iridescenceEnabled?: boolean;
62
+ iridescenceIntensity?: number;
63
+ iridescenceSpeed?: number;
64
+
65
+ // Bloom (fake)
66
+ bloomIntensity?: number;
67
+ bloomThreshold?: number;
68
+
69
+ // Chromatic aberration
70
+ chromaticAberration?: number;
71
+
72
+ // 3D Shapes config
73
+ shapeType?: 'plane' | 'sphere' | 'torus' | 'cylinder' | 'ribbon';
74
+ shapeRotationX?: number;
75
+ shapeRotationY?: number;
76
+ shapeRotationZ?: number;
77
+ shapeAutoRotateSpeedX?: number;
78
+ shapeAutoRotateSpeedY?: number;
79
+ sphereRadius?: number;
80
+ torusRadius?: number;
81
+ torusTube?: number;
82
+ cylinderRadius?: number;
83
+ cylinderHeight?: number;
84
+ planeBend?: number;
85
+ planeTwist?: number;
86
+ transparentTextureVoid?: boolean;
87
+ silhouetteFade?: number;
88
+ cylinderFade?: number;
89
+ ribbonFade?: number;
90
+
91
+ // Camera settings
92
+ cameraLock?: boolean;
93
+ cameraX?: number;
94
+ cameraY?: number;
95
+ cameraZ?: number;
96
+ cameraRotationX?: number;
97
+ cameraRotationY?: number;
98
+ cameraRotationZ?: number;
99
+ cameraZoom?: number;
44
100
  };
45
101
 
46
102
  export type NeatColor = {