v1.1.4shader_buffers
使用著色器來驅動著色器。專注於ShaderToy.com網站,讓著色器更易使用。
一個旨在簡化使用分層著色器的 Flutter 小部件,重點關注 ShaderToy.com 網站
{"sdk":"flutter"}^3.0.1{"sdk":"flutter"}^5.1.0以下為英文專案原文快照,最新內容請造訪 GitHub。
shader_buffers aims to simplify the use of shaders with a focus on the ShaderToy.com
sampler2D uniforms.Please, take a look at the shader_presets package which implements some ready-to-use shaders, like transitions and effects (from gl-transitions and ShaderToy).
Tested on Android, Linux and web, it should work on other desktops. Seems that impeller engine is not yet ready, on iOS it should work with impeller disabled. Shader examples are from ShaderToy.com and have been slightly modified. Credits links are in the main shaders sources.
[!NOTE]
Using a shader output to feed itself, produces a memory leak: memory leak issue. Please thumb it up! A temporary fix is used.
img
The main widget to use is ShaderBuffers, which takes its size and mainImage as input. Optionally, you can add the buffers.
mainImage and buffers are of type LayerBuffer, which defines the fragment shader asset source and the texture channels.
mainImage shader must be provided. The more buffers, the more performances will be affected.
Think of mainImage as the Image layer fragment in ShaderToy.com and buffers as Buffer[A-D].
Each frame buffers are computed from the 1st to the last, then mainImage.
This widget provides the following uniforms to the fragment shader:
sampler2D iChannel[0-N] as many as defined in LayerBuffer.channelsvec2 iResolution the widget width and heightfloat iTime the current time in seconds from the start of renderingfloat iFrame the current rendering frame numbervec4 iMouse for user interaction with pointer (see IMouse)To start, you can define the layers:
/// The main layer uses `shader_main.frag` as a fragment shader source and some float uniforms
final mainImage = LayerBuffer(
shaderAssetsName: 'shaders/shader_main.glsl',
uniforms: Uniforms([
Uniform(
name: 'blur',
range: const RangeValues(0, 1),
defaultValue: 0.9,
value: 0.9,
),
Uniform(
name: 'velocity',
range: const RangeValues(0, 1),
defaultValue: 0.2,
value: 0.2,
),
]),
);
Now you can use ShaderBuffers:
ShaderController controller = ShaderController();
ShaderBuffers(
controller: controller,
mainImage: mainImage,
)
mainImage and buffers are of type IChannel. The latter represents the uniform sampler2D texture to be passed to the fragment shader.
ShaderBuffers listen to the pointer with onPointerDown, onPointerMove, onPointerUp which give back the controller and the position in pixels. Most of the time is more useful to have back the normalized position (in the 0~1 range) instead of pixels. This can be achieved with onPointerDownNormalized, onPointerMoveNormalized, onPointerUpNormalized callbacks. With the controller, the one passed to ShaderBuffers, or the one returned by the onPointer* callbacks, is possible to do these:
It's possible to check for some conditions.
controller
..addConditionalOperation(
(
layerBuffer: mainImage,
param: Param.iMouseXNormalized,
checkType: CheckOperator.minor,
checkValue: 0.5,
operation: (result) {
/// [result] == true means (iMouseXNormalized < 0.5 )
},
),
)
final mainLayer = LayerBuffer(
shaderAssetsName: 'shaders/shader_main.frag',
);
final bufferA = LayerBuffer(
shaderAssetsName: 'shaders/shader_bufferA.frag',
);
final bufferB = LayerBuffer(
shaderAssetsName: 'shaders/shader_bufferB.frag',
);
final bufferC = LayerBuffer(
shaderAssetsName: 'shaders/shader_bufferC.frag',
);
mainLayer.setChannels([IChannel(buffer: bufferC)]);
bufferB.setChannels([IChannel(buffer: bufferA)]);
bufferC.setChannels([
IChannel(buffer: bufferA),
IChannel(buffer: bufferB),
IChannel(assetsTexturePath: 'assets/bricks.jpg'),
]);
The main drawback when willing to port ShaderToy shader buffers is that they use 4 floats per RGBA channel, while with Flutter shader we are stuck using 4 int8 RGBA.
Also, the coordinate system is slightly different: the origin in ShaderToy is bottom-left while in Flutter, is top-left. This latter issue can be easily bypassed where in the main image layer you see this:
vec2 uv = fragCoord.xy / iResolution.xy;
after this line, you can add this to swap Y coordinates:
uv = vec2(uv.x, 1. - uv.y);
It's mandatory to provide the shader the following uniforms since shader_buffer always sends them:
uniform vec2 iResolution;
uniform float iTime;
uniform float iFrame;
uniform vec4 iMouse;
For simplicity, there is assets/shader/common/common_header.frag to include at the very beginning of the shader:
#include <common/common_header.frag>
it provides also:
out vec4 fragColor;
If you are experimenting with ShaderToy shaders, start your code copied from it and at the bottom of the file include main_shadertoy.frag:
#include <common/main_shadertoy.frag>