Refactor Shiny widget and shader code to remove unused time variable; optimize shader functions by eliminating time dependency for improved performance

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-04-15 12:03:59 +02:00
parent 676da4d1ed
commit eda4a5a90c
2 changed files with 38 additions and 52 deletions
+11 -28
View File
@@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'shiny_controller.dart'; import 'shiny_controller.dart';
@@ -244,30 +243,19 @@ class Shiny extends StatefulWidget {
State<Shiny> createState() => _ShinyState(); State<Shiny> createState() => _ShinyState();
} }
class _ShinyState extends State<Shiny> with TickerProviderStateMixin { class _ShinyState extends State<Shiny> {
static Future<ui.FragmentProgram>? _programFuture; static Future<ui.FragmentProgram>? _programFuture;
ui.FragmentShader? _shader; ui.FragmentShader? _shader;
StreamSubscription<Offset>? _tiltSub; StreamSubscription<Offset>? _tiltSub;
late final Ticker _ticker;
double _time = 0.0;
Offset _tilt = Offset.zero; Offset _tilt = Offset.zero;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_ticker = createTicker((Duration elapsed) {
if (!mounted || _shader == null) return;
setState(() {
_time = elapsed.inMicroseconds / 1000000.0;
});
});
_attachController(); _attachController();
if (widget.enableShader) { if (widget.enableShader) {
_loadShader(); _loadShader();
_ticker.start();
} }
} }
@@ -282,9 +270,6 @@ class _ShinyState extends State<Shiny> with TickerProviderStateMixin {
if (oldWidget.enableShader != widget.enableShader) { if (oldWidget.enableShader != widget.enableShader) {
if (widget.enableShader) { if (widget.enableShader) {
if (_shader == null) _loadShader(); if (_shader == null) _loadShader();
_ticker.start();
} else {
_ticker.stop();
} }
} }
} }
@@ -329,7 +314,6 @@ class _ShinyState extends State<Shiny> with TickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
_ticker.dispose();
_tiltSub?.cancel(); _tiltSub?.cancel();
_shader?.dispose(); _shader?.dispose();
super.dispose(); super.dispose();
@@ -343,17 +327,16 @@ class _ShinyState extends State<Shiny> with TickerProviderStateMixin {
..setFloat(1, bounds.height) ..setFloat(1, bounds.height)
..setFloat(2, effectiveTilt.dx) ..setFloat(2, effectiveTilt.dx)
..setFloat(3, effectiveTilt.dy) ..setFloat(3, effectiveTilt.dy)
..setFloat(4, _time) ..setFloat(4, widget.prismatic)
..setFloat(5, widget.prismatic) ..setFloat(5, widget.sparkle)
..setFloat(6, widget.sparkle) ..setFloat(6, widget.specular)
..setFloat(7, widget.specular) ..setFloat(7, widget.diffraction)
..setFloat(8, widget.diffraction) ..setFloat(8, widget.style.index.toDouble())
..setFloat(9, widget.style.index.toDouble()) ..setFloat(9, widget.sparkleShape.kind.index.toDouble())
..setFloat(10, widget.sparkleShape.kind.index.toDouble()) ..setFloat(10, widget.sparkleShape.primary)
..setFloat(11, widget.sparkleShape.primary) ..setFloat(11, widget.sparkleShape.secondary)
..setFloat(12, widget.sparkleShape.secondary) ..setFloat(12, widget.sparkleShape.tertiary)
..setFloat(13, widget.sparkleShape.tertiary) ..setFloat(13, widget.opacity);
..setFloat(14, widget.opacity);
return shader; return shader;
} }
+27 -24
View File
@@ -5,7 +5,6 @@ out vec4 fragColor;
uniform vec2 uSize; uniform vec2 uSize;
uniform vec2 uTilt; uniform vec2 uTilt;
uniform float uTime;
uniform float uPrismatic; uniform float uPrismatic;
uniform float uSparkle; uniform float uSparkle;
uniform float uSpecular; uniform float uSpecular;
@@ -120,14 +119,15 @@ float sparkleShapeMask(vec2 p, vec2 id) {
return sparkleRectangleMask(p, uSparklePrimary, uSparkleSecondary, randomRotation * uSparkleTertiary); return sparkleRectangleMask(p, uSparklePrimary, uSparkleSecondary, randomRotation * uSparkleTertiary);
} }
vec3 styleHolographicSilver(vec2 uv, vec2 tilt, float time) { vec3 styleHolographicSilver(vec2 uv, vec2 tilt) {
vec2 p = uv * 2.0 - 1.0; vec2 p = uv * 2.0 - 1.0;
float flowA = noise(uv * 3.5 + vec2(time * 0.03, -time * 0.02)); vec2 tiltWarp = tilt * 0.18;
float flowB = noise(uv * 7.0 + vec2(-time * 0.02, time * 0.03)); float flowA = noise(uv * 3.5 + tiltWarp + vec2(dot(uv, vec2(0.25, -0.22))));
float flowB = noise(uv * 7.0 - tiltWarp + vec2(dot(uv, vec2(-0.18, 0.21))));
vec2 warp = p + 0.25 * vec2(flowA - 0.5, flowB - 0.5); vec2 warp = p + 0.25 * vec2(flowA - 0.5, flowB - 0.5);
float ribbon = sin(dot(warp, vec2(6.5, 2.8)) + flowA * 5.0 + dot(tilt, vec2(2.2, -1.7))); float ribbon = sin(dot(warp, vec2(6.5, 2.8)) + flowA * 5.0 + dot(tilt, vec2(2.2, -1.7)));
float plume = sin(length(warp + vec2(flowB - 0.5, flowA - 0.5)) * 9.0 - tilt.x * 3.0 + time * 0.2); float plume = sin(length(warp + vec2(flowB - 0.5, flowA - 0.5)) * 9.0 - tilt.x * 3.0 + dot(uv, vec2(1.5, -1.2)));
float phase = flowA * 1.8 + ribbon * 0.28 + plume * 0.22 + time * 0.03; float phase = flowA * 1.8 + ribbon * 0.28 + plume * 0.22;
vec3 holo = rainbow(phase, 0.72, 1.0); vec3 holo = rainbow(phase, 0.72, 1.0);
float blend = smoothstep(-0.65, 0.95, ribbon + plume * 0.55); float blend = smoothstep(-0.65, 0.95, ribbon + plume * 0.55);
@@ -136,7 +136,7 @@ vec3 styleHolographicSilver(vec2 uv, vec2 tilt, float time) {
return mix(silver, holo, 0.65 * blend); return mix(silver, holo, 0.65 * blend);
} }
vec3 styleCrackedIce(vec2 uv, vec2 tilt, float time) { vec3 styleCrackedIce(vec2 uv, vec2 tilt) {
float maxZ = -999.0; float maxZ = -999.0;
vec2 bestSlope = vec2(0.0); vec2 bestSlope = vec2(0.0);
float bestHash = 0.0; float bestHash = 0.0;
@@ -187,11 +187,11 @@ vec3 styleCrackedIce(vec2 uv, vec2 tilt, float time) {
// --- VIBRANT COLOR OPTICS --- // --- VIBRANT COLOR OPTICS ---
float alignment = dot(tilt, normalize(bestSlope + vec2(0.001))); float alignment = dot(tilt, normalize(bestSlope + vec2(0.001)));
float phase = dot(uv, vec2(0.6, -0.4)) + time * 0.05 + bestHash * 0.6 + alignment * 0.5; float phase = dot(uv, vec2(0.6, -0.4)) + bestHash * 0.6 + alignment * 0.5;
float saturation = 0.7 + 0.3 * hash(vec2(bestHash, 6.1)); float saturation = 0.7 + 0.3 * hash(vec2(bestHash, 6.1));
vec3 baseColor = rainbow(phase, saturation, 1.0); vec3 baseColor = rainbow(phase, saturation, 1.0);
float lightCycle = sin(alignment * 4.0 + bestHash * TWO_PI + time * 0.15); float lightCycle = sin(alignment * 4.0 + bestHash * TWO_PI + dot(uv, vec2(2.1, -1.7)));
float ambient = 0.15 + 0.25 * max(0.0, lightCycle); float ambient = 0.15 + 0.25 * max(0.0, lightCycle);
vec3 finalColor = baseColor * ambient; vec3 finalColor = baseColor * ambient;
float flash = pow(max(0.0, lightCycle), 5.0); float flash = pow(max(0.0, lightCycle), 5.0);
@@ -201,7 +201,7 @@ vec3 styleCrackedIce(vec2 uv, vec2 tilt, float time) {
return finalColor; return finalColor;
} }
vec3 styleSilverMosaic(vec2 uv, vec2 tilt, float time) { vec3 styleSilverMosaic(vec2 uv, vec2 tilt) {
// Mosaic tile grid. // Mosaic tile grid.
vec2 g = uv * 6.0; vec2 g = uv * 6.0;
vec2 id = floor(g); vec2 id = floor(g);
@@ -228,7 +228,7 @@ vec3 styleSilverMosaic(vec2 uv, vec2 tilt, float time) {
// 2D cross product sign decides which side of the highlight receives hue shift. // 2D cross product sign decides which side of the highlight receives hue shift.
float side = sign(radialDir.x * lightDir.y - radialDir.y * lightDir.x); float side = sign(radialDir.x * lightDir.y - radialDir.y * lightDir.x);
float basePhase = length(tilt) * 2.5 + time * 0.1; float basePhase = length(tilt) * 2.5 + dot(uv, vec2(0.9, -0.7));
float phase = basePhase + side * angleDiff * 1.8; float phase = basePhase + side * angleDiff * 1.8;
vec3 holoColor = rainbow(phase, 0.85, 1.0); vec3 holoColor = rainbow(phase, 0.85, 1.0);
@@ -252,7 +252,7 @@ vec3 styleSilverMosaic(vec2 uv, vec2 tilt, float time) {
return clamp(finalColor * border, 0.0, 1.0); return clamp(finalColor * border, 0.0, 1.0);
} }
vec3 styleSuperGoldVinyl(vec2 uv, vec2 tilt, float time) { vec3 styleSuperGoldVinyl(vec2 uv, vec2 tilt) {
vec2 g = uv * 60.0; vec2 g = uv * 60.0;
vec2 staggered = vec2(g.x + 0.5 * mod(floor(g.y), 2.0), g.y); vec2 staggered = vec2(g.x + 0.5 * mod(floor(g.y), 2.0), g.y);
vec2 f = fract(staggered) - 0.5; vec2 f = fract(staggered) - 0.5;
@@ -262,7 +262,7 @@ vec3 styleSuperGoldVinyl(vec2 uv, vec2 tilt, float time) {
float dotMask = smoothstep(0.1764, 0.01, d2); float dotMask = smoothstep(0.1764, 0.01, d2);
float emboss = smoothstep(0.2025, 0.0324, d2); float emboss = smoothstep(0.2025, 0.0324, d2);
float sheen = 0.5 + 0.5 * sin(dot(uv, vec2(18.0, -6.0)) + noise(uv * 6.0) * 2.5 + dot(tilt, vec2(2.4, 1.8)) * 2.0 + time * 0.2); float sheen = 0.5 + 0.5 * sin(dot(uv, vec2(18.0, -6.0)) + noise(uv * 6.0) * 2.5 + dot(tilt, vec2(2.4, 1.8)) * 2.0);
vec3 gold = vec3(0.84, 0.70, 0.24); vec3 gold = vec3(0.84, 0.70, 0.24);
vec3 warm = vec3(1.0, 0.88, 0.42); vec3 warm = vec3(1.0, 0.88, 0.42);
vec3 shadow = vec3(0.18, 0.14, 0.05); vec3 shadow = vec3(0.18, 0.14, 0.05);
@@ -286,15 +286,18 @@ void main() {
// Highlight uses stretched UVs so glare spans the full widget area. // Highlight uses stretched UVs so glare spans the full widget area.
float highlightDistance = length(uv - (center + (uTilt * 0.35))); float highlightDistance = length(uv - (center + (uTilt * 0.35)));
float specularMask = pow(max(0.0, 1.0 - highlightDistance * 2.6), 3.2);
bool isHolographicSilver = uStyle < 0.5; bool isHolographicSilver = uStyle < 0.5;
bool isCrackedIce = uStyle >= 0.5 && uStyle < 1.5; bool isCrackedIce = uStyle >= 0.5 && uStyle < 1.5;
bool isSilverMosaic = uStyle >= 1.5 && uStyle < 2.5; bool isSilverMosaic = uStyle >= 1.5 && uStyle < 2.5;
float prismaticGain = isHolographicSilver ? 1.10 : (isCrackedIce ? 1.00 : (isSilverMosaic ? 1.30 : 1.55)); float specularSharpness = isHolographicSilver ? 3.0 : (isCrackedIce ? 4.0 : (isSilverMosaic ? 3.4 : 3.6));
float sparkleGain = isHolographicSilver ? 1.00 : (isCrackedIce ? 0.55 : (isSilverMosaic ? 0.80 : 0.70)); float specularMask = pow(max(0.0, 1.0 - highlightDistance * 2.6), specularSharpness);
float specularGain = isHolographicSilver ? 1.00 : (isCrackedIce ? 1.35 : (isSilverMosaic ? 1.15 : 1.50));
float prismaticGain = isHolographicSilver ? 1.10 : (isCrackedIce ? 1.00 : (isSilverMosaic ? 1.30 : 1.18));
float sparkleGain = isHolographicSilver ? 1.00 : (isCrackedIce ? 0.70 : (isSilverMosaic ? 0.80 : 0.70));
float specularGain = isHolographicSilver ? 1.00 : (isCrackedIce ? 1.65 : (isSilverMosaic ? 1.15 : 1.50));
float prismaticTintGain = isHolographicSilver ? 1.00 : (isCrackedIce ? 0.90 : (isSilverMosaic ? 1.15 : 0.65));
float sparkleGridScale = isCrackedIce ? 7.0 : (isSilverMosaic ? 14.0 : 18.0); float sparkleGridScale = isCrackedIce ? 7.0 : (isSilverMosaic ? 14.0 : 18.0);
@@ -313,7 +316,7 @@ void main() {
float alignment = max(0.0, dot(tiltDir, sparkleNormal)); float alignment = max(0.0, dot(tiltDir, sparkleNormal));
if (alignment > 0.0) { if (alignment > 0.0) {
float twinkle = 0.5 + 0.5 * sin(uTime * 5.5 + sparkleHash * TWO_PI); float twinkle = 0.45 + 0.55 * sin(dot(cell, vec2(21.0, -17.0)) + dot(uTilt, vec2(5.3, -4.1)) + sparkleHash * TWO_PI);
float shapeMask = 0.0; float shapeMask = 0.0;
if (isCrackedIce) { if (isCrackedIce) {
@@ -336,13 +339,13 @@ void main() {
vec3 styleBase; vec3 styleBase;
// Style bases sample aspect-corrected UVs for stable pattern geometry. // Style bases sample aspect-corrected UVs for stable pattern geometry.
if (uStyle < 0.5) { if (uStyle < 0.5) {
styleBase = styleHolographicSilver(aspectUV, uTilt, uTime); styleBase = styleHolographicSilver(aspectUV, uTilt);
} else if (uStyle < 1.5) { } else if (uStyle < 1.5) {
styleBase = styleCrackedIce(aspectUV, uTilt, uTime); styleBase = styleCrackedIce(aspectUV, uTilt);
} else if (uStyle < 2.5) { } else if (uStyle < 2.5) {
styleBase = styleSilverMosaic(aspectUV, uTilt, uTime); styleBase = styleSilverMosaic(aspectUV, uTilt);
} else { } else {
styleBase = styleSuperGoldVinyl(aspectUV, uTilt, uTime); styleBase = styleSuperGoldVinyl(aspectUV, uTilt);
} }
float styleLuma = dot(styleBase, vec3(0.299, 0.587, 0.114)); float styleLuma = dot(styleBase, vec3(0.299, 0.587, 0.114));
@@ -357,8 +360,8 @@ void main() {
float edgeSpec = pow(1.0 - directional, 2.2); float edgeSpec = pow(1.0 - directional, 2.2);
vec3 specular = vec3(specularMask) * (0.3 + 0.7 * edgeSpec) * uSpecular * (0.35 + 0.65 * tiltMag) * specularGain; vec3 specular = vec3(specularMask) * (0.3 + 0.7 * edgeSpec) * uSpecular * (0.35 + 0.65 * tiltMag) * specularGain;
float prismaticPhase = dot(aspectUV, vec2(3.6, -2.1)) + dot(uTilt, vec2(0.9, -0.7)) + uTime * 0.04; float prismaticPhase = dot(aspectUV, vec2(3.6, -2.1)) + dot(uTilt, vec2(0.9, -0.7));
vec3 prismaticTint = rainbow(prismaticPhase, 0.72, 1.0) * uPrismatic * prismaticGain * (0.08 + 0.14 * edgeSpec); vec3 prismaticTint = rainbow(prismaticPhase, 0.72, 1.0) * uPrismatic * prismaticGain * prismaticTintGain * (0.08 + 0.14 * edgeSpec);
float microStrength = isCrackedIce ? 0.08 : 0.18; float microStrength = isCrackedIce ? 0.08 : 0.18;