Three.js程序化3D城市建模【OpenStreetMap】

对于我在 Howest 的研究项目,我决定构建一个 3D 版本的 Lucas Bebber 的“交互式讲故事的动画地图路径”项目。

我将使用 OSM 中的矢量轮廓来挤出建筑物的形状并将它们添加到 3js 场景中,随后我将对其进行动画处理

在这里插入图片描述

推荐:用 NSDT编辑器 快速搭建可编程3D场景

1、开发环境设置

为了使用 Node 和 npm 包,我选择使用 Vite.js。 Vite 是一款构建工具,旨在为现代 Web 项目提供更快、更精简的开发体验。 它由两个主要部分组成:

  • 开发服务器提供比本机ES 模块丰富的功能增强,例如极快的热模块替换(HMR)。
  • 将代码与Rollup 捆绑在一起的构建命令,预先配置为输出高度优化的静态资源以用于生产。

选择 Vite 是因为我过去在一些 Vue.js 项目中使用过它,因此对它很熟悉,事实证明它快速且可靠。

鉴于其受欢迎程度,Three.js 被选为该项目的首选框架,因为这种受欢迎程度催生了大量的文档和教程。

因为我希望以后能够将这个研究项目集成到我自己的网站中,所以我决定将其开发为 NPM 包。 这涉及到制作两个独立的项目——第一个用于实际的 3D 应用程序,另一个用于实现该应用程序的测试网站。

在项目文件夹中,npm init 命令用于创建 package.json 文件,其中包含包的元数据,例如名称、版本、依赖项、入口点和其他信息。 Index.js 将作为包的入口点,其中 src 文件夹包含代码,示例文件夹包含默认资源。

计划是将功能拆分为单独的 JavaScript 模块,以提高清晰度和可维护性,最终被初始化、全局、城市、动画和路径。

2、初始化模块

从初始化模块开始。 initialize() 函数创建并配置场景、相机、灯光和渲染器对象,从 DOM 中选择画布元素,然后将渲染器附加到其上。 此外,它还用于启用或禁用调试信息,例如 FPS 计数器和 Axis 可视化工具。

然后,该模块用于初始化 MapControls 并创建动画循环,但更多内容请参见交互性部分。

这个 npm 包被称为 Storymap

3、创建管理页面

为了测试新创建的npm包是否工作,使用了npm create vite@latest。

初始化一个单独的演示项目,该项目将充当包的使用者。

为了在该项目中安装本地包,使用 npm link 在演示的 node_modules 中创建了一个指向 Storymap 的符号链接。

该项目将用于创建和测试“管理”面板,网络开发人员可以在其中为新的或更新的游览创建路径。

在index.html文件中创建canvas元素并将其添加到storymap的配置中后,可以通过在终端中输入npm run dev并在浏览器中转到localhost:3000来开始测试,我们在其中看到一个空的 Three.js 场景。

由于storymap包是符号链接的,我们对其所做的更改将自动传播到演示项目,因此可以保持演示项目运行,并不断编写代码和测试更改,从而实现高效的开发流程。

4、OSM数据

我最初计划使用 OSM Buildings 来获取数据,但是我发现他们的文档已经过时,我决定切换到 Overpass Turbo,这样我就可以更快地开始开发并弄清楚如何使用 OSMB。 OSMB 使用 GeoJSON,Overpass Turbo 允许将其数据导出到JSON格式。

GeoJSON 文件包含地图的各种元素,例如由各种坐标和元数据数组表示的道路、公园、建筑物。建筑物具有轮廓、孔洞、剖面、高度或水平面以及许多其他属性。
在这里插入图片描述

建筑物及其元数据的示例模型

JSON 是一个不错的选择,因为它在网络上的流行意味着对解析它的本机支持。 弄清楚如何使用 OverPass API 需要一些时间,因为文档没有明确说明如何构建其查询语言,并且使用它仍然具有挑战性。 你可以在下面看到最终的查询。

[out:json]
[bbox:{{bbox}}]
[timeout:30];(
way["building"]({{bbox}});
relation["building"]["type"="multipolygon"]({{bbox}});way["highway"]({{bbox}});
relation["highway"]["type"="polygon"]({{bbox}});way["natural"="water"]({{bbox}});
way["water"="lake"]({{bbox}});
way["natural"="coastline"]({{bbox}});
way["waterway"="riverbank"]({{bbox}});way["leisure"="park"]({{bbox}});
way["leisure"="garden"]({{bbox}});
);out;
>;
out qt;

5、OSM建筑渲染

建筑物的生成被分成城市模块。 它解析 GeoJson 文件来查找建筑物。 建筑物被表示为形成建筑物轮廓的多边形的纬度和经度坐标数组,最终更多的多边形表示该形状中的孔,就像一个内部花园或悬挑。

第一个重大挑战是由 JavaScript 本身以及 Three.js 中的坐标系引起的。 场景的中心由 0,0,0 表示,离它越远,就越不准确和不稳定,这主要是由于 JavaScript 较差的浮点精度造成的。

GeoJSON坐标是全球经纬度坐标,它们是大浮点数,差异很小,这加剧了精度问题。

因此,有必要将全局坐标标准化为局部空间。 占据该区域的中心,使用了一个名为 geolib 的第三方库

计算从中心到每个点的距离,导致坐标标准化为原点,具有更大的标准偏差。
在这里插入图片描述

显示相对于原点的坐标的网格

有了这些标准化数据,我就可以创建 Three.js 形状和几何图形。 使用 Three.js 的内置功能,我能够轻松地从轮廓上“打出”孔。 然后使用该几何体与材质一起创建网格,然后将其添加到场景中。

由于three.jsXYZ 方向,我还必须分别在 X 轴和 Z 轴上将其旋转 90 度和 180 度。 使用这种方法,我现在有了一个非常平坦的城市的第一个版本。 为了使其成为 3D,我使用了级别属性,该属性表示楼层数乘以任意值来挤出这些形状。

在这里插入图片描述

浏览器中的城市!

6、表演与互动

现在城市已经生成了,一个问题出现了——性能很糟糕,FPS 下降到个位数。 解决方案是将所有建筑物合并到一个大网格中,而不是为每个建筑物生成单独的对象。 这种方法会产生副作用,所有建筑物都会具有相同的材料,但由于性能的大幅提高,这是值得的。 此修复适用于后来生成的所有几何图形,例如道路、绿地和水道

Web 开发人员需要能够加载和浏览整个区域并为游览路径设置路点。 这可以通过使用 three.js 的地图控件来实现。它是 3js 轨道控件的一个子集,其工作方式与 Google 地图和其他流行的数字地图软件类似,允许使用右键单击使相机围绕页面中心旋转,并使用左键单击拖动相机。 它们在初始化模块中导入并初始化。 为了让它们工作,它们需要更新每一帧,因此它们被添加到动画循环中。

为了创建路径的路点,你需要选择道路,以及将 2D 鼠标坐标转换为 3D 空间的方法。 对于后者,光线投射器可用于从鼠标位置向 3D 场景投射光线,并查看与它相交的内容。 从那里,你可以将其过滤为只能选择道路。

对于前者,类似的技术也用于建筑物。 使用 Overpass Turbo 而不是 OSM Buildings 结果因祸得福,因为 OSM 的 API 只提供建筑物的 GeoJSON,所以我需要找到并实现一个额外的 API 来获取道路数据。

立交桥 API查询被调整为包括道路,它们的坐标被标准化,它们被生成并添加到城市模块中的场景中。 由于这座城市看起来单调乏味,OverPass 还被用来获取绿地和水道的数据。
在这里插入图片描述

正在绘制的路径

路径模块用于实现 Raycaster 和其他必要的控件来绘制和导出路径。Three.js 内置了 Raycaster,因此很容易实现。 它链接到一个 OnMouseMove 事件侦听器,用于连续投射光线,以显示是否用户是否选择了道路。

鼠标左键单击用于设置航路点,具有各种键盘按钮负责保存、重置、撤消和重做功能。

路径导出到 JSON 数组中。 一旦开发者开始创建一条路径,我需要让它跟随鼠标。 因此,在单击之前,路径的最后一个点链接到鼠标位置,也使用 OnMouseMove 事件侦听器。

7、客户端

为了测试客户端实现,我创建了一个新项目,并以与管理项目类似的方式对其进行配置,使用 vite 引导项目并使用 npm 链接安装 Storymap 包。 唯一的变化是,这次场景不再占据整个页面,而是占据了一半,另一半用于显示有关兴趣点的信息。

对于客户端来说,他们对交互的唯一控制是在浏览器中滚动。 他们不应该能够直接与 Three.js 场景交互,就像用户无法与 Lucas 演示中的画布交互一样。

简而言之,当客户端在浏览器中向下滚动时,路径应该出现并从起始位置动画出来,并在用户滚动到底部并且相机跟随时完成。

这是该项目最困难的部分之一。

在这里插入图片描述

显示路径计算方式的图表

总而言之,路径被绘制为具有多个点的一个 Line 对象。 当用户向下滚动时,最后一个点会不断更新,使其看起来有动画效果。

为了获取该点的坐标,将 getScrollPercentage() 函数附加到滚动事件侦听器并用于计算浏览器滚动位置。然后使用该百分比来连续操作选择哪对点作为起点和终点,并使用 lerpVectors 函数在两者之间进行插值并计算当前路径的最后一个点应该是什么。

让我们通过一个例子来清楚地说明这一点。 让我们选择一条有 11 个单独点的路径。 第一个是从头开始绘制的,因此您必须将总页面高度(以百分比表示)除以 10。

  • 这意味着我们每滚动 10%,就会通过一个点。 全局滚动百分比确定我们需要在哪对点之间进行插值。
  • 如果用户滚动到 26.53%,我们需要使用插值法计算第 3 点和第 4 点之间 65.3% 处的点的坐标。
  • 该坐标用于更新Path的最后位置。 每次用户使用时都会发生这种情况卷轴,给人一种路径缩小或增长的错觉。
  • 相机位置偏移到路径一侧的后面,最后一条路径位置作为目标,随路径一起移动。

在这里插入图片描述

项目结构

研究的最终结果由 3 个不同的项目组成:负责生成和解析地图数据的 Storymap npm 包、允许在地图上创建路径的管理项目以及客户端。

该项目使所创建的穿过城市的路径充满活力。
在这里插入图片描述

可以点击这里查看在线演示,源码可从GitHub获取。


原文链接:OSM+three.js打造3D城市 — BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/44286.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++坦克大战源代码

源码: #include <iostream> #include <time.h> #include <windows.h>#define W 1 //上 #define S 2 //下 #define A 3 //左 #define D 4 //右 #define L 5 // 坦克有4条命void HideCursor() { //隐藏光标 …

【会议征稿信息】第二届信息学,网络与计算技术国际学术会议(ICINC2023)

2023年第二届信息学&#xff0c;网络与计算技术国际学术会议(ICINC2023) 2023 2nd International Conference on Informatics,Networking and Computing (ICINC 2023) 2023年第二届信息学&#xff0c;网络与计算技术国际学术会议(ICINC2023)将于2023年10月27-29日于中国武汉召…

首起针对国内金融企业的开源组件投毒攻击事件

简述 2023年8月9日&#xff0c;墨菲监控到用户名为 snugglejack_org (邮件地址&#xff1a;SnuggleBearrxxhotmail.com&#xff09;的用户发布到 NPM 仓库中的 ws-paso-jssdk 组件包具有发向 https://ql.rustdesk[.]net 的可疑流量&#xff0c;经过确认该组件包携带远控脚本&a…

.NET Core6.0使用NPOI导入导出Excel

一、使用NPOI导出Excel //引入NPOI包 HTML <input type"button" class"layui-btn layui-btn-blue2 layui-btn-sm" id"ExportExcel" onclick"ExportExcel()" value"导出" />JS //导出Excelfunction ExportExcel() {…

aardio开发语言Excel数据表读取修改保存实例练习

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right759;bottom479) winform.add( buttonEnd{cls"button";text"末页";left572;top442;right643;bottom473;z6}; buttonExcelRead{cls"button";text"读取Exce…

Qt实现简单的漫游器

文章目录 Qt的OpenGL窗口GLSL的实现摄像机类的实现简单的漫游器 Qt的OpenGL窗口 Qt主要是使用QOpenGLWidget来实现opengl的功能。  QOpenGLWidget 提供了三个便捷的虚函数&#xff0c;可以重载&#xff0c;用来重新实现典型的OpenGL任务&#xff1a; paintGL&#xff1a;渲染…

【数据库系统】--【5】DBMS查询处理

DBMS查询处理 01查询处理概述02查询编译词法、语法分析语义分析查询重写查询优化 03查询执行算法04查询执行模型 01查询处理概述 02查询编译 词法、语法分析 语义分析 查询重写 查询优化 03查询执行算法 04查询执行模型 小结 ● 查询处理概述 ● 查询编译 词法、语法分析语义分…

2021年06月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;数对 给定2到15个不同的正整数&#xff0c;你的任务是计算这些数里面有多少个数对满足&#xff1a;数对中一个数是另一个数的两倍。 比如给定1 4 3 2 9 7 18 22&#xff0c;得到的答案是3&#xff0c;因为2是1的两倍&#xff0c;4是2个两倍&#xff0c;18是9的…

CNN卷积详解(三)

一、卷积层的计算 4 ∗ * ∗ 4的输入矩阵 I I I 和 3 ∗ * ∗ 3 的卷积核 K K K: 在步长&#xff08;stride&#xff09;为 1 时&#xff0c;输出的大小为 ( 4 − 3 1 ) ( 4 − 3 1) 计算公式&#xff1a; ● 输入图片矩阵 I I I 大小&#xff1a; w w w w ww ●…

微服务系列文章之 SpringBoot 最佳实践

Spring Boot 是一种广泛使用且非常流行的企业级高性能框架。 以下是一些最佳实践和一些技巧&#xff0c;我们可以使用它们来改进 Spring Boot 应用程序并使其更加高效。 Spring Boot 的四大核心 1、自动配置 针对很多Spring应用程序和常见的应用功能&#xff0c;Spring Boo…

攻防世界-fileclude

原题 解题思路 直接展示源码了&#xff0c;flag.php应该存放了flag&#xff0c;在file1与file2都不为空且file2是“hello ctf”时file1将被导入。接下来做法很明显&#xff0c;让file为flag.php&#xff0c;file2为“hello ctf”。“?file1php://filter/readconvert.base64-en…

Open3D 最小二乘拟合空间直线(方法一)

目录 一、算法原理1、空间直线2、最小二乘法拟合二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、空间直线 x −

基于web的停车场收费管理系统/基于springboot的停车场管理系统

摘 要 随着汽车工业的迅猛发展&#xff0c;我国汽车拥有量急剧增加。停车场作为交通设施的组成部分,随着交通运输的繁忙和不断发展&#xff0c;人们对其管理的要求也不断提高&#xff0c;都希望管理能够达到方便、快捷以及安全的效果。停车场的规模各不相同,对其进行管理的模…

ElasticSearch 数据聚合、自动补全(自定义分词器)、数据同步

文章目录 数据聚合一、聚合的种类二、DSL实现聚合1、Bucket&#xff08;桶&#xff09;聚合2、Metrics&#xff08;度量&#xff09;聚合 三、RestAPI实现聚合 自动补全一、拼音分词器二、自定义分词器三、自动补全查询四、实现搜索款自动补全&#xff08;例酒店信息&#xff0…

【rust/egui】(三)看看template的app.rs:序列化、持久化存储

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 serde app.rs中首先定义了我们的Templ…

Git判断本地是否最新

场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作&#xff0c;然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…

域名解析和代理

购买域名 这里使用腾讯云进行购买。 对域名进行解析 通过添加记录接口对域名进行解析。 此时我们的服务器地址就被解析到域名上了。 我们可以通过以下格式进行访问&#xff1a; [域名]:[对应的项目端口] 效果为下: 通过nginx进行代理 如果我们使用上述的方式进行访问还是…

【hive】hive分桶表的学习

hive分桶表的学习 前言&#xff1a; 每一个表或者分区&#xff0c;hive都可以进一步组织成桶&#xff0c;桶是更细粒度的数据划分&#xff0c;他本质不会改变表或分区的目录组织方式&#xff0c;他会改变数据在文件中的分布方式。 分桶规则&#xff1a; 对分桶字段值进行哈…

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等 一、react-redux介绍二、React-Redux-基本使用三、获取状态useSelector四、分发动作useDispatch五、 Redux 数据流六、代码结构七、ActionType的使用八、Reducer的分离与合并九、购物挣…

计算机视觉的应用11-基于pytorch框架的卷积神经网络与注意力机制对街道房屋号码的识别应用

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用11-基于pytorch框架的卷积神经网络与注意力机制对街道房屋号码的识别应用&#xff0c;本文我们借助PyTorch&#xff0c;快速构建和训练卷积神经网络&#xff08;CNN&#xff09;等模型&#xff0c;…