0.效果预览,没有加框线所以墙体有点丑,但是哈哈哈,保证是正常的,因为是同一个颜色,视觉上会有差异

效果查看:Three.js实现墙梁板柱的绘制_哔哩哔哩_bilibili

1.准备工作

你看到这篇文章的时候,默认你已经会基础的three.js了,至少,你得会简单的three.js的引入了,当然,不会也不要紧,跟着我的,复制粘贴下面我的代码也可以得到下面这个页面,会的可以跳过这部分直接看第二部分。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测试代码</title><style>* {padding: 0;margin: 0;}</style></head><body></body><script type="module">// 引入相关包import * as THREE from "./js/three.module.js";import { OrbitControls } from "./js/OrbitControls.js";// 初始化场景,相机,渲染器,控制器,光线let scene, camera, renderer, controls, light, stats;function init() {// 场景初始化scene = new THREE.Scene();// 初始化相机camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(100, 190, 300);// 初始化渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);// 设置渲染器初始颜色renderer.setClearColor(new THREE.Color("rgb(200, 200, 200)"));document.body.appendChild(renderer.domElement);// 初始化控制器controls = new OrbitControls(camera, renderer.domElement);// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;//动态阻尼系数 就是鼠标拖拽旋转灵敏度controls.dampingFactor = 0.25;//是否可以缩放controls.enableZoom = true;//是否自动旋转controls.autoRotate = false;controls.autoRotateSpeed = 0.5;//设置相机距离原点的最远距离controls.minDistance = 1;//设置相机距离原点的最远距离controls.maxDistance = 2000;//是否开启右键拖拽controls.enablePan = false;// 创建网格const gridHelper = new THREE.GridHelper(1000,100,"rgb(248, 248, 248)","rgb(248, 248, 248)",);scene.add(gridHelper);console.log(scene.children);}// 动画函数,每帧渲染function animate() {requestAnimationFrame(animate);renderer.render(scene, camera);}//窗口变动触发的函数function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}// 运行页面function run() {init();animate();window.onresize = onWindowResize();}run();</script>
</html>

粘贴上面的代码,运行后得到下面这个界面,注意你引用的文件路径

得到这个图片后,接下来我们就可以进行下一步了

二. 绘制

绘制会遇到下面几个问题

  1. 鼠标点击拿到对应的三维空间坐标,即二维转三维

  2. 鼠标点击时,实时的生成一条线进行指引

  3. 点击第二下时,生成三维墙体

  4. 生成墙体后,如果不退出绘制,继续绘制,点击右键退出此次绘制

2.1.现在来解决第一个问题,获取到三维点

 // 获取点function getCoord(event) {const container = document.querySelector("canvas");const adrees = container.getBoundingClientRect();const raycaster = new THREE.Raycaster();const pointer = new THREE.Vector2();pointer.x = ((event.clientX - adrees.left) / adrees.width) * 2 - 1;pointer.y = 1 - ((event.clientY - adrees.top) / adrees.height) * 2;// 创建平面const normal = new THREE.Vector3(0, 1, 0);const planeGround = new THREE.Plane(normal, 0);// 从相机发出一条射线经过鼠标点击的位置raycaster.setFromCamera(pointer, camera);// 拿到该射线const ray = raycaster.ray;// 计算相机到射线的对象,可能有多个对象,返回一个数组,按照相机距离远近排列const intersects = ray.intersectPlane(planeGround,new THREE.Vector3(0, 0, 0));// 返回向量return intersects;}

此时鼠标点击就可以拿到三维点了

2.2 现在来解决第二个问题,鼠标点击时生成实时的线段

    // 鼠标点击时拿到第一个坐标const canvas = document.querySelector("canvas");const pointsArr = []; // 存储画墙的坐标canvas.onmousedown = function (e) {// 获取到每次点击的坐标const p = getCoord(e);// 鼠标按下是左键时,拿到第一个点if (e.button === 0) {// 把坐标放到坐标数组中pointsArr.push([p]);// 如果有两个点,则生成线段和墙体if (pointsArr.length >= 2) {// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 将线段添加到场景中const currentArr = []currentArr.push(pointsArr[0][0])currentArr.push(pointsArr[1][0])lineGeometry.setFromPoints(currentArr)const line = new THREE.Line(lineGeometry, lineMaterial)scene.add(line)  }}// 鼠标移动时生成实时的线段canvas.onmousemove = function (e) {// 获取到第一个点的坐标const intersects = getCoord(e);// 鼠标左键未点击时线段的移动状态;if (scene.getObjectByName("line_move")) {scene.remove(scene.getObjectByName("line_move"));}// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 判断是否有初始点if (pointsArr.length > 0) {const currentArr = [];currentArr.push(pointsArr[0][0]);currentArr.push(new THREE.Vector3(intersects.x, 1, intersects.z));lineGeometry.setFromPoints(currentArr);const line = new THREE.Line(lineGeometry, lineMaterial);line.name = "line_move";scene.add(line);}};};

此时就得到实时的线了,需要注意的是,代码最好写在初始化函数的下边,不然有可能拿不到canvas这个节点,因为我直接插入到页面中的

2.3 接下来开始改造点击函数onmousedown,加上生成墙体的内容

    // 鼠标点击时拿到第一个坐标const canvas = document.querySelector("canvas");const pointsArr = []; // 存储画墙的坐标canvas.onmousedown = function (e) {// 获取到每次点击的坐标const p = getCoord(e);// 鼠标按下是左键时,拿到第一个点if (e.button === 0) {// 把坐标放到坐标数组中pointsArr.push([p]);// 如果有两个点,则生成线段和墙体if (pointsArr.length >= 2) {// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 将线段添加到场景中const currentArr = []currentArr.push(pointsArr[0][0])currentArr.push(pointsArr[1][0])lineGeometry.setFromPoints(currentArr)const line = new THREE.Line(lineGeometry, lineMaterial)scene.add(line)// 画墙开始// 和并曲线const start = pointsArr[0][0];const end = pointsArr[1][0];const curvePath = new THREE.CurvePath();const curv3 = new THREE.LineCurve3(new THREE.Vector3(start.x, start.y, start.z),new THREE.Vector3(end.x, end.y, end.z));curvePath.add(curv3);// 设置挤压参数,按路径挤压const extrudeSettings = {steps: 200,bevelEnabled: true,bevelThickness: 100,extrudePath: curvePath,};// 设置挤压面const pts = [];const deepness = 10; // 厚度const height = 80; // 高度pts.push(new THREE.Vector2(0, -0.5 * deepness));pts.push(new THREE.Vector2(-height, -0.5 * deepness));pts.push(new THREE.Vector2(-height, 0.5 * deepness));pts.push(new THREE.Vector2(0, 0.5 * deepness));// 生成挤压模型const shape = new THREE.Shape(pts);const geometry = new THREE.ExtrudeBufferGeometry(shape,extrudeSettings);const material2 = new THREE.MeshBasicMaterial({color: "green",wireframe: false,});const mesh = new THREE.Mesh(geometry, material2);// 将墙体添加到场景中scene.add(mesh);// 每当到达三个点的时候 把前面一个点给删除掉,保持两个点pointsArr.shift();}}// 鼠标移动时生成实时的线段canvas.onmousemove = function (e) {// 获取到第一个点的坐标const intersects = getCoord(e);// 鼠标左键未点击时线段的移动状态;if (scene.getObjectByName("line_move")) {scene.remove(scene.getObjectByName("line_move"));}// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 判断是否有初始点if (pointsArr.length > 0) {const currentArr = [];currentArr.push(pointsArr[0][0]);currentArr.push(new THREE.Vector3(intersects.x, 1, intersects.z));lineGeometry.setFromPoints(currentArr);const line = new THREE.Line(lineGeometry, lineMaterial);line.name = "line_move";scene.add(line);}};};

上面的代码是改造完成的代码,代码到这,你就可以愉快的画墙了,剩下的就是一些细节处理了

2.4 鼠标右键退出绘制,移除线条,继续改造onmousedown,最终代码如下

  canvas.onmousedown = function (e) {// 获取到每次点击的坐标const p = getCoord(e);// 鼠标按下是左键时,拿到第一个点if (e.button === 0) {// 把坐标放到坐标数组中pointsArr.push([p]);// 如果有两个点,则生成线段和墙体if (pointsArr.length >= 2) {// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 将线段添加到场景中const currentArr = []currentArr.push(pointsArr[0][0])currentArr.push(pointsArr[1][0])lineGeometry.setFromPoints(currentArr)const line = new THREE.Line(lineGeometry, lineMaterial)scene.add(line)// 画墙开始// 和并曲线const start = pointsArr[0][0];const end = pointsArr[1][0];const curvePath = new THREE.CurvePath();const curv3 = new THREE.LineCurve3(new THREE.Vector3(start.x, start.y, start.z),new THREE.Vector3(end.x, end.y, end.z));curvePath.add(curv3);// 设置挤压参数,按路径挤压const extrudeSettings = {steps: 200,bevelEnabled: true,bevelThickness: 100,extrudePath: curvePath,};// 设置挤压面const pts = [];const deepness = 10; // 厚度const height = 80; // 高度pts.push(new THREE.Vector2(0, -0.5 * deepness));pts.push(new THREE.Vector2(-height, -0.5 * deepness));pts.push(new THREE.Vector2(-height, 0.5 * deepness));pts.push(new THREE.Vector2(0, 0.5 * deepness));// 生成挤压模型const shape = new THREE.Shape(pts);const geometry = new THREE.ExtrudeBufferGeometry(shape,extrudeSettings);const material2 = new THREE.MeshBasicMaterial({color: "green",wireframe: false,});const mesh = new THREE.Mesh(geometry, material2);// 讲墙体添加到场景中scene.add(mesh);// 每当到达三个点的时候 把前面一个点给删除掉,保持两个点pointsArr.shift();}}// 鼠标右键点击退出绘制,并回到上一个点if (e.button === 2) {// 移除事件canvas.onmousemove = null// 移除线段// 删除数组中的元素,否则的话再次重绘会链接之前的点接着重绘pointsArr.shift();// 删除线段let length = scene.children.length - 1;const children = scene.children;// 按步骤移除线段if (scene.children[length].isLine) {// 只删除最后一条即可,用pop会弹出两delete scene.children[children.length];length = scene.children.length - 1;}}// 鼠标移动时生成实时的线段canvas.onmousemove = function (e) {// 获取到第一个点的坐标const intersects = getCoord(e);// 鼠标左键未点击时线段的移动状态;if (scene.getObjectByName("line_move")) {scene.remove(scene.getObjectByName("line_move"));}// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 判断是否有初始点if (pointsArr.length > 0) {const currentArr = [];currentArr.push(pointsArr[0][0]);currentArr.push(new THREE.Vector3(intersects.x, 1, intersects.z));lineGeometry.setFromPoints(currentArr);const line = new THREE.Line(lineGeometry, lineMaterial);line.name = "line_move";scene.add(line);}};};

到这里就结束了

三.  源码

所有的源代码,可直接复制粘贴使用,更改下文件的引用路径即可

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测试代码</title><style>* {padding: 0;margin: 0;}</style></head><body></body><script type="module">// 引入相关包import * as THREE from "./js/three.module.js";import { OrbitControls } from "./js/OrbitControls.js";// 初始化场景,相机,渲染器,控制器,光线let scene, camera, renderer, controls, light, stats;function init() {// 场景初始化scene = new THREE.Scene();// 初始化相机camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(100, 190, 300);// 初始化渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);// 设置渲染器初始颜色renderer.setClearColor(new THREE.Color("rgb(200, 200, 200)"));document.body.appendChild(renderer.domElement);// 初始化控制器controls = new OrbitControls(camera, renderer.domElement);// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;//动态阻尼系数 就是鼠标拖拽旋转灵敏度controls.dampingFactor = 0.25;//是否可以缩放controls.enableZoom = true;//是否自动旋转controls.autoRotate = false;controls.autoRotateSpeed = 0.5;//设置相机距离原点的最远距离controls.minDistance = 1;//设置相机距离原点的最远距离controls.maxDistance = 2000;//是否开启右键拖拽controls.enablePan = false;// 创建网格const gridHelper = new THREE.GridHelper(1000,100,"rgb(248, 248, 248)","rgb(248, 248, 248)");scene.add(gridHelper);console.log(scene.children);}// 动画函数,每帧渲染function animate() {requestAnimationFrame(animate);renderer.render(scene, camera);}//窗口变动触发的函数function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}// 运行页面function run() {init();animate();window.onresize = onWindowResize();}run();// 获取点function getCoord(event) {const container = document.querySelector("canvas");const adrees = container.getBoundingClientRect();const raycaster = new THREE.Raycaster();const pointer = new THREE.Vector2();pointer.x = ((event.clientX - adrees.left) / adrees.width) * 2 - 1;pointer.y = 1 - ((event.clientY - adrees.top) / adrees.height) * 2;// 创建平面const normal = new THREE.Vector3(0, 1, 0);const planeGround = new THREE.Plane(normal, 0);// 从相机发出一条射线经过鼠标点击的位置raycaster.setFromCamera(pointer, camera);// 拿到该射线const ray = raycaster.ray;// 计算相机到射线的对象,可能有多个对象,返回一个数组,按照相机距离远近排列const intersects = ray.intersectPlane(planeGround,new THREE.Vector3(0, 0, 0));// 返回向量return intersects;}// 生成辅助点function getPoint(intersects) {const pointLight = new THREE.PointLight(0xff0000, 100, 100);pointLight.position.set(intersects.x, intersects.y, intersects.z);scene.add(pointLight);const sphereSize = 3;const pointLightHelper = new THREE.PointLightHelper(pointLight,sphereSize);scene.add(pointLightHelper);}// 鼠标点击时拿到第一个坐标const canvas = document.querySelector("canvas");const pointsArr = []; // 存储画墙的坐标canvas.onmousedown = function (e) {// 获取到每次点击的坐标const p = getCoord(e);// 鼠标按下是左键时,拿到第一个点if (e.button === 0) {// 把坐标放到坐标数组中pointsArr.push([p]);// 如果有两个点,则生成线段和墙体if (pointsArr.length >= 2) {// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 将线段添加到场景中const currentArr = []currentArr.push(pointsArr[0][0])currentArr.push(pointsArr[1][0])lineGeometry.setFromPoints(currentArr)const line = new THREE.Line(lineGeometry, lineMaterial)scene.add(line)// 画墙开始// 和并曲线const start = pointsArr[0][0];const end = pointsArr[1][0];const curvePath = new THREE.CurvePath();const curv3 = new THREE.LineCurve3(new THREE.Vector3(start.x, start.y, start.z),new THREE.Vector3(end.x, end.y, end.z));curvePath.add(curv3);// 设置挤压参数,按路径挤压const extrudeSettings = {steps: 200,bevelEnabled: true,bevelThickness: 100,extrudePath: curvePath,};// 设置挤压面const pts = [];const deepness = 10; // 厚度const height = 80; // 高度pts.push(new THREE.Vector2(0, -0.5 * deepness));pts.push(new THREE.Vector2(-height, -0.5 * deepness));pts.push(new THREE.Vector2(-height, 0.5 * deepness));pts.push(new THREE.Vector2(0, 0.5 * deepness));// 生成挤压模型const shape = new THREE.Shape(pts);const geometry = new THREE.ExtrudeBufferGeometry(shape,extrudeSettings);const material2 = new THREE.MeshBasicMaterial({color: "green",wireframe: false,});const mesh = new THREE.Mesh(geometry, material2);// 讲墙体添加到场景中scene.add(mesh);// 每当到达三个点的时候 把前面一个点给删除掉,保持两个点pointsArr.shift();}}// 鼠标右键点击退出绘制,并回到上一个点if (e.button === 2) {// 移除事件canvas.onmousemove = null// 移除线段// 删除数组中的元素,否则的话再次重绘会链接之前的点接着重绘pointsArr.shift();// 删除线段let length = scene.children.length - 1;const children = scene.children;// 按步骤移除线段if (scene.children[length].isLine) {// 只删除最后一条即可,用pop会弹出两delete scene.children[children.length];length = scene.children.length - 1;}}// 鼠标移动时生成实时的线段canvas.onmousemove = function (e) {// 获取到第一个点的坐标const intersects = getCoord(e);// 鼠标左键未点击时线段的移动状态;if (scene.getObjectByName("line_move")) {scene.remove(scene.getObjectByName("line_move"));}// 创建线段const lineGeometry = new THREE.BufferGeometry();const lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00,});// 判断是否有初始点if (pointsArr.length > 0) {const currentArr = [];currentArr.push(pointsArr[0][0]);currentArr.push(new THREE.Vector3(intersects.x, 1, intersects.z));lineGeometry.setFromPoints(currentArr);const line = new THREE.Line(lineGeometry, lineMaterial);line.name = "line_move";scene.add(line);}};};</script>
</html>

都看到这里了,点个赞呗

three.js绘制墙体,通过不规则路径生成墙体,3D墙体绘制相关推荐

  1. Three.js通过不规则路径生成墙体

    Three.js通过不规则路径生成墙体 在一些3D场景的搭建中,经常会遇到图中通过墙体来分割内容的效果,目前Threejs提供的Geometry类型还无法直接处理一些不规则墙体的搭建 生成算法 通过B ...

  2. Three.js 绘图之不规则路径 3D 墙体生成算法

    HTML5 是当前最流行的 Web 前端开发技术,其中最大的改变即是 Canvas 对象在各大浏览器平台中变得通用,在 HTML5 流行之前在 Web 端显示三维图形有很多种技术,但各种技术之间存在很 ...

  3. Java 代码分享(第4篇),绘制迷宫2 绘制起点终点和路径

    开发环境: 操作系统Win10. 1.下载Java 15,提取码:soft 2.下载软件 Eclipse 2020-12,提取码:soft 3.生成迷宫第1版 下载本博客的实例工程代码,提取码:sof ...

  4. 3 移动机器人路径规划(1- 栅格地图绘制)

    1 栅格地图绘制 1.1 数据地图 1.1.1地图类型 1.1.2 栅格地图表示方式 1.2 位置的表示方法的区分 1.2.1 在栅格地图上绘制xy点发生的情况 1.2.2 三种表述位置方法的关系 1 ...

  5. js 获取上下文后面的路径_通过在数据后面显示上下文来可视化公众意见

    js 获取上下文后面的路径 In 1824, The Harrisburg Pennsylvanian, a newspaper from a town in Pennsylvania conduct ...

  6. letswave教程:脑电数据图形绘制、批处理以及脚本生成

    letswave教程:脑电数据图形绘制.批处理以及脚本生成 1 单主题图形生成 1.1 打开图形模块 1.2 创建子图 1.3 添加内容 1.4 设置轴参数 1.5 导出图形 2 多主题图形生成 2. ...

  7. SwiftUI之深入解析如何绘制徽章视图的路径和形状

    一.创建徽章视图 创建徽章前需要使用 SwiftUI 的矢量绘画 API 创建一个徽章视图: 选择文件 -> 新建 -> 文件,然后从 iOS 文件模板列表中选择 SwiftUI View ...

  8. Android中绘制简单几何图形和路径Path

    背景 我的博客:http://zhangsunyucong.top 马上就到2018年过年了,然后我又刚好有兴致,就来玩玩Android中的简单几何图形的绘制和使用Path类来绘制路径. Path和C ...

  9. 计算机图形设计论文 真实图形生成技术的发展,绘制技术论文,关于计算机图形图像绘制技术的现状应用相关参考文献资料-免费论文范文...

    导读:这是一篇与绘制技术论文范文相关的免费优秀学术论文范文资料,为你的论文写作提供参考. (四川建筑职业技术学院,德阳618000) (Sichuan College of Architectural ...

最新文章

  1. 不走寻常路 设计ASP.NET应用程序的七大绝招
  2. 数字图像处理:第十九章 立体视觉
  3. 高性能负载均衡设计入门篇
  4. ubuntu date -R查看时区
  5. PHP中error_reporting()用法详解
  6. oracle表字段获取,获取oracle表结构的字段信息
  7. memcache 知识点
  8. 开源软件的中年危机如何破解?
  9. 【观点】健康的心智是中国未来最大的生产力
  10. sip系统完整性是什么?SIP系统完整性保护关闭方法(含M1)
  11. 班级抽签小程序——项目总结
  12. 安川机器人报错_安川机器人报错代码:原点位置数据修改
  13. 二进制像素绘制程序 scratch编程三级
  14. Bloombox:iPhone陶瓷底座,还能当花盆和扩音器
  15. 2015中国大学排行榜100强新鲜出炉(校友会版)-[转]
  16. Android NDK——实战演练之使用Android Studio引用so库,jar包、module,aar以及导入Eclipse项目并使用JNI的正确姿势(一)
  17. html实现文件的下载
  18. Nvidia GPU虚拟化
  19. QGIS基础教程 (入门级)——下载安装、新建工程、加载数据
  20. PHP将ppt转成图片查看

热门文章

  1. 创客(米思奇编程)-03-传感器
  2. 在kile中使用Astyle插件使代码自动对齐
  3. 简历准备(一)—— TPLink
  4. PTA 单链表(流浪狗收养所)
  5. 错过了淘宝、拼多多,今年的电商风口抖音小店无货源你能抓住吗?
  6. DNSPod十问杨晓东:从.cn看中国互联网进化史
  7. iOS XibKits-- Label内边距设置
  8. chatgpt的150个指令大全
  9. 如何调教ChatGpt 让它听话
  10. 阿里云服务器ECS的6大功能组件