当前位置:   article > 正文

Three.js机器人与星系动态场景(四):封装Threejs业务组件

Three.js机器人与星系动态场景(四):封装Threejs业务组件

实际在写业务的时候不会在每个组件里都写几十行的threejs的初始化工作。我们可以 将通用的threejs的场景、相机、render、轨道控制器等进行统一初始化。同时将非主体的函数提到组件外部,通过import导入进组件。将业务逻辑主体更清晰一些。下面的代码是基于react+threejs开发,感兴趣可以看看之前的博客关于这部分详细的介绍

Three.js机器人与星系动态场景:实现3D渲染与交互式控制-CSDN博客

Three.js机器人与星系动态场景(二):强化三维空间认识-CSDN博客

Three.js机器人与星系动态场景(三):如何实现动画-CSDN博客

封装ThreeTool类 

在src目录下新建BasicThree文件夹,index.ts

 

导入相关依赖

导入three的所有方法,命名为THREE

导入轨道控制器

性能监控库

字体加载

文本geometry

  1. import * as THREE from "three"; // 引入Three.js库
  2. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
  3. import Stats from "three/examples/jsm/libs/stats.module.js"; // 引入性能监控库
  4. import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
  5. import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";

 添加属性和构造方法

  1. export class ThreeTool {
  2. public camera: THREE.PerspectiveCamera; // 相机对象
  3. public scene: THREE.Scene; // 场景对象
  4. public renderer: THREE.WebGLRenderer; // 渲染器对象
  5. // 构造函数,初始化Three.js工具
  6. constructor() {
  7. this.renderer = this.initRenderer(); // 初始化渲染器
  8. this.scene = this.initScene(); // 初始化场景
  9. this.camera = this.initCamera(); // 初始化相机
  10. this.initOrbitControls();
  11. }
  12. }

初始化渲染器 

  1. // 初始化渲染器的方法
  2. public initRenderer(): THREE.WebGLRenderer {
  3. const renderer = new THREE.WebGLRenderer({ antialias: true });
  4. renderer.setPixelRatio(window.devicePixelRatio);
  5. renderer.setSize(window.innerWidth, window.innerHeight);
  6. return renderer;
  7. }

 初始化场景

  1. // 初始化场景的方法
  2. public initScene(): THREE.Scene {
  3. const scene = new THREE.Scene();
  4. return scene;
  5. }

初始化渲染器 

  1. // 初始化渲染器的方法
  2. public initRenderer(): THREE.WebGLRenderer {
  3. const renderer = new THREE.WebGLRenderer({ antialias: true });
  4. renderer.setPixelRatio(window.devicePixelRatio);
  5. renderer.setSize(window.innerWidth, window.innerHeight);
  6. return renderer;
  7. }

初始化相机 

  1. // 初始化相机的方法
  2. public initCamera(): THREE.PerspectiveCamera {
  3. const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  4. return camera;
  5. }

 初始化轨道控制器

  1. //初始化轨道控制器
  2. public initOrbitControls() {
  3. const controls = new OrbitControls(this.camera, this.renderer.domElement);
  4. controls.update();
  5. }

 初始化性能监控

  1. // 初始化性能监控的方法
  2. public initStats(container: HTMLDivElement) {
  3. const stats = new Stats();
  4. stats.dom.style.position = "absolute";
  5. stats.dom.style.left = "0";
  6. stats.dom.style.zIndex = "100";
  7. container.appendChild(stats.dom); // 将性能监控DOM元素添加到容器中
  8. return stats;
  9. }

 初始化辅助坐标系

  1. //初始化坐标系辅助
  2. public initAxisHelper(axesLength: number = 150, showText: boolean = true) {
  3. const helper = new THREE.AxesHelper(axesLength);
  4. if (showText) {
  5. const loader = new FontLoader();
  6. let meshX = new THREE.Mesh();
  7. let meshY = new THREE.Mesh();
  8. let meshZ = new THREE.Mesh();
  9. loader.load("fonts/optimer_regular.typeface.json", (font) => {
  10. meshX = this.createText("X", font);
  11. meshY = this.createText("Y", font);
  12. meshZ = this.createText("Z", font);
  13. meshX.position.x = 12;
  14. meshY.position.y = 12;
  15. meshZ.position.z = 12;
  16. this.scene.add(meshX);
  17. this.scene.add(meshY);
  18. this.scene.add(meshZ);
  19. });
  20. }
  21. this.scene.add(helper);
  22. }

 初始化文本

  1. private createText(content: string, font: any) {
  2. const textGeometry = new TextGeometry(content, {
  3. font: font,
  4. size: 1,
  5. depth: 0.1,
  6. curveSegments: 1,
  7. });
  8. textGeometry.center();
  9. const textMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); // front
  10. const mesh = new THREE.Mesh(textGeometry, textMaterial);
  11. return mesh;
  12. }

 完整代码

  1. import * as THREE from "three"; // 引入Three.js库
  2. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
  3. import Stats from "three/examples/jsm/libs/stats.module.js"; // 引入性能监控库
  4. import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
  5. import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
  6. export class ThreeTool {
  7. public camera: THREE.PerspectiveCamera; // 相机对象
  8. public scene: THREE.Scene; // 场景对象
  9. public renderer: THREE.WebGLRenderer; // 渲染器对象
  10. // 构造函数,初始化Three.js工具
  11. constructor() {
  12. this.renderer = this.initRenderer(); // 初始化渲染器
  13. this.scene = this.initScene(); // 初始化场景
  14. this.camera = this.initCamera(); // 初始化相机
  15. this.initOrbitControls();
  16. }
  17. public rendererContainer() {
  18. this.renderer.render(this.scene, this.camera); // 渲染场景和相机
  19. }
  20. // 初始化场景的方法
  21. public initScene(): THREE.Scene {
  22. const scene = new THREE.Scene();
  23. return scene;
  24. }
  25. // 初始化渲染器的方法
  26. public initRenderer(): THREE.WebGLRenderer {
  27. const renderer = new THREE.WebGLRenderer({ antialias: true });
  28. renderer.setPixelRatio(window.devicePixelRatio);
  29. renderer.setSize(window.innerWidth, window.innerHeight);
  30. return renderer;
  31. }
  32. // 初始化相机的方法
  33. public initCamera(): THREE.PerspectiveCamera {
  34. const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  35. return camera;
  36. }
  37. public initOrbitControls() {
  38. const controls = new OrbitControls(this.camera, this.renderer.domElement);
  39. controls.update();
  40. }
  41. // 初始化性能监控的方法
  42. public initStats(container: HTMLDivElement) {
  43. const stats = new Stats();
  44. stats.dom.style.position = "absolute";
  45. stats.dom.style.left = "0";
  46. stats.dom.style.zIndex = "100";
  47. container.appendChild(stats.dom); // 将性能监控DOM元素添加到容器中
  48. return stats;
  49. }
  50. public initAxisHelper(axesLength: number = 150, showText: boolean = true) {
  51. const helper = new THREE.AxesHelper(axesLength);
  52. if (showText) {
  53. const loader = new FontLoader();
  54. let meshX = new THREE.Mesh();
  55. let meshY = new THREE.Mesh();
  56. let meshZ = new THREE.Mesh();
  57. loader.load("fonts/optimer_regular.typeface.json", (font) => {
  58. meshX = this.createText("X", font);
  59. meshY = this.createText("Y", font);
  60. meshZ = this.createText("Z", font);
  61. meshX.position.x = 12;
  62. meshY.position.y = 12;
  63. meshZ.position.z = 12;
  64. this.scene.add(meshX);
  65. this.scene.add(meshY);
  66. this.scene.add(meshZ);
  67. });
  68. }
  69. this.scene.add(helper);
  70. }
  71. private createText(content: string, font: any) {
  72. const textGeometry = new TextGeometry(content, {
  73. font: font,
  74. size: 1,
  75. depth: 0.1,
  76. curveSegments: 1,
  77. });
  78. textGeometry.center();
  79. const textMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); // front
  80. const mesh = new THREE.Mesh(textGeometry, textMaterial);
  81. return mesh;
  82. }
  83. }

代码中使用

通过new ThreeTool()形式创建一个工具类的实例

  1. // 创建 ThreeTool 实例
  2. const instance = new ThreeTool();

 通过示例的属性操作相机、场景

  1. import { useEffect, useRef } from "react";
  2. import * as THREE from "three";
  3. import { generateRobot, generateStarts } from "./generate";
  4. import { ThreeTool } from "../../BasicThree";
  5. import Stats from "three/examples/jsm/libs/stats.module.js";
  6. /**
  7. * 创建一个Three.js场景,包括相机和渲染器
  8. */
  9. function Robot() {
  10. // 创建一个div容器,用于存放渲染的Three.js场景
  11. const containerRef = useRef<HTMLDivElement>(null);
  12. const statsRef = useRef<Stats>(); // 创建用于引用统计信息的 ref
  13. // 创建 ThreeTool 实例
  14. const instance = new ThreeTool();
  15. // 初始化相机位置和朝向
  16. instance.camera.position.set(15, 12, 8);
  17. instance.camera.lookAt(0, 0, 0);
  18. // 添加坐标系
  19. instance.initAxisHelper();
  20. // 生成机器人和星星
  21. const robot = generateRobot();
  22. const robot2 = generateRobot();
  23. robot2.position.x = 6;
  24. robot2.position.z = 6;
  25. const starts = generateStarts(200);
  26. // 将物体添加到场景
  27. instance.scene.add(robot, robot2, starts);
  28. // 创建并设置方向光
  29. const straightLight = new THREE.DirectionalLight(0xffffff, 5);
  30. straightLight.position.set(20, 20, 20);
  31. instance.scene.add(straightLight);
  32. // 动画函数
  33. const animate = () => {
  34. requestAnimationFrame(animate);
  35. robot.rotation.z -= 0.005;
  36. robot2.rotation.y -= 0.005;
  37. starts.rotation.y -= 0.001;
  38. starts.rotation.z += 0.001;
  39. starts.rotation.x += 0.001;
  40. instance.renderer.render(instance.scene, instance.camera);
  41. statsRef.current && statsRef.current.update(); // 更新统计信息
  42. };
  43. // 监听组件挂载和卸载
  44. useEffect(() => {
  45. if (containerRef.current) {
  46. containerRef.current.appendChild(instance.renderer.domElement);
  47. instance.renderer.render(instance.scene, instance.camera);
  48. statsRef.current = instance.initStats(containerRef.current); // 初始化统计信息
  49. // 启动动画循环
  50. animate();
  51. }
  52. }, [containerRef]);
  53. // 返回div容器,用于存放渲染的Three.js场景
  54. return <div ref={containerRef} style={{ width: "100vw", height: "100vh" }}></div>;
  55. }
  56. // 导出Robot组件
  57. export default Robot;

 generate模型生成文件

  1. import * as THREE from "three";
  2. function createHead() {
  3. //SphereGeometry创建球形几何体
  4. const head = new THREE.SphereGeometry(4, 32, 16, 0, Math.PI * 2, 0, Math.PI * 0.5);
  5. const headMaterial = new THREE.MeshStandardMaterial({
  6. color: 0x43b988,
  7. roughness: 0.5,
  8. metalness: 1.0,
  9. });
  10. const headMesh = new THREE.Mesh(head, headMaterial);
  11. return headMesh;
  12. }
  13. //触角
  14. function generateHorn(y: number, z: number, angle: number) {
  15. //触角 CapsuleGeometry 创建胶囊形状的几何体。胶囊形状可以看作是一个圆柱体两端加上半球体
  16. const line = new THREE.CapsuleGeometry(0.1, 2);
  17. const lineMaterial = new THREE.MeshStandardMaterial({
  18. color: 0x43b988,
  19. roughness: 0.5,
  20. metalness: 1.0,
  21. });
  22. const lineMesh = new THREE.Mesh(line, lineMaterial);
  23. lineMesh.position.y = y;
  24. lineMesh.position.z = z;
  25. lineMesh.rotation.x = angle;
  26. return lineMesh;
  27. }
  28. //机器人眼睛
  29. function generateEye(x: number, y: number, z: number) {
  30. //SphereGeometry创建球形几何体
  31. const eye = new THREE.SphereGeometry(0.5, 32, 16, 0, Math.PI * 2, 0, Math.PI * 2);
  32. const eyeMaterial = new THREE.MeshStandardMaterial({
  33. color: 0x212121,
  34. roughness: 0.5,
  35. metalness: 1.0,
  36. });
  37. const eyeMesh = new THREE.Mesh(eye, eyeMaterial);
  38. eyeMesh.position.x = x;
  39. eyeMesh.position.y = y;
  40. eyeMesh.position.z = z;
  41. return eyeMesh;
  42. }
  43. //机器人身体
  44. export function generateBody() {
  45. //CylinderGeometry第一个参数是上部分圆的半径,第二个参数是下部分圆的半径,第三个参数是高度,材质使用的跟腿一样
  46. const body = new THREE.CylinderGeometry(4, 4, 6);
  47. const bodyMaterial = new THREE.MeshStandardMaterial({
  48. color: 0x43b988,
  49. roughness: 0.5,
  50. metalness: 1.0,
  51. });
  52. const bodyMesh = new THREE.Mesh(body, bodyMaterial);
  53. return bodyMesh;
  54. }
  55. //胳膊、腿
  56. function generateLegs(y: number, z: number) {
  57. const leg1 = new THREE.CapsuleGeometry(1, 4);
  58. const legMaterial1 = new THREE.MeshStandardMaterial({
  59. color: 0x43b988,
  60. roughness: 0.5,
  61. metalness: 1.0,
  62. });
  63. const leg1Mesh = new THREE.Mesh(leg1, legMaterial1);
  64. leg1Mesh.position.y = y;
  65. leg1Mesh.position.z = z;
  66. return leg1Mesh;
  67. }
  68. //创建机器人
  69. export function generateRobot() {
  70. // 创建一个Three.js对象,用于存放机器人
  71. const robot = new THREE.Object3D();
  72. const headMesh = createHead();
  73. headMesh.position.y = 6.5;
  74. robot.add(headMesh);
  75. //眼睛
  76. const leftEye = generateEye(3, 8, -2);
  77. const rightEye = generateEye(3, 8, 2);
  78. robot.add(leftEye);
  79. robot.add(rightEye);
  80. const leftHorn = generateHorn(11, -1, (-Math.PI * 30) / 180);
  81. const rightHorn = generateHorn(11, 1, (Math.PI * 30) / 180);
  82. robot.add(leftHorn);
  83. robot.add(rightHorn);
  84. const body = generateBody();
  85. body.position.y = 4;
  86. robot.add(body);
  87. // 生成机器人左腿
  88. robot.add(generateLegs(0, -2));
  89. // 生成机器人右腿
  90. robot.add(generateLegs(0, 2));
  91. //胳膊
  92. robot.add(generateLegs(3, 5));
  93. robot.add(generateLegs(3, -5));
  94. //物体缩放
  95. robot.scale.x = 0.3;
  96. robot.scale.y = 0.3;
  97. robot.scale.z = 0.3;
  98. return robot;
  99. }
  100. //创建粒子星星
  101. export function generateStarts(num: number) {
  102. //制作粒子特效
  103. const starts = new THREE.Object3D();
  104. const obj = new THREE.SphereGeometry(0.2, 3, 3);
  105. const material = new THREE.MeshStandardMaterial({
  106. color: 0x43b988,
  107. roughness: 0.5,
  108. metalness: 5,
  109. });
  110. const mesh = new THREE.Mesh(obj, material);
  111. for (let i = 0; i < num; i++) {
  112. const target = new THREE.Mesh();
  113. target.copy(mesh);
  114. target.position.x = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
  115. target.position.y = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
  116. target.position.z = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
  117. starts.add(target);
  118. }
  119. return starts;
  120. }

效果图 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/820889
推荐阅读
相关标签
  

闽ICP备14008679号