闽公网安备 35020302035485号


备注:不过加载着色器这个行为的开销可能会比较大,所以必须在运行时将它编译为适当的特定于平台的着色器。
5.不支持无符号整数和布尔值
void mainImage( out vec4 fragColor, in vec2 fragCoord ){
float strength = 0.4;
float t = iTime/3.0;
// 堆代码 duidaima.com
vec3 col = vec3(0);
vec2 fC = fragCoord;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
fC = fragCoord+vec2(i,j)/3.0;
vec2 pos = fC/iResolution.xy;
pos.y /= iResolution.x/iResolution.y;
pos = 4.0*(vec2(0.5) - pos);
for(float k = 1.0; k < 7.0; k+=1.0){
pos.x += strength * sin(2.0*t+k*1.5 * pos.y)+t*0.5;
pos.y += strength * cos(2.0*t+k*1.5 * pos.x);
}
col += 0.5 + 0.5*cos(iTime+pos.xyx+vec3(0,2,4));
}
}
col /= 9.0;
col = pow(col, vec3(0.4545));
fragColor = vec4(col,1.0);
}
而在 Flutter 里,就需要转化为如下代码所示:#version 460 core
#include <flutter/runtime_effect.glsl>
out vec4 fragColor;
uniform vec2 resolution;
uniform float iTime;
void main(){
float strength = 0.25;
float t = iTime/8.0;
vec3 col = vec3(0);
vec2 pos = FlutterFragCoord().xy/resolution.xy;
pos = 4.0*(vec2(0.5) - pos);
for(float k = 1.0; k < 7.0; k+=1.0){
pos.x += strength * sin(2.0*t+k*1.5 * pos.y)+t*0.5;
pos.y += strength * cos(2.0*t+k*1.5 * pos.x);
}
col += 0.5 + 0.5*cos(iTime+pos.xyx+vec3(0,2,4));
col = pow(col, vec3(0.4545));
fragColor = vec4(col,1.0);
}
备注:第一行 #version 460 core 指定所用的 OpenGL 语言版本。flutter:
shaders:
- shaders/warp.frag
·············
late Ticker _ticker;
Duration _elapsed = Duration.zero;
@override
void initState() {
super.initState();
_ticker = createTicker((elapsed) {
setState(() {
_elapsed = elapsed;
});
});
_ticker.start();
}
@override
Widget build(BuildContext context) => ShaderBuilder(
assetKey: 'shaders/warp.frag',
(BuildContext context, FragmentShader shader, _) => Scaffold(
appBar: AppBar(
title: const Text('Warp')
),
body: CustomPaint(
size: MediaQuery.of(context).size,
painter: ShaderCustomPainter(shader, _elapsed)
),
),
);
class ShaderCustomPainter extends CustomPainter {
final FragmentShader shader;
final Duration currentTime;
ShaderCustomPainter(this.shader, this.currentTime);
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
shader.setFloat(2, currentTime.inMilliseconds.toDouble() / 1000.0);
final Paint paint = Paint()..shader = shader;
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
这里唯一需要解释的就是 shader.setFloat 流程,因为它其实是通过索引来对应到我们在 .frag文件里的变量,简单来说:这里我们在 GLSL 里定义了 uniform vec2 resolution; 和 uniform float iTime; ,那么 vec2 resolution 就占据了索引 0 和 1 ,float iTime 就占据了索引 2 。

#include <flutter/runtime_effect.glsl>
uniform vec2 resolution;
uniform float pointer;
uniform float origin;
uniform vec4 container;
uniform float cornerRadius;
uniform sampler2D image;
const float r = 150.0;
const float scaleFactor = 0.2;
#define PI 3.14159265359
#define TRANSPARENT vec4(0.0, 0.0, 0.0, 0.0)
mat3 translate(vec2 p) {
return mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, p.x, p.y, 1.0);
}
mat3 scale(vec2 s, vec2 p) {
return translate(p) * mat3(s.x, 0.0, 0.0, 0.0, s.y, 0.0, 0.0, 0.0, 1.0) * translate(-p);
}
vec2 project(vec2 p, mat3 m) {
return (inverse(m) * vec3(p, 1.0)).xy;
}
struct Paint {
vec4 color;
bool stroke;
float strokeWidth;
int blendMode;
};
struct Context {
vec4 color;
vec2 p;
vec2 resolution;
};
bool inRect(vec2 p, vec4 rct) {
bool inRct = p.x > rct.x && p.x < rct.z && p.y > rct.y && p.y < rct.w;
if (!inRct) {
return false;
}
// Top left corner
if (p.x < rct.x + cornerRadius && p.y < rct.y + cornerRadius) {
return length(p - vec2(rct.x + cornerRadius, rct.y + cornerRadius)) < cornerRadius;
}
// Top right corner
if (p.x > rct.z - cornerRadius && p.y < rct.y + cornerRadius) {
return length(p - vec2(rct.z - cornerRadius, rct.y + cornerRadius)) < cornerRadius;
}
// Bottom left corner
if (p.x < rct.x + cornerRadius && p.y > rct.w - cornerRadius) {
return length(p - vec2(rct.x + cornerRadius, rct.w - cornerRadius)) < cornerRadius;
}
// Bottom right corner
if (p.x > rct.z - cornerRadius && p.y > rct.w - cornerRadius) {
return length(p - vec2(rct.z - cornerRadius, rct.w - cornerRadius)) < cornerRadius;
}
return true;
}
out vec4 fragColor;
void main() {
vec2 xy = FlutterFragCoord().xy;
vec2 center = resolution * 0.5;
float dx = origin - pointer;
float x = container.z - dx;
float d = xy.x - x;
if (d > r) {
fragColor = TRANSPARENT;
if (inRect(xy, container)) {
fragColor.a = mix(0.5, 0.0, (d-r)/r);
}
}
else
if (d > 0.0) {
float theta = asin(d / r);
float d1 = theta * r;
float d2 = (3.14159265 - theta) * r;
vec2 s = vec2(1.0 + (1.0 - sin(3.14159265/2.0 + theta)) * 0.1);
mat3 transform = scale(s, center);
vec2 uv = project(xy, transform);
vec2 p1 = vec2(x + d1, uv.y);
s = vec2(1.1 + sin(3.14159265/2.0 + theta) * 0.1);
transform = scale(s, center);
uv = project(xy, transform);
vec2 p2 = vec2(x + d2, uv.y);
if (inRect(p2, container)) {
fragColor = texture(image, p2 / resolution);
} else if (inRect(p1, container)) {
fragColor = texture(image, p1 / resolution);
fragColor.rgb *= pow(clamp((r - d) / r, 0.0, 1.0), 0.2);
} else if (inRect(xy, container)) {
fragColor = vec4(0.0, 0.0, 0.0, 0.5);
}
}
else {
vec2 s = vec2(1.2);
mat3 transform = scale(s, center);
vec2 uv = project(xy, transform);
vec2 p = vec2(x + abs(d) + 3.14159265 * r, uv.y);
if (inRect(p, container)) {
fragColor = texture(image, p / resolution);
} else {
fragColor = texture(image, xy / resolution);
}
}
}

Widget _buildAnimatedCard(BuildContext context, Widget? child) {
return ShaderBuilder(
(context, shader, _) {
return AnimatedSampler(
(image, size, canvas) {
_configureShader(shader, size, image);
_drawShaderRect(shader, size, canvas);
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: cornerRadius),
child: widget.child,
),
);
},
assetKey: 'shaders/page_curl.frag',
);
void _configureShader(FragmentShader shader, Size size, ui.Image image) {
shader
..setFloat(0, size.width) // resolution
..setFloat(1, size.height) // resolution
..setFloat(2, _animationController.value) // pointer
..setFloat(3, 0) // origin
..setFloat(4, 0) // inner container
..setFloat(5, 0) // inner container
..setFloat(6, size.width) // inner container
..setFloat(7, size.height) // inner container
..setFloat(8, cornerRadius) // cornerRadius
..setImageSampler(0, image); // image
}
void _drawShaderRect(FragmentShader shader, Size size, Canvas canvas) {
canvas.drawRect(
Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: size.width,
height: size.height,
),
Paint()..shader = shader,
);
}
完整项目可见:https://github.com/Rahiche/riveo_page_curl