Flutter插件flgl的使用_flgl插件用于在 Flutter 应用程序中暴露 OpenGL ES 功能
Flutter插件flgl的使用_flgl插件用于在 Flutter 应用程序中暴露 OpenGL ES 功能
flgl
插件用于在 Flutter 应用程序中暴露 OpenGL ES 功能。
Flutter插件flgl开始使用
首先,在你的 pubspec.yaml
文件中添加 flgl
依赖:
dependencies:
flgl: ^x.y.z
然后运行 flutter pub get
来安装插件。
如何使用 flgl
渲染 3D 图形
flgl
插件使用 Texture
小部件来渲染 3D 图形。但 Texture
小部件需要一个纹理 ID。例如,在 Android 设备上,你可以使用平台特定的 Android 代码通过平台通道获取纹理 ID。
然后在 Android 代码中进行第二次调用以初始化 EGL,并使用 Dart 侧的 OpenGL ES 绑定来暴露 GL 上下文。
如果你知道如何改进插件,欢迎提交拉取请求。
使用 flgl_3d
类可以做什么?
flgl
插件暴露了 GL 上下文,但与 flgl_3d
结合使用时更加强大。这样你就可以像使用 three.js 一样构建 3D 应用程序,但需要编写更少的代码。
示例代码:使用 flgl_3d
类
import 'package:flgl/flgl.dart';
import 'package:flgl/flgl_3d.dart';
void initScene() {
// 设置相机。
camera = PerspectiveCamera(45, (width * flgl.dpr) / (height * flgl.dpr), 1, 2000);
camera.setPosition(Vector3(0, 0, 300));
// 设置渲染器。
renderer = Renderer(gl, flgl);
renderer.setBackgroundColor(0, 0, 0, 1);
renderer.setWidth(width);
renderer.setHeight(height);
renderer.setDPR(dpr);
// 创建立方体几何。
BoxGeometry boxGeometry = BoxGeometry(0.5);
// 创建立方体材质。
MeshBasicMaterial material = MeshBasicMaterial(
color: Color(1.0, 1.0, 0.0, 1.0),
);
// 创建立方体网格。
Mesh boxMesh = Mesh(gl, boxGeometry, material);
boxMesh.name = 'box';
boxMesh.setPosition(Vector3(4, 0, 0));
boxMesh.setScale(Vector3(100, 100, 100));
scene.add(boxMesh);
}
/// 渲染场景。
void render() {
renderer.render(scene, camera);
}
需要帮助?
查看 examples
目录中的示例。
已知问题
该插件尚不稳定,可以渲染 3D 对象,但我需要帮助解决一些内存问题,并确保应用程序不会崩溃。
我已经在 Android 设备上测试过该插件。
可能问题是由于 EGL 设置或释放。由于某些未知原因,应用程序在第 28 次重新加载后会冻结。
如何提供帮助
核心文件位于 lib/
目录中。
例如,在 lib/flgl.dart
是插件的根目录,你可以找到以下内容:
- 如何获取纹理 ID,
- 如何初始化 EGL,
- 如何准备 EGL 上下文,
- 如何更新纹理,
- 如何释放插件。
示例渲染
import 'package:flgl/flgl.dart';
import 'package:flgl/openGL/contexts/open_gl_context_es.dart';
import 'package:flgl/flgl_viewport.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:typed_data';
import '../gl_utils.dart';
class Example1 extends StatefulWidget {
const Example1({Key? key}) : super(key: key);
[@override](/user/override)
_Example1State createState() => _Example1State();
}
class _Example1State extends State<Example1> {
bool initialized = false;
dynamic positionLocation;
dynamic positionBuffer;
dynamic program;
late Flgl flgl;
late OpenGLContextES gl;
late int width = 1333;
late int height = 752 - 80 - 48;
[@override](/user/override)
void initState() {
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Example Hello world"),
),
body: Column(
children: [
FLGLViewport(
width: width,
height: height,
onInit: (Flgl _flgl) {
setState(() {
initialized = true;
flgl = _flgl;
gl = flgl.gl;
initGl();
draw();
});
}),
Row(
children: [
TextButton(
onPressed: () {
render();
},
child: const Text("Render"),
)
],
)
],
),
);
}
render() {
draw();
}
String vertexShaderSource = """
// an attribute will receive data from a buffer
attribute vec4 a_position;
// all shaders have a main function
void main() {
// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
}
""";
String fragmentShaderSource = """
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default. It means "medium precision"
precision mediump float;
void main() {
// gl_FragColor is a special variable a fragment shader
// is responsible for setting
gl_FragColor = vec4(1, 0, 0.5, 1); // return reddish-purple
}
""";
int createShader(OpenGLContextES gl, int type, String source) {
int shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success == 0 || success == false) {
print("Error compiling shader: " + gl.getShaderInfoLog(shader));
throw 'Failed to create the shader';
}
return shader;
}
int createProgram(OpenGLContextES gl, int vertexShader, int fragmentShader) {
int program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success != 0 || success != false) {
return program;
}
print('getProgramInfoLog: ${gl.getProgramInfoLog(program)}');
gl.deleteProgram(program);
throw 'failed to create the program';
}
initGl() {
int vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
int fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
program = createProgram(gl, vertexShader, fragmentShader);
positionLocation = gl.getAttribLocation(program, "a_position");
positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// three 2d points
List<double> positions = [
0, 0, //
0, 0.5, //
0.5, 0, //
];
gl.bufferData(gl.ARRAY_BUFFER, Float32List.fromList(positions), gl.STATIC_DRAW);
}
draw() {
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, width, height);
// Clear the canvas. sets the canvas background color.
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0;
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
// draw TRIANGLE
var primitiveType = gl.TRIANGLES;
var offset_draw = 0;
var count = 3;
gl.drawArrays(primitiveType, offset_draw, count);
// !super important.
gl.finish();
flgl.updateTexture();
}
}