Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created May 22, 2022 12:52
Show Gist options
  • Save roipeker/0be0eb097e77a89377d7b812b012b506 to your computer and use it in GitHub Desktop.
Save roipeker/0be0eb097e77a89377d7b812b012b506 to your computer and use it in GitHub Desktop.
Flubber Shader
/// roipeker 2022.
///
/// Original Tweet:
/// https://twitter.com/roipekr/status/1527026419649454081
///
///
/// This code uses "shader" package to simplify testing.
/// Add it with: `flutter pub add shader`
///
/// If you want to compile locally, get "shaderc" for your platform [https://github.com/google/shaderc/blob/main/downloads.md]
/// Compile with:
/// `glslc --target-env=opengl -fshader-stage=fragment -o assets/shader_binary.sprv src/shader_source.glsl
///
///
/// Original blue blob shader configuration:
///
/// light1 = 0.16, 0.46, .54,
/// light2 = 0.2, 1.0, 1.0,
///
/// Flubber style:
/// light1 = 0.16, 0.49, .24,
///
///
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:shader/shader.dart';
var time = .0;
Future<void> main() async {
runApp(const ShaderSample());
}
class ShaderSample extends StatelessWidget {
const ShaderSample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => GlslFragmentProgramWebserviceBuilder(
code: kBlobShaderCode,
builder: (context, program) {
return (program == null)
? const CupertinoActivityIndicator()
: RebuildEachFrame(
builder: (context) => CustomPaint(
foregroundPainter: BlobPainter(program),
),
);
},
);
}
class BlobPainter extends CustomPainter {
final FragmentProgram program;
BlobPainter(this.program);
@override
void paint(Canvas canvas, Size size) {
time += .1;
final shader = program.shader(
floatUniforms: Float32List.fromList([
time,
size.width,
size.height,
/// -- light1, base color --
0.203, 0.390, 0.349,
/// -- light 2 --
.19, .9, .03,
]),
);
canvas.drawRect(
Offset.zero & size,
Paint()..shader = shader,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
/// Shader code modified from some shadertoy I can't find :(
String get kBlobShaderCode => '''
#version 320 es
precision mediump float;
layout(location=0) out vec4 fragColor;
layout(location=0) uniform float time;
layout(location=1) uniform vec2 resolution;
layout(location=2) uniform vec3 light1;
layout(location=3) uniform vec3 light2;
/// bigger iterations = blob sharpness, can't be passed as uniform, Skia requires
/// a const for loops.
#define ITERATIONS 13.0
mat2 m(float a) {
float c=cos(a), s=sin(a);
return mat2(c,-s,s,c);
}
float map(vec3 p){
p.xz *= m( time * 0.4 );
p.xy *= m( time * 0.3 );
vec3 q = p * 2. + time;
return length( p + vec3( sin(time*0.7)))*log(length(p)+1.) + sin(q.x+sin(q.z+sin(q.y)))*0.5 - 1.;
}
void main() {
vec2 p = gl_FragCoord.xy / resolution.y - vec2(.5,.5);
vec3 cl = vec3(0.);
float d = .1;
for(float i=0.0; i<=ITERATIONS; i++) {
/// size variance for the blob
vec3 p = vec3( -0.5, 0, 5.) + normalize( vec3(p, -1.) ) * d;
float rz = map(p);
float f = clamp((rz - map( p + .1)) * .5, -.1, .9 );
vec3 l = light1 + light2 * f;
/// smoothstep will change the outter "glow", adjust with ITERATIONS.
cl = cl * l + smoothstep( 5.2, .15, rz ) * .9 * l;
d += min( rz, 1. );
}
fragColor = vec4(cl, 1.);
}
''';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment