Flutter 3D渲染插件simple_3d_renderer的使用

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

Flutter 3D渲染插件simple_3d_renderer的使用

概述

simple_3d_renderer 是一个用于渲染 Sp3dObj 的Flutter插件。Sp3dObj 是一种为科学用途创建的简单3D格式,主要用于科学家使用。请参考以下需要一起使用的包:

如果你希望在直接编辑 Sp3dObj 的应用程序中实现撤销/重做功能,你可以使用以下包:

此外,虽然这是一个非常实验性的项目,但也有将其他3D格式转换为 Sp3dObj 的包。不过由于 Sp3dObj 主要用于科学目的,在功能上有显著差异,因此只支持最小兼容性。

使用方法

示例代码

import 'package:flutter/material.dart';
import 'package:simple_3d/simple_3d.dart';
import 'package:util_simple_3d/util_simple_3d.dart';
import 'package:simple_3d_renderer/simple_3d_renderer.dart';

void main() async {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late List<Sp3dObj> objs = [];
  late Sp3dWorld world;
  bool isLoaded = false;

  @override
  void initState() {
    super.initState();
    // 创建 Sp3dObj.
    Sp3dObj obj = UtilSp3dGeometry.cube(200, 200, 200, 4, 4, 4);
    obj.materials.add(FSp3dMaterial.green.deepCopy());
    obj.fragments[0].faces[0].materialIndex = 1;
    obj.materials[0] = FSp3dMaterial.grey.deepCopy()
      ..strokeColor = const Color.fromARGB(255, 0, 0, 255);
    obj.rotate(Sp3dV3D(1, 1, 0).nor(), 30 * 3.14 / 180);
    objs.add(obj);
    loadImage();
  }

  void loadImage() async {
    world = Sp3dWorld(objs);
    world.initImages().then((List<Sp3dObj> errorObjs) {
      setState(() {
        isLoaded = true;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    if (!isLoaded) {
      return MaterialApp(
        title: 'Sp3dRenderer',
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: const Color.fromARGB(255, 0, 255, 0),
          ),
          backgroundColor: const Color.fromARGB(255, 33, 33, 33),
          body: Container(),
        ),
      );
    } else {
      return MaterialApp(
        title: 'Sp3dRenderer',
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: const Color.fromARGB(255, 0, 255, 0),
          ),
          backgroundColor: const Color.fromARGB(255, 33, 33, 33),
          body: Column(
            children: [
              Sp3dRenderer(
                const Size(800, 800),
                const Sp3dV2D(400, 400),
                world,
                // 如果你想减少失真,可以远距离高倍率拍摄。
                Sp3dCamera(Sp3dV3D(0, 0, 3000), 6000),
                Sp3dLight(Sp3dV3D(0, 0, -1), syncCam: true),
              ),
            ],
          ),
        ),
      );
    }
  }
}

使用图片文件

例如,修改示例代码如下(*注意,为了简化起见保留了一些不必要的参数):

sample_image.png

sample_image.png

// 修改initState中的立方体。
Sp3dObj obj = UtilSp3dGeometry.cube(200,200,200,1,1,1);
--------------------------------------------------------------------
// 修改函数
void loadImage() async {
  this.objs[0].fragments[0].faces[0].materialIndex=1;
  this.objs[0].fragments[0].faces[1].materialIndex=1;
  this.objs[0].fragments[0].faces[2].materialIndex=1;
  this.objs[0].fragments[0].faces[3].materialIndex=1;
  this.objs[0].materials[1].imageIndex = 0;
  // 你可以在项目的assets/images文件夹下添加图片,并在pubspec.yaml中添加资源路径来使用图片。
  // 对于Flutter Web,还需要将其复制到你的web文件夹中。
  this.objs[0].images.add(await _readFileBytes("./assets/images/sample_image.png"));
  this.world = Sp3dWorld(objs);
  this.world.initImages().then(
    (List<Sp3dObj> errorObjs){
      setState(() {
        this.isLoaded = true;
      });
    }
  );
}

// 添加函数
Future<Uint8List> _readFileBytes(String filePath) async {
  ByteData bd = await rootBundle.load(filePath);
  return bd.buffer.asUint8List(bd.offsetInBytes,bd.lengthInBytes);
}

处理用户触摸事件

例如,修改示例代码如下:

// 在_MyAppState中添加变量。
ValueNotifier<int> vn = ValueNotifier<int>(0);
--------------------------------------------------------------------
// 重写Sp3dRenderer。
Sp3dRenderer(
  const Size(800, 800),
  const Sp3dV2D(400, 400),
  world,
  // 如果你想减少失真,可以远距离高倍率拍摄。
  Sp3dCamera(Sp3dV3D(0, 0, 30000), 60000),
  Sp3dLight(Sp3dV3D(0, 0, -1), syncCam: true),
  allowUserWorldRotation: true,
  checkTouchObj: true,
  vn: vn,
  onPanDown: (Sp3dGestureDetails d, Sp3dFaceObj? info){
    print("onPanDown");
    if(info!=null) {
      info.obj.move(Sp3dV3D(50, 0, 0));
      vn.value++;
    }
  },
  onPanCancel: (){
    print("onPanCancel");
  },
  onPanStart: (Sp3dGestureDetails d){
    print("onPanStart");
    print(d.toOffset());
  },
  onPanUpdate: (Sp3dGestureDetails d){
    print("onPanUpdate");
    print(d.toOffset());
  },
  onPanEnd: (Sp3dGestureDetails d){
    print("onPanEnd");
  },
  onPinchStart: (Sp3dGestureDetails d){
    print("onPinchStart");
    print(d.diffV);
  },
  onPinchUpdate: (Sp3dGestureDetails d){
    print("onPinchUpdate");
    print(d.diffV);
  },
  onPinchEnd: (Sp3dGestureDetails d){
    print("onPinchEnd");
    print(d.diffV);
  },
  onMouseScroll: (Sp3dGestureDetails d){
    print("onMouseScroll");
    print(d.diffV);
  },
)

保存或恢复 Sp3dWorld

如果你想保存或恢复多个 Sp3dObj 及其位置,Sp3dWorld 也提供了 toDictfromDict 方法。保存时建议使用 .s3dw 扩展名以避免混淆。

性能

在使用 CPU Ryzen5 5600 进行测试时,考虑了在调试模式和Web浏览器上绘制所需的时间。存在一些速度问题,如在CPU上运行且是单线程的。对于实时渲染,大约1000个立方体(8000个顶点)是一个极限,超过这个数量会变得沉重。

  • 100个立方体:338.6 fps(800个顶点)
  • 1000个立方体:34.1 fps
  • 2500个立方体:13.6 fps

请注意,不是所有对象都能表现得同样好,因为加速逻辑的存在。对于顶点较多的模型(如球体),能够舒适操作的数量要小得多。

版本控制

C部分将在版本升级时更改。

  • 添加变量、结构变化导致读取以前文件出现问题等重大变更:C.X.X
  • 添加方法等:X.C.X
  • 小的变更和错误修复:X.X.C

许可证

此软件根据MIT许可证发布,请参阅LICENSE文件。

版权声明

“Dart”名称和“Flutter”名称是Google LLC的商标。 *该包的开发者不是Google LLC。


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

1 回复

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


当然,以下是如何在Flutter项目中使用simple_3d_renderer插件进行3D渲染的示例代码。这个插件允许你在Flutter应用中进行基本的3D图形渲染。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加simple_3d_renderer依赖:

dependencies:
  flutter:
    sdk: flutter
  simple_3d_renderer: ^最新版本号 # 请替换为实际发布的最新版本号

然后运行flutter pub get来安装依赖。

2. 导入插件

在你的Dart文件中导入simple_3d_renderer

import 'package:simple_3d_renderer/simple_3d_renderer.dart';

3. 初始化3D渲染器

在你的Flutter应用中,你需要一个Simple3DRendererWidget来显示3D内容。下面是一个简单的例子,展示了如何初始化并显示一个3D立方体。

import 'package:flutter/material.dart';
import 'package:simple_3d_renderer/simple_3d_renderer.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Simple 3D Renderer Example'),
        ),
        body: Center(
          child: Simple3DRendererWidget(
            width: 400,
            height: 400,
            renderer: My3DRenderer(),
          ),
        ),
      ),
    );
  }
}

class My3DRenderer extends Simple3DRenderer {
  @override
  void onDrawFrame(Canvas3D canvas) {
    // 清除画布
    canvas.clear(Colors.white);

    // 设置视角矩阵
    Matrix4 perspectiveMatrix = Matrix4.identity();
    perspectiveMatrix.setPerspective(
      fovy: 45.0 * (Math.PI / 180.0),
      aspect: canvas.width / canvas.height,
      zNear: 0.1,
      zFar: 100.0,
    );

    // 设置视图矩阵
    Matrix4 viewMatrix = Matrix4.identity();
    viewMatrix.translate(Vector3(-0.0, 0.0, -5.0));

    // 设置模型矩阵(立方体)
    Matrix4 modelMatrix = Matrix4.identity();
    modelMatrix.scale(Vector3(1.0, 1.0, 1.0));

    // 组合矩阵
    Matrix4 mvpMatrix = perspectiveMatrix * viewMatrix * modelMatrix;

    // 绘制立方体
    drawCube(canvas, mvpMatrix);
  }

  void drawCube(Canvas3D canvas, Matrix4 mvpMatrix) {
    // 定义立方体的顶点位置和颜色
    List<float> vertices = [
      // 顶点位置(x, y, z)
      -1.0, -1.0, -1.0,  1.0, -1.0, -1.0,  1.0,  1.0, -1.0, -1.0,  1.0, -1.0,
      -1.0, -1.0,  1.0,  1.0, -1.0,  1.0,  1.0,  1.0,  1.0, -1.0,  1.0, -1.0,
      // 顶点颜色(r, g, b, a)
      1.0, 0.0, 0.0, 1.0,  1.0, 1.0, 0.0, 1.0,  1.0, 1.0, 1.0, 1.0,  0.0, 1.0, 0.0, 1.0,
      1.0, 0.0, 1.0, 1.0,  0.0, 0.0, 1.0, 1.0,  0.0, 1.0, 1.0, 1.0,  0.0, 1.0, 0.0, 1.0,
    ];

    // 定义立方体的索引(用于连接顶点形成面)
    List<int> indices = [
      0, 1, 2, 0, 2, 3, // 前面
      4, 5, 6, 4, 6, 7, // 后面
      0, 1, 5, 0, 5, 4, // 左面
      2, 3, 7, 2, 7, 6, // 右面
      0, 3, 7, 0, 7, 4, // 上面
      1, 2, 6, 1, 6, 5, // 下面
    ];

    // 绘制立方体
    canvas.drawIndexedTriangles(
      vertices: vertices,
      indices: indices,
      vertexStride: 6, // 每个顶点6个值(3个位置 + 3个颜色)
      indexStride: 1,
      modelViewProjectionMatrix: mvpMatrix,
    );
  }
}

4. 运行应用

现在你可以运行你的Flutter应用,应该会看到一个简单的3D立方体在屏幕上显示。

注意事项

  • simple_3d_renderer是一个简化的3D渲染插件,适用于基本的3D图形渲染。如果需要更复杂的3D渲染功能,可能需要考虑使用更专业的3D引擎,比如Flutter的flutter_three或原生平台的OpenGL/Vulkan等。
  • 确保你理解3D图形的基本概念,比如矩阵变换、顶点着色器和片段着色器等,以便更好地利用这个插件。

希望这个示例能帮助你在Flutter项目中成功使用simple_3d_renderer插件进行3D渲染!

回到顶部