Flutter OpenGL图形渲染插件flutter_opengl的使用

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

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

插件工作流程

  1. 通过 MethodChannel 请求原生代码获取纹理ID。
  2. 使用获取到的纹理ID创建 Texture() 小部件。
  3. 设置顶点着色器和片段着色器。
  4. 启动渲染器。
  5. 可以在运行时动态更改着色器源代码。

主要功能

  • iResolutionvec3 类型的uniform,表示纹理的尺寸。
  • iTimefloat 类型的uniform,表示自着色器创建以来的时间。
  • iMousevec4 类型的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() 添加 iMouseiResolutioniTimeiChannel[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

确保安装了 glewglmOpenCV 包。

Windows
  1. 克隆 Native_SDK

    git clone https://github.com/powervr-graphics/Native_SDK.git
    

    只保留克隆库中的 libinclude 目录。

  2. 克隆 glm

    git clone https://github.com/g-truc/glm.git
    

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

1 回复

更多关于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渲染的复杂性,这个示例仅展示了基本的渲染流程,实际应用中可能需要更多的错误处理和资源管理。

回到顶部