欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > three.js 空间坐标绘制多边形围栏(结合react)

three.js 空间坐标绘制多边形围栏(结合react)

2025/8/27 6:39:59 来源:https://blog.csdn.net/weixin_44242600/article/details/141030430  浏览:    关键词:three.js 空间坐标绘制多边形围栏(结合react)

空间坐标点绘制多边形,实际上可以理解为是由 “点” 到 “线” 到 “面” 的一个过程。将空间坐标点通过THREE.Shape绘制多条线并闭合而得到一个封闭的二维形状平面对象,使用THREE.ShapeGeometryShape对象转换为Geometry对象添加Mesh,最终得到我们想要的多边形几何体。要得到一个围栏就需要添加“墙体”,“墙体”则是通过THREE.BoxGeometry计算偏移角度绘制多个几何体得到。
在这里插入图片描述

使用技术简介

实现一个多边形围栏主要用到THREE.ShapeTHREE.ShapeGeometryTHREE.BoxGeometry

THREE.Shape

Shape是用于创建平面形状的类。Shape可以用来创建一个简单的二维形状,然后使用ShapeGeometry将其转换为可呈现的封闭形状。它可以和ExtrudeGeometryShapeGeometry一起使用,获取点,或者获取三角面。

常用属性

  • uuid: 该类所创建的实例的UUID。自动被指定的,因此它不应当被编辑、更改
  • holes: 表示形状内部的零或多个孔的数组。即表示包含所有内部空洞(也是Shape对象)的数组。默认值是一个空数组。
  • autoClose: 表示路径是否自动关闭的属性。默认false。

常用方法

  • moveTo: 将绘图点的起点移动到一个新的位置(x,y)并在Shape路径的路径中创建一个新的点。
  • lineTo:向Shape路径中添加一条直线,从当前点到新点(x,y)。

THREE.ShapeGeometry

ShapeGeometry是用于从Shape对象创建几何体的类。Shape 对象可以用来创建二维形状,这些形状可以用于创建网格(Mesh)或线框(Line)。

THREE.BoxGeometry

BoxGeometry是用于创建三维立方体的几何体。‌它允许用户指定立方体的宽度、‌高度、‌深度以及这三个方向上的分段数,‌通过这些参数可以灵活地调整立方体的尺寸和细节级别。‌BoxGeometry的构造函数通常包含以下参数:‌

  • width:‌立方体的宽度。‌
  • height:‌立方体的高度。‌
  • depth:‌立方体的深度。‌
  • widthSegments:‌在X轴方向上将立方体的面分成的段数。‌
  • heightSegments:‌在Y轴方向上将立方体的面分成的段数。‌
  • depthSegments:‌在Z轴方向上将立方体的面分成的段数。‌

注意:围栏是一个闭环,初始数据也是实现围栏的基础和关键,必须为一个闭环数据,即数组第一条数据和最后条数据相同。

点数据处理

在three.js中,长度总是从(0, 0, 0)到(x, y, z)的Euclidean distance(欧几里德距离,即直线距离),方向也是从(0, 0, 0)到(x, y, z)的方向。所以我们需要先转换一下。

const points = [[5, 0, 2],[8, 0, 2],[7, 0, 3],[6, 0, 3],[5, 0, 2]
];
const pointVector = [];
for (let i = 0; i < points.length; i++) {const item = points[i];pointVector.push(new THREE.Vector3(item[0], item[1], item[2]));
}

将点绘制成一个二维平面

将转换后的空间坐标,通过Shape moveTolineTo绘制多条线,多条线收尾相连,从而得到一个二维平面。

const shape = new THREE.Shape();
shape.moveTo(pointVector[0].x, pointVector[0].z);
for (let i = 1; i < pointVector.length; i++) {shape.lineTo(pointVector[i].x, pointVector[i].z);
}
shape.autoClose = true;

将二维平面转成多面几何体

Shape得到的二维平面转成Geometry并设置网格样式。

// 从一个或多个路径形状中创建一个单面多边形几何体。
const shapeGeometry = new THREE.ShapeGeometry(shape, 25);
const shapeMaterial = new THREE.MeshBasicMaterial({color: 0xFF0018,side: THREE.DoubleSide,transparent: true,opacity: 0.5
});
const shapeMesh = new THREE.Mesh(shapeGeometry, shapeMaterial);
shapeMesh.rotateX(Math.PI / 2);

绘制围栏

围栏可以理解为给一个房间添加“墙体”。通过BoxGeometry添加多个几何体来代实现多面墙,计算墙体依据多边形的边的偏移角度来实现,从而实现拼凑出围栏的效果。

// 墙体数据处理
const wallArr = [...pointArr];
for (let i = 0; i < wallArr.length; i++) {if (i !== wallArr.length - 1) {let params = {startX: wallArr[i].x,endX: wallArr[i + 1].x,startZ: wallArr[i].z,endZ: wallArr[i + 1].z};const wallHeight = 1; // 墙体高度,默认1// 计算墙体宽度const lens = Math.sqrt(Math.pow((Number(params.endZ) - Number(params.startZ)), 2) + Math.pow((Number(params.endX) - Number(params.startX)), 2));// 绘制网格模型,设置墙体样式const textureLoader = new THREE.TextureLoader();const texture = textureLoader.load(wall);texture.wrapS = THREE.RepeatWrapping; //水平方向如何包裹texture.wrapT = THREE.RepeatWrapping; // 垂直方向如何包裹// uv两个方向纹理重复数量、看板中重复数量texture.repeat.set(10, 1);// 设置偏移 纹理在单次重复时,从一开始将分别在U、V方向上偏移多少。 这个值的范围通常在0.0之间1.0texture.offset = new THREE.Vector2(0, 0);// 绘制墙体const box = new THREE.BoxGeometry(lens, wallHeight, 0); //- 墙体参数 墙体高度1const material = new THREE.MeshBasicMaterial({map: texture,transparent: true,opacity: 1});const mesh = new THREE.Mesh(box, material);// 设置单面墙体位置const posx = (params.endX + params.startX) / 2;const posz = (params.endZ + params.startZ) / 2;mesh.position.set(posx, points[0][1] + (wallHeight / 2), posz);// 设置墙体旋转角度const rotate = -Math.atan2((params.endZ - params.startZ), (params.endX - params.startX));mesh.rotation.y = rotate;// 将墙体添加到场景中scene.add(mesh);}
}

完整实例代码

注意:我这里的数据是一个闭环,如果你们的数据不是闭环需要处理一下,否者平面墙体都没有闭合就达不到围栏效果。

import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
const wall = require('@/static/image/wall.png');
let renderer, controls, scene, camera;// three.js绘制多边形围栏
const Draw3DHollowCylinder = () => {const box = useRef(); // canvas盒子// 渲染动画function renderFn() {requestAnimationFrame(renderFn);// 用相机渲染一个场景renderer.render(scene, camera);}/*** 绘制墙体* @param {墙体坐标点} params* @param {区域位于空间y轴位置} yHei* @param {几何图形组} group*/function drawPloygonWall(params, yHei, group) {const wallHeight = 1; // 墙体高度// 长度const lens = Math.sqrt(Math.pow((Number(params.endZ) - Number(params.startZ)), 2) + Math.pow((Number(params.endX) - Number(params.startX)), 2));// 绘制网格模型,设置墙体样式const textureLoader = new THREE.TextureLoader();const texture = textureLoader.load(wall);texture.wrapS = THREE.RepeatWrapping; //水平方向如何包裹texture.wrapT = THREE.RepeatWrapping; // 垂直方向如何包裹// uv两个方向纹理重复数量、看板中重复数量texture.repeat.set(10, 1);// 设置偏移 纹理在单次重复时,从一开始将分别在U、V方向上偏移多少。 这个值的范围通常在0.0之间1.0texture.offset = new THREE.Vector2(0, 0);// 设置墙体参数,深度为0const box = new THREE.BoxGeometry(lens, wallHeight, 0);const material = new THREE.MeshBasicMaterial({map: texture,transparent: true,opacity: 1});const mesh = new THREE.Mesh(box, material);// 设置单面墙体位置const posx = (params.endX + params.startX) / 2;const posz = (params.endZ + params.startZ) / 2;mesh.position.set(posx, yHei + (wallHeight / 2), posz);// 设置墙体旋转角度const rotate = -Math.atan2((params.endZ - params.startZ), (params.endX - params.startX));mesh.rotation.y = rotate;// 将墙体添加到组中group.add(mesh);}useEffect(() => {if (scene) {const points = [[5, 0, 2],[8, 0, 2],[7, 0, 3],[6, 0, 3],[5, 0, 2]];const pointArr = [];for (let i = 0; i < points.length; i++) {const item = points[i];pointArr.push(new THREE.Vector3(item[0], item[1], item[2]));}// 点绘制成线,再到二维平面const shape = new THREE.Shape();shape.moveTo(pointArr[0].x, pointArr[0].z);for (let i = 1; i < pointArr.length; i++) {shape.lineTo(pointArr[i].x, pointArr[i].z);}shape.autoClose = true; // 设置路径自动关闭// 从一个或多个路径形状中创建一个多边形几何体。const shapeGeometry = new THREE.ShapeGeometry(shape, 25);const shapeMaterial = new THREE.MeshBasicMaterial({color: 0xFF0018,side: THREE.DoubleSide,transparent: true,opacity: 0.5});const shapeMesh = new THREE.Mesh(shapeGeometry, shapeMaterial);shapeMesh.rotateX(Math.PI / 2);const group = new THREE.Group();// 墙体数据处理const wallArr = [...pointArr];for (let i = 0; i < wallArr.length; i++) {if (i !== wallArr.length - 1) {let params = {startX: wallArr[i].x,endX: wallArr[i + 1].x,startZ: wallArr[i].z,endZ: wallArr[i + 1].z};// 绘制墙体drawPloygonWall(params, points[0][1], group);}}group.add(shapeMesh);scene.add(group);}}, [scene]);// 初始化环境、灯光、相机、渲染器useEffect(() => {scene = new THREE.Scene();// 添加光源const ambitlight = new THREE.AmbientLight(0x404040);scene.add(ambitlight)const sunlight = new THREE.DirectionalLight(0xffffff);sunlight.position.set(-20, 1, 1);scene.add(sunlight);// 获取宽高设置相机和渲染区域大小const width = box.current.offsetWidth;const height = box.current.offsetHeight;const k = width / height;// 投影相机camera = new THREE.PerspectiveCamera(75, k, 0.1, 1000);camera.position.set(1, 0, 25);camera.lookAt(scene.position);// 创建一个webGL对象renderer = new THREE.WebGLRenderer({//增加下面两个属性,可以抗锯齿antialias: true,alpha: true});renderer.setSize(width, height); // 设置渲染区域尺寸renderer.setClearColor(0x000000, 1); // 设置颜色透明度// 首先渲染器开启阴影renderer.shadowMap.enabled = true;box.current.appendChild(renderer.domElement);// 监听鼠标事件controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;//设置为true则启用阻尼(惯性),默认falsecontrols.dampingFactor = 0.05;//值越小阻尼效果越强// 渲染renderFn();}, []);return <div className='ui_container_box'>three.js绘制3D多边形区域。<div style={{ width: '100%', height: '100%' }} ref={box}></div></div>;
}export default Draw3DHollowCylinder;

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词