赞
踩
实际在写业务的时候不会在每个组件里都写几十行的threejs的初始化工作。我们可以 将通用的threejs的场景、相机、render、轨道控制器等进行统一初始化。同时将非主体的函数提到组件外部,通过import导入进组件。将业务逻辑主体更清晰一些。下面的代码是基于react+threejs开发,感兴趣可以看看之前的博客关于这部分详细的介绍
Three.js机器人与星系动态场景:实现3D渲染与交互式控制-CSDN博客
在src目录下新建BasicThree文件夹,index.ts
导入three的所有方法,命名为THREE
导入轨道控制器类
性能监控库
字体加载
文本geometry
- import * as THREE from "three"; // 引入Three.js库
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
-
- import Stats from "three/examples/jsm/libs/stats.module.js"; // 引入性能监控库
- import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
- import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
- export class ThreeTool {
- public camera: THREE.PerspectiveCamera; // 相机对象
- public scene: THREE.Scene; // 场景对象
- public renderer: THREE.WebGLRenderer; // 渲染器对象
-
- // 构造函数,初始化Three.js工具
- constructor() {
- this.renderer = this.initRenderer(); // 初始化渲染器
- this.scene = this.initScene(); // 初始化场景
- this.camera = this.initCamera(); // 初始化相机
- this.initOrbitControls();
- }
- }
- // 初始化渲染器的方法
- public initRenderer(): THREE.WebGLRenderer {
- const renderer = new THREE.WebGLRenderer({ antialias: true });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- return renderer;
- }
- // 初始化场景的方法
- public initScene(): THREE.Scene {
- const scene = new THREE.Scene();
- return scene;
- }
- // 初始化渲染器的方法
- public initRenderer(): THREE.WebGLRenderer {
- const renderer = new THREE.WebGLRenderer({ antialias: true });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- return renderer;
- }
- // 初始化相机的方法
- public initCamera(): THREE.PerspectiveCamera {
- const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
-
- return camera;
- }
- //初始化轨道控制器
- public initOrbitControls() {
- const controls = new OrbitControls(this.camera, this.renderer.domElement);
-
- controls.update();
- }
- // 初始化性能监控的方法
- public initStats(container: HTMLDivElement) {
- const stats = new Stats();
- stats.dom.style.position = "absolute";
- stats.dom.style.left = "0";
- stats.dom.style.zIndex = "100";
- container.appendChild(stats.dom); // 将性能监控DOM元素添加到容器中
- return stats;
- }
- //初始化坐标系辅助
- public initAxisHelper(axesLength: number = 150, showText: boolean = true) {
- const helper = new THREE.AxesHelper(axesLength);
- if (showText) {
- const loader = new FontLoader();
- let meshX = new THREE.Mesh();
- let meshY = new THREE.Mesh();
- let meshZ = new THREE.Mesh();
- loader.load("fonts/optimer_regular.typeface.json", (font) => {
- meshX = this.createText("X", font);
- meshY = this.createText("Y", font);
- meshZ = this.createText("Z", font);
- meshX.position.x = 12;
- meshY.position.y = 12;
- meshZ.position.z = 12;
- this.scene.add(meshX);
- this.scene.add(meshY);
- this.scene.add(meshZ);
- });
- }
- this.scene.add(helper);
- }

- private createText(content: string, font: any) {
- const textGeometry = new TextGeometry(content, {
- font: font,
- size: 1,
- depth: 0.1,
- curveSegments: 1,
- });
- textGeometry.center();
- const textMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); // front
- const mesh = new THREE.Mesh(textGeometry, textMaterial);
- return mesh;
- }
- import * as THREE from "three"; // 引入Three.js库
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
-
- import Stats from "three/examples/jsm/libs/stats.module.js"; // 引入性能监控库
- import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
- import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
- export class ThreeTool {
- public camera: THREE.PerspectiveCamera; // 相机对象
- public scene: THREE.Scene; // 场景对象
- public renderer: THREE.WebGLRenderer; // 渲染器对象
-
- // 构造函数,初始化Three.js工具
- constructor() {
- this.renderer = this.initRenderer(); // 初始化渲染器
- this.scene = this.initScene(); // 初始化场景
- this.camera = this.initCamera(); // 初始化相机
- this.initOrbitControls();
- }
- public rendererContainer() {
- this.renderer.render(this.scene, this.camera); // 渲染场景和相机
- }
- // 初始化场景的方法
- public initScene(): THREE.Scene {
- const scene = new THREE.Scene();
- return scene;
- }
-
- // 初始化渲染器的方法
- public initRenderer(): THREE.WebGLRenderer {
- const renderer = new THREE.WebGLRenderer({ antialias: true });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- return renderer;
- }
-
- // 初始化相机的方法
- public initCamera(): THREE.PerspectiveCamera {
- const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
-
- return camera;
- }
- public initOrbitControls() {
- const controls = new OrbitControls(this.camera, this.renderer.domElement);
-
- controls.update();
- }
-
- // 初始化性能监控的方法
- public initStats(container: HTMLDivElement) {
- const stats = new Stats();
- stats.dom.style.position = "absolute";
- stats.dom.style.left = "0";
- stats.dom.style.zIndex = "100";
- container.appendChild(stats.dom); // 将性能监控DOM元素添加到容器中
- return stats;
- }
- public initAxisHelper(axesLength: number = 150, showText: boolean = true) {
- const helper = new THREE.AxesHelper(axesLength);
- if (showText) {
- const loader = new FontLoader();
- let meshX = new THREE.Mesh();
- let meshY = new THREE.Mesh();
- let meshZ = new THREE.Mesh();
- loader.load("fonts/optimer_regular.typeface.json", (font) => {
- meshX = this.createText("X", font);
- meshY = this.createText("Y", font);
- meshZ = this.createText("Z", font);
- meshX.position.x = 12;
- meshY.position.y = 12;
- meshZ.position.z = 12;
- this.scene.add(meshX);
- this.scene.add(meshY);
- this.scene.add(meshZ);
- });
- }
- this.scene.add(helper);
- }
- private createText(content: string, font: any) {
- const textGeometry = new TextGeometry(content, {
- font: font,
- size: 1,
- depth: 0.1,
- curveSegments: 1,
- });
- textGeometry.center();
- const textMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); // front
- const mesh = new THREE.Mesh(textGeometry, textMaterial);
- return mesh;
- }
- }

通过new ThreeTool()形式创建一个工具类的实例
- // 创建 ThreeTool 实例
- const instance = new ThreeTool();
通过示例的属性操作相机、场景
- import { useEffect, useRef } from "react";
- import * as THREE from "three";
- import { generateRobot, generateStarts } from "./generate";
- import { ThreeTool } from "../../BasicThree";
- import Stats from "three/examples/jsm/libs/stats.module.js";
-
- /**
- * 创建一个Three.js场景,包括相机和渲染器
- */
- function Robot() {
- // 创建一个div容器,用于存放渲染的Three.js场景
- const containerRef = useRef<HTMLDivElement>(null);
- const statsRef = useRef<Stats>(); // 创建用于引用统计信息的 ref
-
- // 创建 ThreeTool 实例
- const instance = new ThreeTool();
-
- // 初始化相机位置和朝向
- instance.camera.position.set(15, 12, 8);
- instance.camera.lookAt(0, 0, 0);
-
- // 添加坐标系
- instance.initAxisHelper();
-
- // 生成机器人和星星
- const robot = generateRobot();
- const robot2 = generateRobot();
- robot2.position.x = 6;
- robot2.position.z = 6;
- const starts = generateStarts(200);
-
- // 将物体添加到场景
- instance.scene.add(robot, robot2, starts);
-
- // 创建并设置方向光
- const straightLight = new THREE.DirectionalLight(0xffffff, 5);
- straightLight.position.set(20, 20, 20);
- instance.scene.add(straightLight);
- // 动画函数
- const animate = () => {
- requestAnimationFrame(animate);
- robot.rotation.z -= 0.005;
- robot2.rotation.y -= 0.005;
- starts.rotation.y -= 0.001;
- starts.rotation.z += 0.001;
- starts.rotation.x += 0.001;
- instance.renderer.render(instance.scene, instance.camera);
- statsRef.current && statsRef.current.update(); // 更新统计信息
- };
-
- // 监听组件挂载和卸载
- useEffect(() => {
- if (containerRef.current) {
- containerRef.current.appendChild(instance.renderer.domElement);
- instance.renderer.render(instance.scene, instance.camera);
- statsRef.current = instance.initStats(containerRef.current); // 初始化统计信息
- // 启动动画循环
- animate();
- }
- }, [containerRef]);
-
- // 返回div容器,用于存放渲染的Three.js场景
- return <div ref={containerRef} style={{ width: "100vw", height: "100vh" }}></div>;
- }
-
- // 导出Robot组件
- export default Robot;

- import * as THREE from "three";
- function createHead() {
- //SphereGeometry创建球形几何体
- const head = new THREE.SphereGeometry(4, 32, 16, 0, Math.PI * 2, 0, Math.PI * 0.5);
- const headMaterial = new THREE.MeshStandardMaterial({
- color: 0x43b988,
- roughness: 0.5,
- metalness: 1.0,
- });
- const headMesh = new THREE.Mesh(head, headMaterial);
- return headMesh;
- }
- //触角
- function generateHorn(y: number, z: number, angle: number) {
- //触角 CapsuleGeometry 创建胶囊形状的几何体。胶囊形状可以看作是一个圆柱体两端加上半球体
- const line = new THREE.CapsuleGeometry(0.1, 2);
- const lineMaterial = new THREE.MeshStandardMaterial({
- color: 0x43b988,
- roughness: 0.5,
- metalness: 1.0,
- });
- const lineMesh = new THREE.Mesh(line, lineMaterial);
- lineMesh.position.y = y;
- lineMesh.position.z = z;
- lineMesh.rotation.x = angle;
- return lineMesh;
- }
- //机器人眼睛
- function generateEye(x: number, y: number, z: number) {
- //SphereGeometry创建球形几何体
- const eye = new THREE.SphereGeometry(0.5, 32, 16, 0, Math.PI * 2, 0, Math.PI * 2);
- const eyeMaterial = new THREE.MeshStandardMaterial({
- color: 0x212121,
- roughness: 0.5,
- metalness: 1.0,
- });
- const eyeMesh = new THREE.Mesh(eye, eyeMaterial);
- eyeMesh.position.x = x;
- eyeMesh.position.y = y;
- eyeMesh.position.z = z;
- return eyeMesh;
- }
- //机器人身体
- export function generateBody() {
- //CylinderGeometry第一个参数是上部分圆的半径,第二个参数是下部分圆的半径,第三个参数是高度,材质使用的跟腿一样
- const body = new THREE.CylinderGeometry(4, 4, 6);
- const bodyMaterial = new THREE.MeshStandardMaterial({
- color: 0x43b988,
- roughness: 0.5,
- metalness: 1.0,
- });
- const bodyMesh = new THREE.Mesh(body, bodyMaterial);
- return bodyMesh;
- }
- //胳膊、腿
- function generateLegs(y: number, z: number) {
- const leg1 = new THREE.CapsuleGeometry(1, 4);
- const legMaterial1 = new THREE.MeshStandardMaterial({
- color: 0x43b988,
- roughness: 0.5,
- metalness: 1.0,
- });
- const leg1Mesh = new THREE.Mesh(leg1, legMaterial1);
- leg1Mesh.position.y = y;
- leg1Mesh.position.z = z;
- return leg1Mesh;
- }
- //创建机器人
- export function generateRobot() {
- // 创建一个Three.js对象,用于存放机器人
- const robot = new THREE.Object3D();
- const headMesh = createHead();
- headMesh.position.y = 6.5;
- robot.add(headMesh);
- //眼睛
- const leftEye = generateEye(3, 8, -2);
- const rightEye = generateEye(3, 8, 2);
- robot.add(leftEye);
- robot.add(rightEye);
- const leftHorn = generateHorn(11, -1, (-Math.PI * 30) / 180);
- const rightHorn = generateHorn(11, 1, (Math.PI * 30) / 180);
- robot.add(leftHorn);
- robot.add(rightHorn);
- const body = generateBody();
- body.position.y = 4;
- robot.add(body);
-
- // 生成机器人左腿
- robot.add(generateLegs(0, -2));
- // 生成机器人右腿
- robot.add(generateLegs(0, 2));
- //胳膊
- robot.add(generateLegs(3, 5));
-
- robot.add(generateLegs(3, -5));
- //物体缩放
- robot.scale.x = 0.3;
- robot.scale.y = 0.3;
- robot.scale.z = 0.3;
- return robot;
- }
- //创建粒子星星
- export function generateStarts(num: number) {
- //制作粒子特效
- const starts = new THREE.Object3D();
- const obj = new THREE.SphereGeometry(0.2, 3, 3);
- const material = new THREE.MeshStandardMaterial({
- color: 0x43b988,
- roughness: 0.5,
- metalness: 5,
- });
- const mesh = new THREE.Mesh(obj, material);
- for (let i = 0; i < num; i++) {
- const target = new THREE.Mesh();
- target.copy(mesh);
- target.position.x = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
- target.position.y = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
- target.position.z = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
- starts.add(target);
- }
- return starts;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。