Flutter三维对象渲染插件three_js_objects的使用

Flutter三维对象渲染插件three_js_objects的使用

Gix of metaballs moving.

three_js_objects 是一个基于 Dart 的库,允许用户在其项目中添加复杂的三维对象。该库是对 three.js 和 three_dart 的 Dart 转换版本,最初由 @mrdoob 创建,并由 @wasabia 进行了 Dart 转换。

使用

该 API 允许用户向其 three_js 项目中添加对象帮助器。

参考示例

以下是一个完整的示例代码,展示了如何在 Flutter 应用程序中使用 three_js_objects 插件来渲染三维对象。

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:math' as math;
import 'package:three_js_controls/three_js_controls.dart';
import 'package:three_js_core/three_js_core.dart' as three;
import 'package:three_js_objects/three_js_objects.dart';
import 'package:three_js_math/three_js_math.dart' as tmath;

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter threeJs',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const Marching(),
    );
  }
}

class EffectController {
  EffectController({
    this.material = 'shiny',
    this.speed = 1.0,
    this.numBlobs = 10,
    this.resolution = 28,
    this.isolation = 80,
    this.floor = true,
    this.wallx = false,
    this.wallz = false,
  }) {
    this.dummy = dummy ?? () {};
  }

  String material;
  double speed;
  int numBlobs;
  int resolution;
  int isolation;
  bool floor;
  bool wallx;
  bool wallz;

  late Function? dummy;
}

class Marching extends StatefulWidget {
  const Marching({super.key});

  [@override](/user/override)
  _MarchingState createState() => _MarchingState();
}

class _MarchingState extends State<Marching> {
  late three.ThreeJS threeJs;

  [@override](/user/override)
  void initState() {
    threeJs = three.ThreeJS(
      onSetupComplete: () => setState(() {}),
      setup: setup,
      settings: three.Settings(
        renderOptions: {"format": tmath.RGBAFormat, "samples": 8}
      )
    );
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    threeJs.dispose();
    controls.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: threeJs.build()
    );
  }

  late OrbitControls controls;
  late EffectController effectController;
  String currentMaterial = 'plastic';
  late MarchingCubes effect;
  double time = 0;

  // 更新 marching cubes 中的球体
  void updateCubes(MarchingCubes object, double time, int numblobs, bool floor, bool wallx, bool wallz) {
    object.reset();

    // 填充场中的球体
    final rainbow = [
      tmath.Color.fromHex32(0xff0000),
      tmath.Color.fromHex32(0xffbb00),
      tmath.Color.fromHex32(0xffff00),
      tmath.Color.fromHex32(0x00ff00),
      tmath.Color.fromHex32(0x0000ff),
      tmath.Color.fromHex32(0x9400bd),
      tmath.Color.fromHex32(0xc800eb)
    ];

    const subtract = 12;
    final strength = 1.2 / ((math.sqrt(numblobs) - 1) / 4 + 1);

    for (int i = 0; i < numblobs; i++) {
      final ballx = math.sin(i + 1.26 * time * (1.03 + 0.5 * math.cos(0.21 * i))) * 0.27 + 0.5;
      final bally = (math.cos(i + 1.12 * time * math.cos(1.22 + 0.1424 * i))).abs() * 0.77; // 沉入地板
      final ballz = math.cos(i + 1.32 * time * 0.1 * math.sin((0.92 + 0.53 * i))) * 0.27 + 0.5;

      if (currentMaterial == 'multiColors') {
        object.addBall(ballx, bally, ballz, strength, subtract, rainbow[i % 7]);
      } else {
        object.addBall(ballx, bally, ballz, strength, subtract);
      }
    }

    if (floor) object.addPlaneY(2, 12);
    if (wallz) object.addPlaneZ(2, 12);
    if (wallx) object.addPlaneX(2, 12);

    object.update();
  }

  Future<void> setup() async {
    threeJs.scene = three.Scene();
    threeJs.scene.background = tmath.Color.fromHex32(0x050505);

    threeJs.camera = three.PerspectiveCamera(45, threeJs.width / threeJs.height, 1, 10000);
    threeJs.camera.position.setValues(-500, 500, 1500);

    // 灯光
    three.DirectionalLight light = three.DirectionalLight(0xffffff, 3);
    light.position.setValues(0.5, 0.5, 1);
    threeJs.scene.add(light);

    three.PointLight pointLight = three.PointLight(0xff7c00, 3, 0, 0);
    pointLight.position.setValues(0, 0, 100);
    threeJs.scene.add(pointLight);

    three.AmbientLight ambientLight = three.AmbientLight(0x323232, 3);
    threeJs.scene.add(ambientLight);

    // 材质
    Map<String, three.Material> materials = generateMaterials();

    // marching cubes
    double resolution = 28;

    effect = MarchingCubes(resolution, materials[currentMaterial], true, true, 100000);
    effect.position.setValues(0, 0, 0);
    effect.scale.setValues(700, 700, 700);

    effect.enableUvs = false;
    effect.enableColors = false;

    threeJs.scene.add(effect);

    // 控制器
    controls = OrbitControls(threeJs.camera, threeJs.globalKey);
    controls.minDistance = 500;
    controls.maxDistance = 5000;

    effectController = EffectController(
      material: 'plastic',
      speed: 1.0,
      numBlobs: 10,
      resolution: 28,
      isolation: 80,
      floor: true,
      wallx: false,
      wallz: false,
    );

    threeJs.addAnimationEvent((dt) {
      controls.update();
      time += dt * effectController.speed * 0.5;
      updateCubes(effect, time, effectController.numBlobs, effectController.floor, effectController.wallx, effectController.wallz);
    });
  }

  Map<String, three.Material> generateMaterials() {
    final materials = {
      'shiny': three.MeshStandardMaterial.fromMap({'color': 0x9c0000, 'roughness': 0.1, 'metalness': 1.0}),
      'chrome': three.MeshLambertMaterial.fromMap({'color': 0xffffff}),
      'liquid': three.MeshLambertMaterial.fromMap({'color': 0xffffff, 'refractionRatio': 0.85}),
      'matte': three.MeshPhongMaterial.fromMap({'specular': 0x494949, 'shininess': 1}),
      'flat': three.MeshLambertMaterial.fromMap({'flatShading': true}),
      'textured': three.MeshPhongMaterial.fromMap({'color': 0xffffff, 'specular': 0x111111, 'shininess': 1}),
      'colors': three.MeshPhongMaterial.fromMap({'color': 0xffffff, 'specular': 0xffffff, 'shininess': 2, 'vertexColors': true}),
      'multiColors': three.MeshPhongMaterial.fromMap({'shininess': 2, 'vertexColors': true}),
      'plastic': three.MeshPhongMaterial.fromMap({'color': 0xff414141, 'specular': tmath.Color(0.5, 0.5, 0.5), 'shininess': 15}),
    };
    return materials;
  }
}

更多关于Flutter三维对象渲染插件three_js_objects的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,以下是一个关于如何在Flutter中使用three_js_objects插件来渲染三维对象的代码案例。请注意,three_js_objects并不是一个官方或广泛认可的Flutter插件,因此假设它是一个自定义封装或第三方库,用于在Flutter中集成Three.js的功能。实际使用时,请确保您已经正确安装并配置了该插件。

首先,确保您已经在pubspec.yaml文件中添加了three_js_objects依赖项(假设它存在于pub.dev或作为本地依赖):

dependencies:
  flutter:
    sdk: flutter
  three_js_objects: ^x.y.z  # 替换为实际的版本号

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

接下来是一个简单的Flutter应用示例,它使用three_js_objects插件来渲染一个旋转的立方体。由于Three.js通常在Web环境中运行,而Flutter主要用于移动和桌面应用,这里我们将使用webview_flutter插件来嵌入一个Web视图,并在其中运行Three.js代码。然而,如果three_js_objects插件提供了直接的方法,我们将直接使用该插件的功能。

假设three_js_objects提供了直接的方法

以下是一个假设的示例,展示如何使用three_js_objects来渲染一个立方体:

import 'package:flutter/material.dart';
import 'package:three_js_objects/three_js_objects.dart'; // 假设这是插件的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Three.js Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Three.js Demo'),
        ),
        body: Center(
          child: ThreeJSObjectView(
            sceneBuilder: (ThreeJSController controller) {
              // 创建一个场景
              var scene = controller.createScene();

              // 创建一个相机
              var camera = controller.createPerspectiveCamera(
                fov: 75,
                aspect: controller.screenAspect,
                near: 0.1,
                far: 1000,
              );

              // 设置相机位置
              camera.position.z = 5;

              // 创建一个渲染器
              var renderer = controller.createWebGLRenderer();
              renderer.setSize(controller.screenWidth, controller.screenHeight);

              // 创建一个立方体几何体和材质
              var geometry = controller.createBoxGeometry(width: 1, height: 1, depth: 1);
              var material = controller.createMeshBasicMaterial(color: 0x00ff00);
              var cube = controller.createMesh(geometry, material);

              // 将立方体添加到场景中
              scene.add(cube);

              // 渲染循环
              controller.animate(() {
                cube.rotation.x += 0.01;
                cube.rotation.y += 0.01;
                controller.render(scene, camera);
              });

              return scene;
            },
          ),
        ),
      ),
    );
  }
}

// 假设ThreeJSObjectView和ThreeJSController是插件提供的Widget和控制器类
class ThreeJSObjectView extends StatefulWidget {
  final SceneBuilderCallback sceneBuilder;

  const ThreeJSObjectView({Key key, @required this.sceneBuilder}) : super(key: key);

  @override
  _ThreeJSObjectViewState createState() => _ThreeJSObjectViewState();
}

typedef SceneBuilderCallback = void Function(ThreeJSController controller);

class _ThreeJSObjectViewState extends State<ThreeJSObjectView> {
  // 插件可能需要的控制器状态管理
  // ...

  @override
  Widget build(BuildContext context) {
    // 这里应该是插件提供的自定义Widget,用于渲染Three.js场景
    // 假设为ThreeJSObjectWidget,并传递必要的回调和配置
    return Container(
      // 假设的Widget和属性,实际使用时请参考插件文档
      child: ThreeJSObjectWidget(
        onSceneBuild: widget.sceneBuilder,
      ),
    );
  }
}

// 假设的ThreeJSController类,用于管理Three.js场景、相机和渲染器
class ThreeJSController {
  // 假设的方法和属性
  int get screenWidth => 800; // 实际应获取屏幕宽度
  int get screenHeight => 600; // 实际应获取屏幕高度
  double get screenAspect => screenWidth / screenHeight;

  Scene createScene() {
    // 创建并返回一个Three.js场景
    // ...
    return Scene(); // 假设的Three.js Scene对象
  }

  PerspectiveCamera createPerspectiveCamera({double fov, double aspect, double near, double far}) {
    // 创建并返回一个Three.js透视相机
    // ...
    return PerspectiveCamera(fov, aspect, near, far);
  }

  WebGLRenderer createWebGLRenderer() {
    // 创建并返回一个Three.js WebGL渲染器
    // ...
    return WebGLRenderer();
  }

  BoxGeometry createBoxGeometry({double width, double height, double depth}) {
    // 创建并返回一个Three.js立方体几何体
    // ...
    return BoxGeometry(width, height, depth);
  }

  MeshBasicMaterial createMeshBasicMaterial({int color}) {
    // 创建并返回一个Three.js基本网格材质
    // ...
    return MeshBasicMaterial(color: color);
  }

  Mesh createMesh(Geometry geometry, Material material) {
    // 创建并返回一个Three.js网格对象
    // ...
    return Mesh(geometry, material);
  }

  void animate(VoidCallback callback) {
    // 动画循环,通常使用requestAnimationFrame
    // ...
    Timer.periodic(Duration(milliseconds: 16), (timer) {
      callback();
    });
  }

  void render(Scene scene, Camera camera) {
    // 渲染场景和相机
    // ...
  }
}

// 假设的Three.js类,实际使用时请参考Three.js文档和插件封装
class Scene {}
class PerspectiveCamera {}
class WebGLRenderer {}
class BoxGeometry extends Geometry {}
class MeshBasicMaterial extends Material {}
class Mesh {}
class Geometry {}
class Material {}
class Camera {}

注意

  1. 插件封装:上述代码是基于假设的three_js_objects插件封装。实际使用时,请参考该插件的官方文档和API。
  2. Web视图:如果three_js_objects插件不存在或不支持直接渲染,您可能需要使用webview_flutter插件来嵌入一个Web视图,并在其中运行Three.js代码。
  3. 性能考虑:在Flutter中嵌入Web视图可能会影响性能,特别是在移动设备上。因此,如果可能,请寻找直接在Flutter中渲染三维对象的解决方案。

希望这个示例对您有所帮助!如果您有进一步的问题或需要关于特定插件的帮助,请提供更多详细信息。

回到顶部