Flutter WebGL支持插件flutter_web_gl的使用
Flutter WebGL支持插件flutter_web_gl的使用
FlutterWebGL 
目前在Flutter中高效渲染3D对象的方法尚未实现。同时,直接从Dart访问和编程GPU也未被支持。该插件旨在解决这一问题。
最初,@kentcb和我为Aaronia公司的一个项目共同开发了一个用于OpenGL的封装。尽管我们在那个项目上取得了不错的进展,但考虑到Apple已废弃了OpenGL,我在想这是否仍是正确的方向。
幸运的是,Simon让我意识到Google有一个名为Angle的项目,该项目为不支持它们的所有平台实现了OpenGL ES API。
此项目将解决两个目标:
- 提供一个低级Dart FFI层,通过OpenGL ES 3.0 API将渲染内容输出到Flutter Texture小部件。
- 实现一个Dart版本的WebGL接口,覆盖了OpenGL ES的大部分功能,但无需处理FFI本地类型和适当的错误处理。
此外,其他包可以构建更易于使用的3D API,例如,有一个Dart版的JavaScript 3D框架three.js。
如何工作
Flutter有一个Texture
小部件,它是图形内容的占位符,这些内容会被渲染到原生的Texture对象上。这通过在插件的原生部分创建一个Texture对象,并将其注册到Flutter引擎来实现。引擎会返回一个ID,然后可以在Flutter中的Texture小部件中使用它。
Flutter将显示该纹理的当前内容。如果该纹理的内容由原生代码更改,我们需要通知Flutter引擎,以便它可以更新相关的Texture小部件。
此插件使用OpenGL ES将内容渲染到这些原生纹理上。由于OpenGL的工作方式,我们可以通过Dart-FFI绑定到OpenGL ES API来访问OpenGL的“渲染缓冲区对象”(RBO),这样我们就可以用Dart编写所有渲染代码(除了着色器)。
要使用此功能,你需要执行以下操作:
- 调用
FlutterWebGL.initOpenGL()
,这将在插件的Dart和原生部分设置所需的基础设施。 - 使用
FlutterWebGL.createTexture
创建一个纹理,这将返回纹理ID以在Texture小部件中使用。 - 激活纹理以使用OpenGL命令访问它(此部分尚未完成,目前只支持一个纹理)。
- 使用OpenGL ES底层或WebGL命令渲染到该纹理。
- 通过调用
FlutterWebGL.updateTexture
通知Flutter引擎该纹理上有新内容可用。
查看示例以了解其工作原理。
目前,渲染处理故意与任何特定的小部件分离,而是保持独立。这使得即使从单独的Isolate更新纹理内容也成为可能,从而开启了更多可能性。
Roadmap
幸运的是,Aaronia支持此项目,因此我可以利用部分时间来做这个项目。
由于还有很多事情要做,并且需要了解不同平台的工具链,如果您能加入这个项目将会非常棒。
目前,我认为需要解决的任务如下:
- 构建Angle框架
- Windows ✅
- Linux ❌(直到Flutter引擎在Linux上支持原生纹理为止)
- MacOS ❌
- iOS ❌(似乎主Angle项目没有跟上其分支metalangle,所以我们可能应该使用这个)
- 创建一个Dart-FFI层,用于OpenGL ES 3.0 API和EGL 1.5
- 实现原生插件部分,注册并更新纹理
- Windows ✅(处理多个纹理的缺失)
- Android ❌
- iOS ❌
- macOS ❌
- Linux ❌(见上文)
- Web ❌(不确定如何实现,但或许有人知道)
- 在Dart中实现WebGL
- 添加一种渲染文本的方式。我的当前想法是使用Flutter将文本渲染到画布上,然后将其传递给OpenGL,以便可以将其作为多边形上的纹理使用。
- 设计小部件,使您可以使用WebGL而无需关心纹理分配和通知。
- 编写示例和文档
示例代码
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_web_gl/flutter_web_gl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
int textureId = 0;
[@override](/user/override)
void initState() {
super.initState();
initPlatformState();
}
// 平台消息是异步的,所以我们初始化在一个异步方法中。
Future<void> initPlatformState() async {
String platformVersion;
// 平台消息可能会失败,所以我们使用try/catch PlatformException。
try {
platformVersion = await FlutterWebGL.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
FlutterWebGL.initOpenGL();
try {
textureId = await FlutterWebGL.createTexture(600, 400);
} on PlatformException {
print("failed to get texture id");
}
// 如果在异步平台消息飞行时小部件从树中移除,我们想要丢弃回复而不是调用setState来更新我们的非存在的外观。
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
title: const Text('插件示例应用'),
),
body: Center(
child: Column(
children: [
Text('运行在: $_platformVersion\n'),
SizedBox(width: 600, height: 400, child: Texture(textureId: textureId)),
MaterialButton(
onPressed: draw,
color: Colors.grey,
child: Text('绘制'),
),
],
),
),
),
);
}
void draw() async {
final gl = FlutterWebGL.rawOpenGl;
int vertexShader = gl.glCreateShader(GL_VERTEX_SHADER);
var sourceString = Utf8.toUtf8(vertexShaderSource);
var arrayPointer = allocate<Pointer<Int8>>();
arrayPointer.value = Pointer.fromAddress(sourceString.address);
gl.glShaderSource(vertexShader, 1, arrayPointer, nullptr);
gl.glCompileShader(vertexShader);
free(arrayPointer);
free(sourceString);
int fragmentShader = gl.glCreateShader(GL_FRAGMENT_SHADER);
sourceString = Utf8.toUtf8(fragmentShaderSource);
arrayPointer = allocate<Pointer<Int8>>();
arrayPointer.value = Pointer.fromAddress(sourceString.address);
gl.glShaderSource(fragmentShader, 1, arrayPointer, nullptr);
gl.glCompileShader(fragmentShader);
free(arrayPointer);
free(sourceString);
final shaderProgram = gl.glCreateProgram();
gl.glAttachShader(shaderProgram, vertexShader);
gl.glAttachShader(shaderProgram, fragmentShader);
gl.glLinkProgram(shaderProgram);
gl.glClearColor(0, 0, 1, 1);
gl.glClear(GL_COLOR_BUFFER_BIT);
gl.glUseProgram(shaderProgram);
final points = [-0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0];
Pointer<Uint32> vbo = allocate();
gl.glGenBuffers(1, vbo);
gl.glBindBuffer(GL_ARRAY_BUFFER, vbo.value);
gl.glBufferData(GL_ARRAY_BUFFER, 36, floatListToArrayPointer(points).cast(), GL_STATIC_DRAW);
gl.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, Pointer<Void>.fromAddress(0).cast());
gl.glEnableVertexAttribArray(0);
gl.glDrawArrays(GL_TRIANGLES, 0, 3);
gl.glDeleteShader(vertexShader);
gl.glDeleteShader(fragmentShader);
await FlutterWebGL.updateTexture(textureId);
}
}
const vertexShaderSource =
'#version 300 es\n'
'layout (location = 0) in vec4 aPos;\n'
'\n'
'void main()\n'
'{\n'
' gl_Position = aPos;\n'
'}\n';
const fragmentShaderSource =
'#version 300 es\n'
'precision mediump float;\n'
'out vec4 FragColor;\n'
'\n'
'void main()\n'
'{\n'
' FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n'
'} \n';
Pointer<Float> floatListToArrayPointer(List<double> list) {
final ptr = allocate<Float>(count: list.length);
for (var i = 0; i < list.length; i++) {
ptr.elementAt(i).value = list[i];
}
return ptr;
}
更多关于Flutter WebGL支持插件flutter_web_gl的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter WebGL支持插件flutter_web_gl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flutter_web_gl
是一个用于在 Flutter Web 应用中集成 WebGL 的插件。它允许你在 Flutter Web 项目中使用 WebGL 进行 3D 图形渲染。以下是如何使用 flutter_web_gl
插件的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 flutter_web_gl
插件的依赖:
dependencies:
flutter:
sdk: flutter
flutter_web_gl: ^0.1.0 # 请检查最新版本
然后运行 flutter pub get
来获取依赖。
2. 导入插件
在你的 Dart 文件中导入 flutter_web_gl
插件:
import 'package:flutter_web_gl/flutter_web_gl.dart';
3. 使用 WebGL 视图
你可以使用 WebGLView
来嵌入 WebGL 内容。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:flutter_web_gl/flutter_web_gl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter WebGL Example'),
),
body: Center(
child: WebGLView(
onWebViewCreated: (WebGLController controller) {
// 在这里初始化 WebGL 上下文并加载你的 3D 场景
controller.initialize();
controller.loadScene();
},
),
),
),
);
}
}
4. 初始化 WebGL 上下文
在 onWebViewCreated
回调中,你可以初始化 WebGL 上下文并加载你的 3D 场景。以下是一个简单的示例:
controller.initialize();
controller.loadScene();
5. 编写 WebGL 代码
你可以在 WebGLController
中编写 WebGL 代码。例如,你可以初始化 WebGL 上下文、加载着色器、创建缓冲区等。
class WebGLController {
void initialize() {
// 初始化 WebGL 上下文
}
void loadScene() {
// 加载 3D 场景
}
}
6. 运行应用
最后,运行你的 Flutter Web 应用:
flutter run -d chrome