Created
June 5, 2023 10:39
-
-
Save abichinger/191c0c413d34d4c926455219f4ce0665 to your computer and use it in GitHub Desktop.
Flutter pixel-by-pixel fade-in shader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:ui' as ui; | |
import 'package:flutter/material.dart'; | |
class PixelFade extends StatefulWidget { | |
final String shaderAsset; | |
final Widget? child; | |
final Duration duration; | |
final int width; | |
final int height; | |
const PixelFade({ | |
super.key, | |
required this.child, | |
this.duration = const Duration(milliseconds: 1000), | |
this.shaderAsset = 'shaders/pixel_fade.frag', | |
required this.width, | |
required this.height, | |
}); | |
@override | |
State<PixelFade> createState() => _PixelFadeState(); | |
} | |
class _PixelFadeState extends State<PixelFade> with TickerProviderStateMixin { | |
late final AnimationController controller; | |
late final Animation<double> animation; | |
ui.FragmentShader? shader; | |
@override | |
void initState() { | |
controller = AnimationController( | |
duration: widget.duration, | |
vsync: this, | |
); | |
animation = Tween<double>( | |
begin: 0, | |
end: 2, | |
).animate(controller); | |
super.initState(); | |
Future.delayed(Duration.zero, () async { | |
var program = await ui.FragmentProgram.fromAsset(widget.shaderAsset); | |
shader = program.fragmentShader(); | |
controller.forward(); | |
}); | |
} | |
@override | |
void dispose() { | |
controller.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
animation: animation, | |
builder: (context, child) { | |
return ShaderMask( | |
shaderCallback: (bounds) { | |
var shader = this.shader; | |
if (shader == null) { | |
return const LinearGradient( | |
colors: [Colors.transparent, Colors.transparent], | |
).createShader(bounds); | |
} | |
shader.setFloat(0, animation.value); | |
shader.setFloat(1, bounds.width); | |
shader.setFloat(2, bounds.height); | |
shader.setFloat(3, widget.width.toDouble()); | |
shader.setFloat(4, widget.height.toDouble()); | |
return shader; | |
}, | |
child: widget.child, | |
); | |
}, | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 460 core | |
uniform float u_time; | |
uniform vec2 u_resolution; | |
uniform vec2 u_size; | |
out vec4 fragColor; | |
float manhattan(vec2 pos){ | |
// Calculate the normalized coordinates | |
vec2 uv=pos/u_resolution; | |
vec2 pixel=uv*u_size; | |
return floor(pixel.x)+floor(pixel.y); | |
} | |
void main(){ | |
// Calculate manhatten distance | |
float distance=manhattan(gl_FragCoord.xy); | |
// Calculate the normalized distance | |
float ud=distance/(u_size.x+u_size.y); | |
// Calculate the fade value based on time and distance | |
float fade=smoothstep(0.,1.,u_time-ud); | |
// Set the output color with the fade value | |
fragColor=vec4(fade,fade,fade,fade); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment