Created
May 22, 2022 12:52
-
-
Save roipeker/0be0eb097e77a89377d7b812b012b506 to your computer and use it in GitHub Desktop.
Flubber Shader
This file contains 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
/// 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