Skip to content

Commit abc4a02

Browse files
committed
feat: add Branded AI Assistant shader and integrate into shader list
1 parent 9d88c30 commit abc4a02

File tree

8 files changed

+187
-15
lines changed

8 files changed

+187
-15
lines changed
313 KB
Loading
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:flutter/material.dart';
2+
import 'dart:ui';
3+
import 'shader_builder.dart';
4+
5+
class BrandedAiAssistantShaderBuilder extends CustomShaderBuilder {
6+
const BrandedAiAssistantShaderBuilder();
7+
8+
@override
9+
bool get requiresImageSampler => false;
10+
11+
@override
12+
Duration? get animationDuration => null; // Unbounded animation
13+
14+
@override
15+
void setUniforms(FragmentShader shader, Size size, double time) {
16+
shader
17+
..setFloat(0, size.width)
18+
..setFloat(1, size.height)
19+
..setFloat(2, time);
20+
}
21+
22+
@override
23+
Widget buildShader(
24+
ShaderMetadata metadata,
25+
FragmentShader shader,
26+
Size size,
27+
double time,
28+
Widget? child,
29+
) {
30+
return ClipRect(
31+
child: MouseRegion(
32+
onHover: (event) {
33+
// Update shader uniforms based on hover position
34+
final hoverPosition = event.localPosition;
35+
shader.setFloat(3, hoverPosition.dx);
36+
shader.setFloat(4, hoverPosition.dy);
37+
},
38+
child: CustomPaint(
39+
size: Size.infinite,
40+
painter: _BrandedAiAssistantShaderPainter(shader),
41+
),
42+
),
43+
);
44+
}
45+
46+
@override
47+
Widget? childBuilder(BuildContext context) {
48+
return null;
49+
}
50+
}
51+
52+
class _BrandedAiAssistantShaderPainter extends CustomPainter {
53+
final FragmentShader shader;
54+
55+
_BrandedAiAssistantShaderPainter(this.shader);
56+
57+
@override
58+
void paint(Canvas canvas, Size size) {
59+
canvas.drawRect(
60+
Rect.fromLTWH(0, 0, size.width, size.height),
61+
Paint()..shader = shader,
62+
);
63+
}
64+
65+
@override
66+
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
67+
}

lib/main.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'ntsc_shader_builder.dart';
1717
import 'rings_shader_builder.dart';
1818
import 'shader_screen.dart';
1919
import 'ai_assistant_shader_builder.dart';
20+
import 'branded_ai_assistant_shader_builder.dart';
2021

2122
void main() {
2223
usePathUrlStrategy();
@@ -117,14 +118,14 @@ final shaders = [
117118
assetKey: 'shaders/clearly_bug_shader.frag',
118119
description: 'A "Happy Accident" raymarching shader with fractal patterns and beautiful lighting.',
119120
sourceUrl: 'https://www.shadertoy.com/view/33cGDj',
120-
author: 'Various (see comments)',
121+
author: 'mrange',
121122
dateAdded: DateTime(2025, 7, 29),
122123
builder: const ClearlyBugShaderBuilder(),
123124
path: 'clearly-bug-shader',
124125
),
125126
ShaderInfo(
126127
name: 'AI Assistant',
127-
assetKey: 'shaders/ai_assistant.frag',
128+
assetKey: 'shaders/branded_ai_assistant.frag',
128129
description: 'A rotating effect resembling an AI assistant.',
129130
sourceUrl: 'https://www.shadertoy.com/view/MXsyzl',
130131
author: 'Saphirah',
@@ -135,6 +136,18 @@ final shaders = [
135136
backgroundColor: Colors.black,
136137
aspectRatio: 1,
137138
),
139+
ShaderInfo(
140+
assetKey: 'shaders/branded_ai_assistant.frag',
141+
name: 'Branded AI Assistant',
142+
description: 'Simple scifi ai assistant orb. Use mouse hover to interact.',
143+
sourceUrl: 'https://www.shadertoy.com/view/tfcGD8',
144+
author: 'Wickone',
145+
dateAdded: DateTime(2025, 8, 21),
146+
builder: const BrandedAiAssistantShaderBuilder(),
147+
path: 'branded-ai-assistant',
148+
backgroundColor: Colors.black,
149+
aspectRatio: 1,
150+
),
138151
];
139152

140153
// Helper function to create URL-safe shader names

lib/widgets/shader_card.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ class ShaderCard extends StatelessWidget {
3939
TextSpan(
4040
children: [
4141
TextSpan(text: shaderInfo.description),
42-
TextSpan(text: 'by ${shaderInfo.author}'),
42+
TextSpan(text: ' • '),
43+
TextSpan(
44+
text: shaderInfo.author,
45+
style: ShadTheme.of(context).textTheme.muted,
46+
),
4347
],
4448
style: ShadTheme.of(context).textTheme.muted,
4549
),

pubspec.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,26 +256,26 @@ packages:
256256
dependency: transitive
257257
description:
258258
name: leak_tracker
259-
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
259+
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
260260
url: "https://pub.dev"
261261
source: hosted
262-
version: "10.0.9"
262+
version: "11.0.1"
263263
leak_tracker_flutter_testing:
264264
dependency: transitive
265265
description:
266266
name: leak_tracker_flutter_testing
267-
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
267+
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
268268
url: "https://pub.dev"
269269
source: hosted
270-
version: "3.0.9"
270+
version: "3.0.10"
271271
leak_tracker_testing:
272272
dependency: transitive
273273
description:
274274
name: leak_tracker_testing
275-
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
275+
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
276276
url: "https://pub.dev"
277277
source: hosted
278-
version: "3.0.1"
278+
version: "3.0.2"
279279
lints:
280280
dependency: transitive
281281
description:
@@ -493,10 +493,10 @@ packages:
493493
dependency: transitive
494494
description:
495495
name: test_api
496-
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
496+
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
497497
url: "https://pub.dev"
498498
source: hosted
499-
version: "0.7.4"
499+
version: "0.7.6"
500500
two_dimensional_scrollables:
501501
dependency: transitive
502502
description:
@@ -613,10 +613,10 @@ packages:
613613
dependency: transitive
614614
description:
615615
name: vector_math
616-
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
616+
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
617617
url: "https://pub.dev"
618618
source: hosted
619-
version: "2.1.4"
619+
version: "2.2.0"
620620
vm_service:
621621
dependency: transitive
622622
description:

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ flutter:
5050
- shaders/rings_shader.frag
5151
- shaders/clearly_bug_shader.frag
5252
- shaders/ai_assistant.frag
53+
- shaders/branded_ai_assistant.frag

shaders/ai_assistant.frag

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ float circleMask(float distance){
5555
return pow(saturate(distance * circleSizeConfigured), 50.0);
5656
}
5757

58-
void main()
59-
{
58+
void main() {
6059
vec2 fragCoord = FlutterFragCoord().xy;
6160
// UV Map
6261
float aspectRatio = iResolution.x / iResolution.y;

shaders/branded_ai_assistant.frag

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#version 460 core
2+
precision mediump float;
3+
4+
#include <flutter/runtime_effect.glsl>
5+
6+
uniform vec2 iResolution;
7+
uniform float iTime;
8+
uniform vec2 iMouse;
9+
10+
out vec4 fragColor;
11+
12+
#define PI 3.14159265359
13+
#define TAU 6.28318530718
14+
15+
float hash21(vec2 p) {
16+
p = fract(p * vec2(234.34, 435.345));
17+
p += dot(p, p + 34.23);
18+
return fract(p.x * p.y);
19+
}
20+
21+
float noise(vec2 p) {
22+
vec2 i = floor(p);
23+
vec2 f = fract(p);
24+
f = f * f * (3.0 - 2.0 * f);
25+
float a = hash21(i);
26+
float b = hash21(i + vec2(1.0, 0.0));
27+
float c = hash21(i + vec2(0.0, 1.0));
28+
float d = hash21(i + vec2(1.0, 1.0));
29+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
30+
}
31+
32+
float fbm(vec2 p) {
33+
float sum = 0.0;
34+
float amp = 0.5;
35+
float freq = 1.0;
36+
for (int i = 0; i < 6; i++) {
37+
sum += noise(p * freq) * amp;
38+
amp *= 0.5;
39+
freq *= 2.0;
40+
}
41+
return sum;
42+
}
43+
44+
void main() {
45+
vec2 fragCoord = FlutterFragCoord().xy;
46+
47+
vec2 uv = fragCoord / iResolution.xy;
48+
vec2 aspect = vec2(iResolution.x / iResolution.y, 1.0);
49+
uv = (uv - 0.5) * aspect;
50+
51+
vec2 mouse = (iMouse.xy / iResolution.xy - 0.5) * aspect;
52+
float mouseDist = length(uv - mouse);
53+
54+
vec3 col = vec3(0.0);
55+
56+
float radius = 0.3 + sin(iTime * 0.5) * 0.02;
57+
float d = length(uv);
58+
59+
float angle = atan(uv.y, uv.x);
60+
float wave = sin(angle * 3.0 + iTime) * 0.1;
61+
float wave2 = cos(angle * 5.0 - iTime * 1.3) * 0.08;
62+
63+
float noise1 = fbm(uv * 3.0 + iTime * 0.1);
64+
float noise2 = fbm(uv * 5.0 - iTime * 0.2);
65+
66+
vec3 orbColor = vec3(0.2, 0.6, 1.0);
67+
float orb = smoothstep(radius + wave + wave2, radius - 0.1 + wave + wave2, d);
68+
69+
vec3 gradient1 = vec3(0.8, 0.2, 0.5) * sin(angle + iTime);
70+
vec3 gradient2 = vec3(0.2, 0.5, 1.0) * cos(angle - iTime * 0.7);
71+
72+
float particles = 0.0;
73+
for (float i = 0.0; i < 3.0; i++) {
74+
vec2 particlePos = vec2(
75+
sin(iTime * (0.5 + i * 0.2)) * 0.5,
76+
cos(iTime * (0.3 + i * 0.2)) * 0.5
77+
);
78+
particles += smoothstep(0.05, 0.0, length(uv - particlePos));
79+
}
80+
81+
col += orb * mix(orbColor, gradient1, noise1);
82+
col += orb * mix(gradient2, orbColor, noise2) * 0.5;
83+
col += particles * vec3(0.5, 0.8, 1.0);
84+
col += exp(-d * 4.0) * vec3(0.2, 0.4, 0.8) * 0.5;
85+
col += exp(-mouseDist * 8.0) * vec3(0.5, 0.7, 1.0) * 0.2;
86+
87+
fragColor = vec4(col, 1.0);
88+
}

0 commit comments

Comments
 (0)