Flutter OpenGL图形渲染插件flutter_opengl的使用
Flutter OpenGL图形渲染插件flutter_opengl的使用
简介
flutter_opengl
是一个用于在Flutter中进行OpenGL ES图形渲染的插件,它使用 Texture()
小部件来显示OpenGL渲染的内容。该插件支持Android、Linux和Windows平台。许多来自ShaderToy.com的着色器可以直接复制粘贴到此插件中使用。
支持的平台
平台 | 支持情况 |
---|---|
Android | ✅ |
Windows | ✅ |
Linux | ✅ |
iOS | x |
MacOS | x |
Web | x |
插件工作流程
- 通过
MethodChannel
请求原生代码获取纹理ID。 - 使用获取到的纹理ID创建
Texture()
小部件。 - 设置顶点着色器和片段着色器。
- 启动渲染器。
- 可以在运行时动态更改着色器源代码。
主要功能
- iResolution:
vec3
类型的uniform,表示纹理的尺寸。 - iTime:
float
类型的uniform,表示自着色器创建以来的时间。 - iMouse:
vec4
类型的uniform,表示鼠标或触摸位置。 - iChannel[0-3]:
Sampler2D
类型的uniform纹理。
示例代码
以下是一个完整的示例代码,展示了如何使用 flutter_opengl
插件来渲染一个来自ShaderToy的着色器。
import 'package:flutter/material.dart';
import 'package:flutter_opengl/flutter_opengl.dart';
void main() {
// 初始化OpenGL控制器
OpenGLController().initializeGL();
runApp(const MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// 定义片段着色器代码
final String fShader = '''
// https://www.shadertoy.com/view/XlfGRj
// Star Nest by Pablo Roman Andrioli
#define iterations 17
#define formuparam 0.53
#define volsteps 20
#define stepsize 0.1
#define zoom 0.800
#define tile 0.850
#define speed 0.010
#define brightness 0.0015
#define darkmatter 0.300
#define distfading 0.730
#define saturation 0.850
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// 获取坐标和方向
vec2 uv = fragCoord.xy / iResolution.xy - 0.5;
uv.y *= iResolution.y / iResolution.x;
vec3 dir = vec3(uv * zoom, 1.);
float time = iTime * speed + 0.25;
// 鼠标旋转
float a1 = 0.5 + iMouse.x / iResolution.x * 2.;
float a2 = 0.8 + iMouse.y / iResolution.y * 2.;
mat2 rot1 = mat2(cos(a1), sin(a1), -sin(a1), cos(a1));
mat2 rot2 = mat2(cos(a2), sin(a2), -sin(a2), cos(a2));
dir.xz *= rot1;
dir.xy *= rot2;
vec3 from = vec3(1., 0.5, 0.5);
from += vec3(time * 2., time, -2.);
from.xz *= rot1;
from.xy *= rot2;
// 体积渲染
float s = 0.1, fade = 1.;
vec3 v = vec3(0.);
for (int r = 0; r < volsteps; r++) {
vec3 p = from + s * dir * 0.5;
p = abs(vec3(tile) - mod(p, vec3(tile * 2.))); // 瓦片折叠
float pa, a = pa = 0.;
for (int i = 0; i < iterations; i++) {
p = abs(p) / dot(p, p) - formuparam; // 魔法公式
a += abs(length(p) - pa); // 绝对平均变化的总和
pa = length(p);
}
float dm = max(0., darkmatter - a * a * 0.001); // 暗物质
a *= a * a; // 增加对比度
if (r > 6) fade *= 1. - dm; // 暗物质,不渲染近处
v += fade;
v += vec3(s, s * s, s * s * s * s) * a * brightness * fade; // 基于距离的颜色
fade *= distfading; // 距离衰减
s += stepsize;
}
v = mix(vec3(length(v)), v, saturation); // 颜色调整
fragColor = vec4(v * 0.01, 1.);
}
''';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 400,
height: 300,
child: FutureBuilder(
// 创建表面,指定实际的纹理尺寸
future: OpenGLController().openglPlugin.createSurface(300, 200),
builder: (_, snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return const SizedBox.shrink();
}
// 当获取到纹理ID后,可以启动渲染器,设置着色器并显示
// 启动渲染线程
OpenGLController().openglFFI.startThread();
// 设置片段着色器
OpenGLController().openglFFI.setShaderToy(fShader);
// 构建纹理小部件
return OpenGLTexture(id: snapshot.data!);
},
),
),
),
);
}
}
插件方法
以下是 OpenGLController().openglFFI
提供的主要方法:
方法名 | 描述 |
---|---|
bool rendererStatus() |
返回true,如果纹理已成功通过 createSurface() 创建 |
Size getTextureSize() |
获取当前纹理的尺寸,未设置时返回 Size(-1, -1) |
startThread() |
启动绘制线程循环 |
stopThread() |
删除着色器、删除纹理并停止绘制线程循环 |
String setShader(bool isContinuous, String vertexShader, String fragmentShader) |
设置顶点和片段着色器,返回编译错误字符串或空字符串 |
String setShaderToy(String fragmentShader) |
设置来自ShaderToy的片段着色器 |
String getVertexShader() |
获取当前顶点着色器文本 |
String getFragmentShader() |
获取当前片段着色器文本 |
addShaderToyUniforms() |
添加 iMouse 、iResolution 、iTime 和 iChannel[0-3] uniform |
setMousePosition(Offset startingPos, Offset pos, PointerEventType eventType, Size twSize) |
设置 iMouse uniform,处理鼠标输入 |
double getFps() |
获取当前FPS(限制为100) |
bool addBoolUniform(String name, bool val) |
添加布尔uniform,返回true表示成功,false表示已存在 |
bool addIntUniform(String name, int val) |
添加整数uniform,返回true表示成功,false表示已存在 |
bool addFloatUniform(String name, double val) |
添加浮点uniform,返回true表示成功,false表示已存在 |
bool addVec2Uniform(String name, List<double> val) |
添加 vec2 uniform,返回true表示成功,false表示已存在 |
bool addVec3Uniform(String name, List<double> val) |
添加 vec3 uniform,返回true表示成功,false表示已存在 |
bool addVec4Uniform(String name, List<double> val) |
添加 vec4 uniform,返回true表示成功,false表示已存在 |
bool addMat2Uniform(String name, List<double> val) |
添加 mat2 uniform,返回true表示成功,false表示已存在 |
bool addMat3Uniform(String name, List<double> val) |
添加 mat3 uniform,返回true表示成功,false表示已存在 |
bool addMat4Uniform(String name, List<double> val) |
添加 mat4 uniform,返回true表示成功,false表示已存在 |
bool addSampler2DUniform(String name, int width, int height, Uint8List val) |
添加 Sampler2D uniform,图像格式为RGBA32 |
bool replaceSampler2DUniform(String name, int width, int height, Uint8List val) |
替换 Sampler2D uniform,使用不同尺寸的图像 |
bool setBoolUniform(String name, bool val) |
设置布尔uniform的值,返回false表示uniform不存在 |
bool setIntUniform(String name, int val) |
设置整数uniform的值,返回false表示uniform不存在 |
bool setFloatUniform(String name, double val) |
设置浮点uniform的值,返回false表示uniform不存在 |
bool setVec2Uniform(String name, List<double> val) |
设置 vec2 uniform的值,返回false表示uniform不存在 |
bool setVec3Uniform(String name, List<double> val) |
设置 vec3 uniform的值,返回false表示uniform不存在 |
bool setVec4Uniform(String name, List<double> val) |
设置 vec4 uniform的值,返回false表示uniform不存在 |
bool setMat2Uniform(String name, List<double> val) |
设置 mat2 uniform的值,返回false表示uniform不存在 |
bool setMat3Uniform(String name, List<double> val) |
设置 mat3 uniform的值,返回false表示uniform不存在 |
bool setMat4Uniform(String name, List<double> val) |
设置 mat4 uniform的值,返回false表示uniform不存在 |
bool setSampler2DUniform(String name, Uint8List val) |
替换 Sampler2D uniform,使用相同尺寸的图像 |
bool startCaptureOnSampler2D(String name, String completeFilePath) |
设置 Sampler2D uniform,使用OpenCV VideoCapture捕获帧 |
bool stopCapture() |
停止捕获线程 |
设置
Linux
确保安装了 glew
、glm
和 OpenCV
包。
Windows
-
克隆
Native_SDK
:git clone https://github.com/powervr-graphics/Native_SDK.git
只保留克隆库中的
lib
和include
目录。 -
克隆
glm
:git clone https://github.com/g-truc/glm.git
更多关于Flutter OpenGL图形渲染插件flutter_opengl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter OpenGL图形渲染插件flutter_opengl的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用flutter_opengl
插件进行OpenGL图形渲染的示例代码。这个示例将展示如何设置OpenGL上下文并绘制一些基本的图形。
首先,确保你已经在pubspec.yaml
文件中添加了flutter_opengl
依赖:
dependencies:
flutter:
sdk: flutter
flutter_opengl: ^x.y.z # 请替换为最新的版本号
然后,运行flutter pub get
来获取依赖。
接下来,创建一个Flutter项目,并在lib
目录下创建一个新的Dart文件,比如main.dart
。在这个文件中,我们将设置OpenGL上下文并绘制一个简单的三角形。
import 'package:flutter/material.dart';
import 'package:flutter_opengl/flutter_opengl.dart';
import 'dart:typed_data';
import 'dart:ui' as ui;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter OpenGL Example'),
),
body: OpenGLWidget(),
),
);
}
}
class OpenGLWidget extends StatefulWidget {
@override
_OpenGLWidgetState createState() => _OpenGLWidgetState();
}
class _OpenGLWidgetState extends State<OpenGLWidget> {
late OpenGLRenderer _renderer;
@override
void initState() {
super.initState();
_renderer = OpenGLRenderer();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: CustomPaint(
painter: _OpenGLPainter(_renderer),
size: Size(double.infinity, double.infinity),
),
);
}
@override
void dispose() {
_renderer.dispose();
super.dispose();
}
}
class OpenGLRenderer {
late ui.Window _window;
late FlutterOpenGL _flutterOpenGL;
OpenGLRenderer() {
_window = ui.window;
_flutterOpenGL = FlutterOpenGL(_window);
// 设置视口大小
_flutterOpenGL.glViewport(0, 0, _window.physicalSize.width.toInt(), _window.physicalSize.height.toInt());
// 初始化OpenGL状态
_initializeOpenGL();
}
void _initializeOpenGL() {
// 启用深度测试
_flutterOpenGL.glEnable(0x0B71); // GL_DEPTH_TEST
// 设置清除颜色为黑色
_flutterOpenGL.glClearColor(0.0, 0.0, 0.0, 1.0);
// 创建着色器程序
int vertexShader = _loadShader(GL_VERTEX_SHADER, vertexShaderSource);
int fragmentShader = _loadShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
int shaderProgram = _createProgram(vertexShader, fragmentShader);
_flutterOpenGL.glUseProgram(shaderProgram);
// 获取属性位置
int positionAttribLocation = _flutterOpenGL.glGetAttribLocation(shaderProgram, 'a_position');
_flutterOpenGL.glEnableVertexAttribArray(positionAttribLocation);
// 设置顶点数据
Float32List vertices = Float32List.fromList([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5,
]);
Uint16List indices = Uint16List.fromList([0, 1, 2]);
// 创建并绑定缓冲区
int vertexBuffer = _flutterOpenGL.glGenBuffers();
_flutterOpenGL.glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
_flutterOpenGL.glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
int indexBuffer = _flutterOpenGL.glGenBuffers();
_flutterOpenGL.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
_flutterOpenGL.glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
// 指定顶点属性指针
_flutterOpenGL.glVertexAttribPointer(positionAttribLocation, 2, GL_FLOAT, false, 0, 0);
}
int _loadShader(int type, String source) {
int shader = _flutterOpenGL.glCreateShader(type);
_flutterOpenGL.glShaderSource(shader, source);
_flutterOpenGL.glCompileShader(shader);
int compileStatus = _flutterOpenGL.glGetShaderiv(shader, GL_COMPILE_STATUS);
if (compileStatus == 0) {
String infoLog = _flutterOpenGL.glGetShaderInfoLog(shader);
throw Exception('Failed to compile shader: $infoLog');
}
return shader;
}
int _createProgram(int vertexShader, int fragmentShader) {
int program = _flutterOpenGL.glCreateProgram();
_flutterOpenGL.glAttachShader(program, vertexShader);
_flutterOpenGL.glAttachShader(program, fragmentShader);
_flutterOpenGL.glLinkProgram(program);
int linkStatus = _flutterOpenGL.glGetProgramiv(program, GL_LINK_STATUS);
if (linkStatus == 0) {
String infoLog = _flutterOpenGL.glGetProgramInfoLog(program);
throw Exception('Failed to link program: $infoLog');
}
return program;
}
void render() {
_flutterOpenGL.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 绘制三角形
int indexCount = 3;
_flutterOpenGL.glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
// 交换缓冲区
_flutterOpenGL.glFlush();
_flutterOpenGL.glFinish();
_window.scheduleFrame();
}
void dispose() {
_flutterOpenGL.dispose();
}
}
class _OpenGLPainter extends CustomPainter {
final OpenGLRenderer renderer;
_OpenGLPainter(this.renderer);
@override
void paint(Canvas canvas, Size size) {
renderer.render();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
const String vertexShaderSource = '''
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
''';
const String fragmentShaderSource = '''
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
''';
在这个示例中,我们创建了一个自定义的OpenGLWidget
,它包含一个OpenGLRenderer
对象来管理OpenGL的渲染逻辑。OpenGLRenderer
类负责初始化OpenGL状态、加载着色器、设置顶点数据,并在每一帧中渲染内容。
请注意,这个示例假设你已经正确设置了Flutter开发环境,并且flutter_opengl
插件是可用的。如果flutter_opengl
插件的API有所变化,你可能需要根据最新的文档进行调整。此外,由于OpenGL渲染的复杂性,这个示例仅展示了基本的渲染流程,实际应用中可能需要更多的错误处理和资源管理。