Skip to content

QGIS 图纸到 glTF 模型的数据链路 ​

个人技术实验记录,全部产物自制。

三维场景最难的不是渲染,是几何从哪来。手绘一晚上也就十几栋,而一个场地几十上百栋。我的思路是把建模拆成"取数 → 清洗 → 参数化 → 导出"四段,中间全部脚本化,QGIS 只负责取数和检查。

一、取数:局部坐标系比经纬度好用 ​

场景是几百米尺度,用经纬度算距离、铺网格会一直被投影变形烦。我把原点定在场地一角,全程用局部平面坐标(米),只在最后接影像时才换算一次经纬度。

元数据里保留三样东西,后面所有脚本都依赖它们:

字段含义用途
origin局部坐标原点对应的经纬度接影像、接地理引擎时做一次换算
bbox_deg场地外包框(经纬度)描边、相机定位、瓦片取图范围
pixel_size_m影像像素地面分辨率配准误差判断、贴图细节估算

具体带号投影(比如 CGCS2000 的 3 度带)在 QGIS 工程里定义,导出时已经是本地米。注意:精确坐标属于敏感地理信息,公开发布时一律降精度或直接删掉,所以这篇里只写字段含义、不贴真实数值。

二、清洗:轮廓必须是四边形 ​

体块生成对轮廓的要求很朴素:四个角点、按绕向排好。真实图纸里常见的问题:

  • 多边形顶点冗余(一条直线上插了十几个点)→ 用道格拉斯-普克抽稀
  • 建筑被拆成多个碎块 → 按外接矩形合并
  • 绕向不一致 → 统一成逆时针,法向计算才不会翻面

抽稀阈值我按 0.5 m 试的,再大就会把小体量建筑削成三角形。

三、参数化:三个字段决定一栋楼 ​

每栋楼只保留最小信息集(下面是脱敏后的结构示例,坐标为场地内部局部米制,没有原点无法换算回实地):

json
{
  "id": "B01",
  "area_m2": 2192.5,
  "height_m": 10.15,
  "floors": 1,
  "aspect": 1.56,
  "gable": false,
  "points": [[-158.3, 78.05], [-168.03, 103.77], [-93.45, 131.96], [-83.73, 106.24]]
}
  • aspect(长宽比)用来判断是不是狭长体,狭长的更适合单坡
  • gable 决定要不要起屋脊
  • height_m 由层数 × 层高推,单层大跨度直接按净高给

屋顶起坡高度是常量(我用了墙高 8.8 m、屋脊抬 1.6 m 这一组),先全局统一,之后要精细化再按栋覆盖。先跑通再好看,这条管线一开始就想做细节肯定写不完。

四、导出:Blender 无头 + glTF ​

Blender 用命令行无头模式跑,脚本里 bmesh 直接建顶点面,比调用建模 UI 操作稳得多,而且能进版本控制:

bash
blender --background --python scripts/build_model_boxes.py

关键约定:

  1. 轴向:Blender X=东、Y=-北、Z=上;导出 glTF 后 X=东、Z=北。跨引擎时这一条不写死必然翻车。
  2. 贴图在 Blender 内部用 numpy 生成后 pack() 进 glb,不引外部素材文件,产物单文件可分发。
  3. 材质按法向分配:顶面(normal.z > 0.9)给屋顶材质,其余给墙面材质。
  4. UV 用盒式投影(按面法向选投影平面),世界坐标除以重复尺度,这样贴图密度和真实尺寸挂钩,不会每栋楼贴图大小不一。
py
def add_uv(obj, scale=6.0, up_scale=0.5):
    for poly in mesh.polygons:
        n = poly.normal
        if abs(n.z) > 0.7:      u, t = v.x / scale, v.y / (scale * up_scale)
        elif abs(n.x) > abs(n.y): u, t = v.y / scale, v.z / scale
        else:                    u, t = v.x / scale, v.z / scale

五、检查:中间产物一定要能画图看 ​

每一步都输出 PNG 预览(轮廓叠加、体块正交视图、配准对比)。没有中间可视化时,几何错误要等到渲染完才暴露,排查成本翻几倍。这也是为什么效果图页里全是半成品图——那些才是真正有用的调试产物。

六、链路小结 ​

矢量图纸 / 影像
   → QGIS 抽稀、取角点、局部坐标化
   → JSON(每栋一栋:轮廓 + 高度 + 坡向)
   → Blender 无头脚本(bmesh 建体块 + 程序化贴图 + 盒式 UV)
   → glTF(单文件,贴图内嵌)
   → 浏览器 Three.js 实时渲染

换场地时只需要重跑前两步,后面的脚本不动——这是我判断一条管线算不算"搭好了"的标准。

相关 ​

本站为个人技术展示网站,仅用于个人研发作品、3D 数字孪生技术学习展示,无任何经营性服务、无社区互动、不对外提供商业服务。

本站为个人技术展示网站,仅用于个人研发作品、3D 数字孪生技术学习展示,无任何经营性服务、无社区互动、不对外提供商业服务。

蜀ICP备2026068005号-1|个人 3D 数字孪生技术研发展示站