Flutter着色器渲染插件shadertoy的使用
Flutter着色器渲染插件shadertoy的使用
shadertoy
库提供了与 Shadertoy 网站和 REST API 的交互支持,定义了数据模型和合同以查询和存储着色器、评论、用户、播放列表和网站媒体。
概览
shadertoy
库提供了与 Shadertoy 网站和 REST API 的交互支持,定义了数据模型和合同以查询和存储着色器、评论、用户、播放列表和网站媒体。
特性
- 📌 REST API - 支持所有 Shadertoy REST API(如 这里 所定义)
- 🌐 站点 API - 支持直接从 Shadertoy 网站抓取数据,允许检索用户查询评论、用户、播放列表和网站媒体。
- 🔗 混合 API - 尊重着色器的
public+api
隐私设置,同时提供额外的操作支持,例如检索着色器评论、用户、播放列表和网站媒体。 - ➿ 可扩展 - 可插入新的存储和客户端实现,重用
shadertoy
包中定义的 API 和实体。
客户端实现
以下是一些可用的客户端实现:
包名 | Pub | 描述 |
---|---|---|
shadertoy_client | 使用 dio 包的 Shadertoy REST 和站点 API 的 HTTP 客户端 |
存储实现
以下是一些可用的存储实现:
包名 | Pub | 描述 |
---|---|---|
shadertoy_sqlite | 使用 drift 包的 SQLite 存储实现 |
开始使用
选择一个客户端或存储实现(或两者)并将其添加到你的 pubspec.yaml
文件中,并替换为最新版本。以下示例使用 shadertoy_client
包来实现 Shadertoy REST 和站点 API:
dependencies:
shadertoy_client: ^x.x.x
运行以下命令安装依赖项:
dart pub get
最后,导入库:
import 'package:shadertoy_client/shadertoy_client.dart';
使用
实例化一个 ShadertoyClient
实现,例如由包 shadertoy_client
提供的实现,以访问客户端 API:
// 创建一个使用 API 密钥 `apiKey` 的 REST API 客户端
final client = newShadertoyWSClient(apiKey);
执行其中一个方法,例如通过提供着色器 ID 来获取着色器:
// 调用 REST API
final result = await client.findShaderById('Mt3XW8');
// 检查是否有错误
if (result.ok) {
// 打印着色器名称
print(fsr?.shader.name);
} else {
// 否则打印错误消息
print('Error: ${result.error.message}');
}
作为替代方案,实例化一个 ShadertoyExtendedClient
实现,例如由包 shadertoy_client
提供的实现,以访问站点 API:
// 创建一个使用 API 密钥 `apiKey` 的站点 API 客户端
final client = newShadertoySiteClient();
执行其中一个方法,例如通过提供着色器 ID 来获取着色器评论:
// 调用站点 API
final result = await client.findCommentsByShaderId('MdX3Rr');
// 检查是否有错误
if (result.ok) {
// 打印着色器评论
print(jsonEncode(result.comments));
} else {
// 否则打印错误消息
print('Error: ${fsr.error.message}');
}
如特性列表中所述,为了(可选地)尊重着色器的隐私约束但仍能受益于更大的一组操作(其中一些仅通过站点 API 可用),可以使用混合 API。首先实例化一个合适的混合 API 实现,例如由 shadertoy_client
包提供的实现:
// 使用 API 密钥 `apiKey` 创建一个混合客户端
// 这实际上会过滤着色器并只提供符合着色器 `public+api` 隐私设置的着色器
final client = newShadertoyHybridClient(apiKey: apiKey);
执行其中一个方法,例如通过提供着色器 ID 来获取着色器:
// 调用混合 API
final result = await client.findShaderById('3lsSzf');
// 检查是否有错误
if (result.ok) {
// 打印着色器 ID
print(result.shader?.info.id);
} else {
// 否则打印错误消息
print('Error: ${result.error.message}');
}
要创建一个数据库,提供与前面的 API 相同的一组读操作,但也可以保存着色器和其他实体,ShadertoyStore
合约也提供。要使用 ShadertoyStore
,应该提供一个合适的实现,例如,由 shadertoy_sqlite
提供的实现:
// 使用内存执行器创建一个新的存储
final store = newShadertoySqliteStore(memoryExecutor());
执行其中一个操作,例如保存一个用户:
// 创建一个用户
final user = User(id: 'UzZ0Z1', about: 'About user 1', memberSince: DateTime.now());
// 保存用户
final result = await store.saveUser(user);
// 检查是否有错误
if (result.ok) {
// 打印成功消息
print('Shader stored');
} else {
// 否则打印错误消息
print('Error: ${response.error.message}');
}
API
- 客户端 API - 针对 Shadertoy 在 howto 中定义的 REST 接口,允许用户浏览具有
public+api
隐私设置的可用着色器。注意,此 API 可用的操作数量有限,尽管足以用于简单的浏览。要开始使用,需要通过 Shadertoy 网站 上的用户部分的 apps 页面获取 API 密钥。 - 扩展客户端 API - 提供与前一个 API 相同的方法,但增加了仅通过站点 API 可用的功能,例如支持检索用户、播放列表、着色器评论和网站媒体。注意,此 API 返回的着色器不受
public+api
隐私设置的限制。 - 混合 API - 补充基本的客户端 API,使基本 REST API 客户端能够受益于扩展客户端 API 的附加功能,同时(可选地)遵守着色器创作者施加的
public+api
约束。简而言之,它允许 REST API 用户访问具有public+api
设置的着色器的用户、着色器评论和网站媒体。 - 存储 API - 定义支持创建数据存储的合约,从而提供一种离线工作的方法,而不是依赖于 Shadertoy APIs。它支持与前一个 API 相同的所有方法加上存储原语。
客户端 API
操作 | 描述 |
---|---|
findShaderById |
通过 ID 查找着色器 |
findShadersByIdSet |
通过一组 ID 查找着色器 |
findShaders |
通过术语、标签查询着色器,并按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 from 和 num 参数分页。 |
findAllShaderIds |
获取所有着色器 ID |
findShaderIds |
通过术语、标签查询着色器 ID,并按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 from 和 num 参数分页。 |
扩展客户端 API
在客户端 API 的基础上增加了以下功能:
操作 | 描述 |
---|---|
findUserById |
通过 ID 查找 Shadertoy 用户 |
findShadersByUserId |
通过用户 ID 查询着色器,标签并允许按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 from 和 num 参数分页。 |
findShaderIdsByUserId |
通过用户 ID 查询着色器 ID,标签并允许按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 from 和 num 参数分页。 |
findAllShaderIdsByUserId |
通过用户 ID 获取所有着色器 ID |
findCommentsByShaderId |
获取着色器 ID 的评论 |
findPlaylistById |
获取播放列表 ID |
findShadersByPlaylistId |
获取播放列表 ID 的着色器。所有查询结果都通过 from 和 num 参数分页。 |
findShaderIdsByPlaylistId |
获取播放列表 ID 的着色器 ID。所有查询结果都通过 from 和 num 参数分页。 |
混合客户端
具备客户端和扩展客户端的所有功能,但可选地限制请求到具有 public+api
隐私设置的着色器。
存储 API
具备基本和扩展客户端的所有功能,加上以下功能:
操作 | 描述 |
---|---|
findAllUserIds |
返回所有存储的用户 ID |
findAllUsers |
返回所有存储的用户 |
saveUser |
保存一个用户 |
saveUsers |
保存多个用户 |
deleteUserById |
通过 ID 删除一个用户 |
findAllShaders |
获取所有着色器 |
saveShader |
保存一个着色器 |
saveShaders |
保存多个着色器 |
deleteShaderById |
通过 ID 删除一个着色器 |
findCommentById |
通过 ID 获取评论 |
findAllCommentIds |
返回所有评论 ID |
findAllComments |
返回所有评论 |
saveShaderComments |
保存多个着色器评论 |
deleteCommentById |
通过 ID 删除一个评论 |
savePlaylist |
保存一个播放列表 |
savePlaylistShaders |
将一组着色器 ID 关联到播放列表 |
deletePlaylistById |
通过 ID 删除一个播放列表 |
findAllPlaylistIds |
返回所有播放列表 ID |
findAllPlaylists |
返回所有播放列表 |
模型
贡献
这是一个非官方的 Shadertoy 客户端 API。它是由最佳努力开发的,在“为自己挠痒痒”的口号下,意味着对作者用例有意义的 API。
如果你希望贡献其他部分的 API,请随时在 Github 上提交拉取请求,因为我在寻找贡献测试、文档和新 API。
请参阅 CONTRIBUTING.md 了解如何开始。
功能和问题
请在 issue tracker 上提交功能请求和错误报告。
许可证
该项目在 MIT 许可证下发布,请参阅 LICENSE 文件了解详细信息。
示例代码
import 'dart:convert';
import 'package:shadertoy/shadertoy_api.dart';
void main() {
final encoder = JsonEncoder.withIndent(' ');
// 创建一个用户
final user = User(
id: 'userid',
about: 'About this user',
memberSince: DateTime.now(),
);
print(encoder.convert(user));
// 创建一个包含两个渲染通道的着色器
final shader = Shader(
version: '0.1',
info: Info(
id: 'ZzZ0Zz',
date: DateTime.fromMillisecondsSinceEpoch(1360495251),
views: 131083,
name: 'Example',
userId: 'example',
description: 'A shader example',
likes: 570,
privacy: ShaderPrivacy.publicApi,
flags: 32,
tags: [
'procedural',
'3d',
'raymarching',
'distancefield',
'terrain',
'motionblur',
'vr'
],
hasLiked: false,
),
renderPasses: [
RenderPass(
name: 'Image',
type: RenderPassType.image,
description: '',
code: 'code 0',
inputs: [
Input(
id: '257',
src: '/media/previz/buffer00.png',
type: InputType.texture,
channel: 0,
sampler: Sampler(
filter: FilterType.linear,
wrap: WrapType.clamp,
vflip: true,
srgb: true,
internal: 'byte',
),
published: 1,
)
],
outputs: [
Output(id: '37', channel: 0)
],
),
RenderPass(
name: 'Buffer A',
type: RenderPassType.buffer,
description: '',
code: 'code 1',
inputs: [
Input(
id: '17',
src: '/media/a/zs098rere0323u85534ukj4.png',
type: InputType.texture,
channel: 0,
sampler: Sampler(
filter: FilterType.mipmap,
wrap: WrapType.repeat,
vflip: false,
srgb: false,
internal: 'byte',
),
published: 1,
)
],
outputs: [
Output(id: '257', channel: 0)
]
)
],
);
print(encoder.convert(shader));
// 创建一个着色器评论
final comment = Comment(
id: 'AaA0Aa',
shaderId: 'ZzZ0Zz',
userId: 'userId',
picture: '/img/profile.jpg',
date: DateTime.now(),
text: 'Great shader!',
);
print(encoder.convert(comment));
// 创建一个播放列表
final playlist = Playlist(
id: 'week',
userId: 'shadertoy',
name: 'Shaders of the Week',
description: 'Playlist with every single shader of the week ever.',
);
print(encoder.convert(playlist));
}
更多关于Flutter着色器渲染插件shadertoy的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter着色器渲染插件shadertoy的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中使用Shadertoy风格的着色器渲染,你可以通过自定义着色器(Shader)和Flutter的CustomPaint
或ShaderMask
等组件来实现。虽然Flutter没有直接集成Shadertoy的功能,但你可以利用GLSL(OpenGL Shading Language)来编写着色器,并将其嵌入到Flutter应用中。
以下是一个简单的步骤指南,展示如何在Flutter中使用自定义着色器:
1. 编写GLSL着色器
首先,你需要编写一个GLSL着色器。这个着色器可以类似于你在Shadertoy上看到的那些。
例如,以下是一个简单的GLSL片段着色器,它会在屏幕上绘制一个渐变颜色:
// shader.frag
precision mediump float;
uniform vec2 uResolution;
uniform float uTime;
void main() {
vec2 uv = gl_FragCoord.xy / uResolution;
vec3 color = vec3(uv.x, uv.y, sin(uTime));
gl_FragColor = vec4(color, 1.0);
}
2. 将GLSL着色器嵌入Flutter
在Flutter中,你可以使用FragmentProgram
来加载和编译GLSL着色器。首先,将你的GLSL着色器文件放在assets
目录下。
在pubspec.yaml
中添加资源:
flutter:
assets:
- shaders/shader.frag
然后,在Dart代码中加载和编译着色器:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ShaderWidget extends StatefulWidget {
@override
_ShaderWidgetState createState() => _ShaderWidgetState();
}
class _ShaderWidgetState extends State<ShaderWidget> with SingleTickerProviderStateMixin {
FragmentProgram? _program;
AnimationController? _controller;
@override
void initState() {
super.initState();
_loadShader();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 10),
)..repeat();
}
Future<void> _loadShader() async {
_program = await FragmentProgram.fromAsset('shaders/shader.frag');
setState(() {});
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_program == null) {
return Center(child: CircularProgressIndicator());
}
return AnimatedBuilder(
animation: _controller!,
builder: (context, child) {
return CustomPaint(
size: Size.infinite,
painter: ShaderPainter(_program!, _controller!.value),
);
},
);
}
}
class ShaderPainter extends CustomPainter {
final FragmentProgram program;
final float time;
ShaderPainter(this.program, this.time);
@override
void paint(Canvas canvas, Size size) {
final shader = program.shader(
floatUniforms: Float32List.fromList([time]),
samplerUniforms: [],
);
final paint = Paint()..shader = shader;
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
3. 使用ShaderWidget
最后,在你的应用中嵌入ShaderWidget
:
void main() {
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: ShaderWidget(),
),
),
));
}