Flutter着色器渲染插件shadertoy的使用

发布于 1周前 作者 ionicwang 来自 Flutter

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 Pub 使用 dio 包的 Shadertoy REST 和站点 API 的 HTTP 客户端

存储实现

以下是一些可用的存储实现:

包名 Pub 描述
shadertoy_sqlite Pub 使用 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 通过术语、标签查询着色器,并按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 fromnum 参数分页。
findAllShaderIds 获取所有着色器 ID
findShaderIds 通过术语、标签查询着色器 ID,并按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 fromnum 参数分页。

扩展客户端 API

在客户端 API 的基础上增加了以下功能:

操作 描述
findUserById 通过 ID 查找 Shadertoy 用户
findShadersByUserId 通过用户 ID 查询着色器,标签并允许按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 fromnum 参数分页。
findShaderIdsByUserId 通过用户 ID 查询着色器 ID,标签并允许按名称、喜欢次数、查看次数、新度和热度排序(与流行程度成正比,与寿命成反比)。所有查询结果都通过 fromnum 参数分页。
findAllShaderIdsByUserId 通过用户 ID 获取所有着色器 ID
findCommentsByShaderId 获取着色器 ID 的评论
findPlaylistById 获取播放列表 ID
findShadersByPlaylistId 获取播放列表 ID 的着色器。所有查询结果都通过 fromnum 参数分页。
findShaderIdsByPlaylistId 获取播放列表 ID 的着色器 ID。所有查询结果都通过 fromnum 参数分页。

混合客户端

具备客户端和扩展客户端的所有功能,但可选地限制请求到具有 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 Model

贡献

这是一个非官方的 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

1 回复

更多关于Flutter着色器渲染插件shadertoy的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中使用Shadertoy风格的着色器渲染,你可以通过自定义着色器(Shader)和Flutter的CustomPaintShaderMask等组件来实现。虽然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(),
      ),
    ),
  ));
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!