基于webgl(threejs)的路面编辑
楔子
在很多应用中,特别是一些园区类的应用。 都需要对园区的地面 环境进行展示,路面就是地面的一部分。
通常的做法是,都是建模的时候把相关的元素都建好,然后导入到展示系统中进行展示。
不过有些情况下,可能建模并不太方便,所以三维编辑器可以直接进行简单的路面编辑显得挺有必要。
路面对象扩展
简单的路面希望能够通过一个路径来生成。 我们知道在threejs中有通过路径生成管路的对象,参考文章 WebGL管网展示(及TubeGeometry优化) 管路的横截面是一个圆形。 道路的横截面期望是一个矩形,因此,我们可以仿照管路的思路制作一个类似的对象PathRectGeometry,只是计算顶点的时候,横截面不再使用圆形,而是使用一个矩形,代码如下:
 let points = [new Vec3(-width/2,-height/2,0),new Vec3(-width/2,height/2,0),
        new Vec3(width/2,height/2,0),new Vec3(width/2,-height/2,0)]
			if(!scope.clockwise) {
				points = [new Vec3(-width/2,-height/2,0),new Vec3(width/2,-height/2,0),
					new Vec3(width/2,height/2,0),new Vec3(-width/2,height/2,0)];
			}
      for( let j = 0;j <= points.length;j ++) {
        let jj = j == points.length ? 0 : j;
        let point = points[jj];
        let radius = Math.hypot(point.x,point.y);
        const sin = point.y / radius;
        const cos = point.x / radius;
      	normal.x = ( cos * N.x + sin * B.x );
				normal.y = ( cos * N.y + sin * B.y );
				normal.z = ( cos * N.z + sin * B.z );
				normal.normalize();
				normals.push( 0,1,0 );
			// vertex
				vertex.x = P.x + radius * normal.x;
				vertex.y = P.y + radius * normal.y;
				vertex.z = P.z + radius * normal.z;
				vertices.push( vertex.x, vertex.y, vertex.z );
      }
通过PathRectGeometry创建对象的效果如下图所示:
路面编辑
通过在平面上面打点来构建直线和贝塞尔曲线,然后通过构建得线条了生成路径,通过路径就可以生成路面效果,
 graph.getView().addEventListener("click", (event) => {
        let now = new Date().getTime();
        if (t != 0 && now - t < 500) {
          return;
        }
        t = now;
        if (path) {
          let pos = graph.getPositionOnPlaneByEvent(event, plane);
          constraintsHorizontalOrVertical(path, pos);
          path.lineTo(pos.x, pos.y, pos.z);
          tempPath = path.clone(tempPath);
          tempRoad.geometry.path = tempPath;
        }
      })
大概得过程如下所示:
在生成得路径上,会有很多控制点,拖动控制点可以二次修改路径:
生成连接处
两条路得连接处会有斑马线之类得,点击生成斑马线,可以通过算法自动计算斑马线,
// 找到road1 到road2的joint
    function createJointShape(road1, road2) {
      let path = road1.geometry.path;
      let path2 = road2.geometry.path;
      let lastPoint = path.points.at(-1);
      let lastCurve = path.curves.at(-1);
      let curves = path2.curves;
      console.log(curves);
      let minCurve, minDist = Infinity,
        minPoint
      for (let i = 0; i < curves.length; i++) {
        let curve = curves[i];
        if (curve.type == "LineCurve3") {
          let {
            dist,
            point
          } = findClosestPoint(lastPoint, curve.v1, curve.v2);
          if (dist < minDist) {
            minDist = dist;
            minPoint = point;
            minCurve = curve;
          }
        }
      }
      console.log(minCurve, minDist, minPoint);
      let v1 = lastCurve.v1,
        v2 = lastCurve.v2;
      let tagent = new dt.Vec3().subVectors(v2, v1);
      let up = new dt.Vec3(0, 1, 0);
      let cross = new dt.Vec3().cross(up, tagent);
      cross.normalize();
      let halfRoadWidth = 50;
      cross.multiplyScalar(halfRoadWidth);
      let cross2 = cross.clone().multiplyScalar(3.0);
      let p1 = lastPoint.clone().add(cross),
        p2 = lastPoint.clone().sub(cross);
      let sub = new dt.Vec3().subVectors(minPoint, lastPoint);
      console.log(sub.length(), minDist, halfRoadWidth)
      sub.setLength(minDist - halfRoadWidth);
      let joinPoint = new dt.Vec3().addVectors(lastPoint, sub);
      let halfSub = sub.clone().multiplyScalar(0.75);
      let p3Center = p1.clone().add(halfSub);
      let p4Center = p2.clone().add(halfSub);
      let p3 = joinPoint.clone().add(cross2);
      let p4 = joinPoint.clone().sub(cross2)
      let newPath = new dt.ShapePath();
      newPath.moveTo(p2.x, p2.z);
      newPath.quadraticCurveTo(p4Center.x, p4Center.z, p4.x, p4.z);
      newPath.lineTo(p3.x, p3.z);
      newPath.quadraticCurveTo(p3Center.x, p3Center.z, p1.x, p1.z);
      // newPath.closePath();
      // let geo = new dt.PathTubeGeometry(newPath, 64, 2);
      // let tube = new dt.Mesh(geo);
      let shapePath = newPath;
      const simpleShapes = shapePath.toShapes(true);
      var texture = graph.loadTexture("./road/001.jpg", {
        wrapT: dt.RepeatWrapping,
        wrapS: dt.RepeatWrapping,
      });
      texture.repeat.set(1 / 100, 1 / 100);
      texture.anisotropy = 16;
      let m1 = new dt.BasicMaterial({
        //  flatShading:true,
        map: texture,
        // envMap:envMap,
        // reflectivity:0.4,
        color: 0xffffff,
        toneMapped: false,
      });
      var geometry = new dt.ExtrudeGeometry(simpleShapes, {
        depth: 1,
        bevelEnabled: false,
        vertical: true,
      });
      var mesh = new dt.Mesh(geometry, m1);
      window.graph.getDataManager().add(mesh);
      road1.add(mesh);
    }
如下图所示:
结语
本文所示只是一个demo级别得尝试,如果要做一个强度得路面编辑器系统,可能要考虑得还有很多,比如多车道效果,更重得衔接形状等等。这在后续得产品中会持续强化相关功能。
如果你有好的思路,也欢迎和我交流。关注公号“ITMan彪叔” 可以添加作者微信进行交流,及时收到更多有价值的文章。
基于webgl(threejs)的路面编辑的更多相关文章
- 基于WebGL/Threejs技术的BIM模型轻量化之图元合并
		伴随着互联网的发展,从桌面端走向Web端.移动端必然的趋势.互联网技术的兴起极大地改变了我们的娱乐.生活和生产方式.尤其是HTML5/WebGL技术的发展更是在各个行业内引起颠覆性的变化.随着WebG ... 
- canvas svg webgl threejs d3js 的区别
		canvas 绘制2D位图. Echarts是基于Canvas技术的可视化工具,底层封装了原生的JavaScript的绘图 API. canvas里面绘制的图形不能被引擎抓取,canvas中我们绘制图 ... 
- PixiJS - 基于 WebGL 的超快 HTML5 2D 渲染引擎
		Pixi.js 是一个开源的HTML5 2D 渲染引擎,使用 WebGL 实现,不支持的浏览器会自动降低到 Canvas 实现.PixiJS 的目标是提供一个快速且轻量级的2D库,并能兼容所有设备.此 ... 
- 基于WebGL的三维的物联网平台技术
		参加工作三年了,从一个搞调试的民工进阶为程序员,收获还是有那么一点的.慢慢讲一些. 去年在网上发现了https://hightopo.com/cn-index.html图扑软件的基于WebGL的三维j ... 
- WPF学习12:基于MVVM Light 制作图形编辑工具(3)
		本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ... 
- WPF学习11:基于MVVM Light 制作图形编辑工具(2)
		本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTarget ... 
- 用基于WebGL的BabylonJS来共享你的3D扫描模型
		转自:http://www.geekfan.net/6578/ 用基于WebGL的BabylonJS来共享你的3D扫描模型 杰克祥子 2014 年 2 月 26 日 0 条评论 标签:3D扫描 , B ... 
- 基于layui,Jquery 表格动态编辑 设置 编辑值为 int 或者 double 类型及默认值
		首先先推荐大家在看这篇笔记时,阅读过我写的这篇 Layui表格编辑[不依赖Layui的动态table加载] 阅读过上面那篇笔记之后呢,才能更好的理解我现在所要说的这个东西 接下来废话不多说,上代码. ... 
- 基于 webGL 的元素周期表 3D 交互展示
		前言 之前在网上看到别人写的有关元素周期表的文章,深深的勾起了一波回忆,记忆里初中时期背的“氢氦锂铍硼,碳氮氧氟氖,钠镁铝硅磷,硫氯氩钾钙”.“养(氧)龟(硅)铝铁盖(钙),哪(钠)家(钾)没(镁)青 ... 
随机推荐
- 【freertos】010-消息队列概念及其实现细节
			目录 前言 10.1 消息队列概念 10.2 消息队列的数据传输机制 10.3 消息队列的阻塞访问机制 10.4 消息队列使用场景 10.5 消息队列控制块 10.5.1 队列控制块源码 10.5.2 ... 
- Sec资产管理——SwebUI开源应用解决方案
			产品简介 Sweb Sec是一款资产管理类的开源解决方案,通过SwebUI平台开发,包含资产管理.耗材管理两种管理系统,由部门管理.区域管理.盘点.标签打印.出入库等核心功能组成. 免费获取方案 开源 ... 
- Java系列之运算符
			运算符 算术运算符:+ (加).-(减) .*(乘)./(除).%(模).++(自增) . --(自减) 赋值运算符:= 关系运算符:>.<.>= <= == != insta ... 
- MAUI模板项目闪退问题
			MAUI模板项目闪退问题 在MAUI最初发布的时候就曾创建过几个模板项目进行体验过,没遇到什么坑.由于最近需要开发针对餐饮行业的收银机(安卓系统)开发一款应用,这种收银机一般配置不咋滴,系统版本和性能 ... 
- tmux(Terminal MultipleXer)命令使用
			作用:命令行多窗口显示:命令行程序与本机脱离 1 安装tmux (1)redhat.centos系统 yum install tmux (2)ubuntu系统 apt-get install tmux ... 
- 重学ES系列之字符串方面的处理
			<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- Python实现12种概率分布(附代码)
			今天给大家带来的这篇文章是关于机器学习的,机器学习有其独特的数学基础,我们用微积分来处理变化无限小的函数,并计算它们的变化:我们使用线性代数来处理计算过程:我们还用概率论与统计学建模不确定性. 在这其 ... 
- Ubuntu14.04.6配置阿里源
			Ubuntu14.04.6配置阿里源 这两天上手 Ubuntu 系统,因为公司用的是 14.04.6 版本,所以有了一些踩坑记录. 起因是安装完系统我需要安装一个搜狗输入法,过程得安装 fcitx,需 ... 
- Collection集合和Collection集合常用功能
			Collection集合常用功能 方法: boolean add(E e); 向集合中添加元素 boolean remove(E e); 删除集合中的某个元素 void clear(); 清空集合所有 ... 
- jdk8 hashmap 链表resize 源码分析
			重点看这部分代码 for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { old ... 
