1. 为什么要在Blender中使用天地图?
天地图作为国内权威的地理信息公共服务平台,为三维建模提供了丰富的地理数据支持。在Blender 5.0.1中配置天地图,可以让你直接在三维场景中调用真实的地理坐标数据,这对于建筑可视化、城市规划、影视特效等领域的创作者来说是个重大利好。
我最近在做一个城市重建项目时,发现手动创建地形和建筑布局耗时费力。通过将天地图接入Blender,不仅节省了90%的基础建模时间,还能确保场景地理位置的准确性。比如在做南京长江大桥周边环境建模时,天地图的卫星影像和矢量数据让我能精确还原周边建筑布局和地形起伏。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 配置前的准备工作
2.1 获取天地图API密钥
要使用天地图服务,首先需要申请开发者密钥。访问天地图官网的开发者平台,完成个人或企业认证后,可以免费获取基础服务的API调用权限。这里有个小技巧:申请时选择"Web服务API"而非"JavaScript API",因为Blender需要通过Python脚本来调用服务。
注意:天地图API有每日调用限额,个人开发者通常是1000次/天。大型项目建议提前规划数据缓存策略。
2.2 安装必备的Python库
Blender 5.0.1内置的Python环境需要以下扩展库:
- requests:用于HTTP请求
- numpy:处理地理坐标数据
- pillow:处理图像数据
在Blender的Python控制台中运行以下命令安装:
python复制import subprocess
subprocess.call([bpy.app.binary_path_python, "-m", "pip", "install", "requests", "numpy", "pillow"])
2.3 坐标系转换准备
天地图使用WGS84坐标系(EPSG:4326),而Blender使用右手坐标系。我们需要一个转换函数来处理这个差异:
python复制import math
def wgs84_to_blender(lon, lat, alt=0):
# 简化版墨卡托投影转换
x = (lon - 120) * 10000 # 经度偏移和缩放
y = (lat - 30) * 10000 # 纬度偏移和缩放
return (x, alt, y) # 注意Blender中Y轴是垂直方向
3. 在Blender中配置天地图服务
3.1 创建自定义脚本
在Blender中新建一个文本编辑器窗口,创建名为"tianditu_loader.py"的脚本:
python复制import bpy
import requests
import numpy as np
from PIL import Image
import io
TIANDITU_KEY = "你的API密钥" # 替换为实际密钥
def load_tianditu_image(x, y, z, layer='img'):
"""加载天地图瓦片
layer可选: img(影像), vec(矢量), cva(矢量注记), cia(影像注记)
"""
url = f"http://t{s%4}.tianditu.gov.cn/{layer}_w/wmts?tk={TIANDITU_KEY}"
url += f"&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0"
url += f"&LAYER={layer}&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles"
url += f"&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}"
try:
response = requests.get(url, timeout=10)
img = Image.open(io.BytesIO(response.content))
return np.array(img)
except Exception as e:
print(f"加载天地图失败: {e}")
return None
3.2 创建地理参考平面
- 在3D视图中按Shift+A添加平面
- 进入编辑模式(Tab),细分平面20次
- 添加置换修改器,新建一个空纹理
- 在纹理属性中设置类型为"Image or Movie"
- 使用Python脚本动态加载天地图高程数据:
python复制def add_terrain(lon, lat, size=1000):
# 创建地形网格
bpy.ops.mesh.primitive_grid_add(x_subdivisions=50, y_subdivisions=50, size=size)
terrain = bpy.context.object
# 设置材质
mat = bpy.data.materials.new(name="TerrainMaterial")
terrain.data.materials.append(mat)
# 加载天地图作为纹理
img_data = load_tianditu_image(lon, lat, 15, 'img')
if img_data is not None:
img = bpy.data.images.new("TiandituTexture", width=img_data.shape[1], height=img_data.shape[0])
img.pixels = img_data.astype(np.float32).flatten() / 255.0
img.pack()
# 创建纹理节点
mat.use_nodes = True
nodes = mat.node_tree.nodes
tex_node = nodes.new('ShaderNodeTexImage')
tex_node.image = img
bsdf = nodes.get('Principled BSDF')
mat.node_tree.links.new(tex_node.outputs['Color'], bsdf.inputs['Base Color'])
return terrain
4. 实战:创建基于天地图的3D场景
4.1 加载特定区域地图
假设我们要创建北京故宫周边的3D场景:
python复制# 故宫中心坐标 (116.391, 39.916)
beijing_lon, beijing_lat = 116.391, 39.916
# 创建1km×1km的地形
terrain = add_terrain(beijing_lon, beijing_lat, size=1000)
# 设置相机视角
camera = bpy.data.objects['Camera']
camera.location = wgs84_to_blender(beijing_lon, beijing_lat, 500)
camera.rotation_euler = (math.radians(60), 0, math.radians(-90))
4.2 建筑物轮廓提取
天地图的矢量数据可以用于生成建筑物基础模型:
python复制def generate_buildings(lon, lat, radius=0.01):
"""根据矢量数据生成建筑物"""
# 这里简化处理,实际应调用天地图POI接口
buildings = []
for i in range(5):
b_lon = lon + (random.random() - 0.5) * radius
b_lat = lat + (random.random() - 0.5) * radius
height = random.randint(20, 100)
# 创建简单建筑模型
bpy.ops.mesh.primitive_cube_add(size=10, location=wgs84_to_blender(b_lon, b_lat, height/2))
building = bpy.context.object
building.dimensions = (10, 10, height)
buildings.append(building)
return buildings
generate_buildings(beijing_lon, beijing_lat)
5. 性能优化与常见问题
5.1 数据缓存策略
频繁请求天地图服务会导致性能下降,建议实现本地缓存:
python复制import os
import hashlib
CACHE_DIR = os.path.expanduser("~/.blender_tianditu_cache")
def get_tile_cache_path(x, y, z, layer):
key = f"{layer}_{x}_{y}_{z}"
return os.path.join(CACHE_DIR, hashlib.md5(key.encode()).hexdigest() + ".png")
def load_tile_with_cache(x, y, z, layer):
cache_path = get_tile_cache_path(x, y, z, layer)
if os.path.exists(cache_path):
return Image.open(cache_path)
img_data = load_tianditu_image(x, y, z, layer)
if img_data:
os.makedirs(CACHE_DIR, exist_ok=True)
Image.fromarray(img_data).save(cache_path)
return img_data
5.2 常见错误排查
-
黑屏或粉红贴图:
- 检查API密钥是否有效
- 确认网络连接正常
- 验证坐标参数是否在有效范围内
-
坐标偏移问题:
- 确保使用正确的坐标转换公式
- 检查Blender场景的单位设置(建议使用米制单位)
-
性能卡顿:
- 减少单次加载的瓦片数量
- 使用较低精度的地图层级(z值)
- 启用缓存机制减少重复请求
我在实际项目中发现,当加载超过100个瓦片时,Blender的视口性能会明显下降。解决方案是分批加载,或者使用LOD(细节层次)技术,根据相机距离动态调整地图精度。
6. 进阶应用技巧
6.1 结合GIS数据增强场景
将天地图与其他GIS数据源结合使用:
python复制def load_geojson_to_blender(geojson_path):
"""导入GeoJSON数据到Blender"""
import json
with open(geojson_path) as f:
data = json.load(f)
for feature in data['features']:
if feature['geometry']['type'] == 'LineString':
coords = feature['geometry']['coordinates']
# 创建曲线对象
curve = bpy.data.curves.new('Road', type='CURVE')
curve.dimensions = '3D'
polyline = curve.splines.new('POLY')
polyline.points.add(len(coords)-1)
for i, (lon, lat) in enumerate(coords):
x, _, z = wgs84_to_blender(lon, lat)
polyline.points[i].co = (x, 0, z, 1)
obj = bpy.data.objects.new('Road', curve)
bpy.context.collection.objects.link(obj)
6.2 自动化场景生成
创建一个完整的城市区块:
python复制def generate_city_block(center_lon, center_lat, size=0.02):
"""生成一个城市街区"""
# 创建地形基底
terrain = add_terrain(center_lon, center_lat, size=size*100000)
# 生成道路网格
road_width = size * 0.2
for i in range(5):
offset = (i - 2) * size * 0.25
# 横向道路
create_road(center_lon - size, center_lat + offset,
center_lon + size, center_lat + offset,
width=road_width)
# 纵向道路
create_road(center_lon + offset, center_lat - size,
center_lon + offset, center_lat + size,
width=road_width)
# 生成建筑物
for x in range(4):
for y in range(4):
b_lon = center_lon + (x - 1.5) * size * 0.25
b_lat = center_lat + (y - 1.5) * size * 0.25
generate_buildings(b_lon, b_lat, radius=size*0.1)
在实际项目中,我发现这种自动化生成可以快速搭建场景原型,然后再手动调整重点区域的细节。比如商业区的建筑可以更高更密集,而住宅区则可以更稀疏并添加更多绿化。
