shader
Shader manages the compilation of your GLSL shaders into SPIR-V byte code and Dart code.
Quickstart
# Install cli
dart pub global activate shader
# Compile all glsl files in our project
shader --use-remote --to-dart
# Discover all features
shader --help
Table of Contents
- Compile to Dart
- Use of uniforms
- Make use of sampler uniform
- Use a local compiler
- Improve development cycle
- Other features
Getting started
Install the command-line executable shader
for your current user:
dart pub global activate shader
Now you can use the shader
CLI tool:
shader --help
Hint: If pub binaries are not known to the path, you can also run it by:
dart pub global run shader --help
Usage
Compile to Dart
The easiest way of using shaders in your app, is to use it this way:
shader --use-remote --to-dart
It will scan your project for *.glsl
files and use the hosted cloud service to compile it. Flutter needs SPR-V byte code at runtime.
A very simple shader red-shader.glsl
could be this:
#version 320 es
precision highp float;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
This shader has no uniforms (input parameters) and paints each pixel red.
The compiler created a dart file red_shader_sprv.dart
that contains a function Future<FragmentProgram> redShaderFragmentProgram()
that will initialize the shader at runtime.
We can utilize a FutureBuilder
to load that:
import 'dart:ui';
import 'package:flutter/material.dart';
/// Import file generated by cli
import 'package:flutter_app/shader/red_shader_sprv.dart';
class RedShaderWidget extends StatelessWidget {
const RedShaderWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder<FragmentProgram>(
/// Use the generated loader function here
future: redShaderFragmentProgram(),
builder: ((context, snapshot) {
if (!snapshot.hasData) {
/// Shader is loading
return const WidgetWhenLoading();
}
/// Shader is ready to use
return WidgetThatUsesShader(snapshot.data!);
}),
);
}
}
You can find an app example of Red Shader.
Use of uniforms
Uniforms is the term in the GLSL world for input parameter. The uniforms can be changed for each frame, but they are constant for every pixel during a single frame.
Given the following GLSL file:
#version 320 es
precision highp float;
layout(location = 0) out vec4 fragColor;
// define uniforms:
layout(location = 0) uniform vec3 color1;
layout(location = 1) uniform vec3 color2;
layout(location = 2) uniform float someValue;
layout(location = 3) uniform vec2 size;
void main() {
// ...
}
This can be addressed in FragmentProgram
's shader()
method:
@override
void paint(Canvas canvas, Size size) {
/// Inputs
Color color1 = Colors.blue;
Color color2 = Colors.green;
double someValue = 0.5;
/// Create paint using a shader
final paint = Paint()
..shader = fragmentProgram.shader(
/// Specify input parameter (uniforms)
floatUniforms: Float32List.fromList([
/// color1 takes 3 floats and will be mapped to `vec3`
color1.red / 255.0,
color1.green / 255.0,
color1.blue / 255.0,
/// color2 also takes 3 floats and will be mapped to `vec3`
color2.red / 255.0,
color2.green / 255.0,
color2.blue / 255.0,
/// someValue takes 1 float and will be mapped to `float`
someValue,
/// size takes 2 floats and will be mapped to `vec2`
size.width,
size.height,
]));
/// Draw a rectangle with the shader-paint
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
}
Also take a look at Color Shader example, that combines Flutter animtions and the use of uniforms.
Make use of sampler uniform
Image textures can be accessed via sampler2d
uniforms:
layout(location = 0) uniform sampler2D image;
For that you need to create an ImageShader
:
final asset = await rootBundle.load("assets/image.jpg");
final image = await decodeImageFromList(asset.buffer.asUint8List());
/// Create ImageShader that will provide a GLSL sampler
final ImageShader imageShader = ImageShader(
image,
// Specify how image repetition is handled for x and y dimension
TileMode.repeated,
TileMode.repeated,
// Transformation matrix (identity matrix = no transformation)
Matrix4.identity().storage,
);
That ImageShader
can be passed into the FragmentProgram
's shader()
method as samplerUniform
:
final paint = Paint()
..shader = fragmentProgram.shader(
samplerUniforms: [
imageShader,
],
);
You can see everything wired up in the Image Scale app example.
Use a local compiler
If you don't want to rely on the hosted cloud service or the cloud service not available, you can use local compiler.
You can download the compiler at https://github.com/google/shaderc.
The --use-local
option takes a path that roughly points to the compiler binary:
shader --use-local $HOME/sdk/shaderc --to-dart
Improve development cycle
In order to iterate faster and use the hot-reload, you can use the --watch
flag:
shader --use-remote --to-dart --watch
shader --use-local $HOME/sdk/shaderc --to-dart --watch
Other features
The shader
executable also supports some other options and output format. Type to find get information:
shader --help
Writing shaders
This section covers useful information and resources writing own shader code.
Constraints in Flutter
Shaders are not supported for Flutter web, yet. But there is a project plan for the Flutter engine developers to enable it.
Also the capabilities of GLSL language feature are restricted. Take a look at the specifications of the SPIR-V Transpiler.
This package compiles GLSL code to SPIR-V code, and at runtime SPIR-V transpiler converts it to native API (e.g. OpenGL, Vulkan). So it might be that shader
will compile fine, but it fails at runtime.
Learning GLSL
There are various sources to learn GLSL: