• 基于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 );
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    通过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;
            }
          })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    大概得过程如下所示:

    在这里插入图片描述

    在生成得路径上,会有很多控制点,拖动控制点可以二次修改路径:

    在这里插入图片描述

    生成连接处

    两条路得连接处会有斑马线之类得,点击生成斑马线,可以通过算法自动计算斑马线,

    // 找到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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    如下图所示:

    在这里插入图片描述

    结语

    本文所示只是一个demo级别得尝试,如果要做一个强度得路面编辑器系统,可能要考虑得还有很多,比如多车道效果,更重得衔接形状等等。这在后续得产品中会持续强化相关功能。

    如果你有好的思路,也欢迎和我交流。关注公号“ITMan彪叔” 可以添加作者微信进行交流,及时收到更多有价值的文章。

  • 相关阅读:
    1036 Boys vs Girls
    【P15 Python基础】Pandas
    Unity中Shader的深度缓冲区
    来自阿里十余年的老架构师自述:成为架构师你只差了一步
    数据库迁移-国产化-Oracle迁移至GBase8a(操作符)
    kafka的请求处理机制
    LDAPWordlistHarvester:基于LDAP数据的字典生成工具
    【VMware虚拟机使用记录】—— 虚拟机开启失败的问题分析及解决方法
    用一个极致简单的场景演练领域建模
    传输大文件小工具:bypy
  • 原文地址:https://blog.csdn.net/netcy/article/details/126290278