本文最后更新于11 天前
效果预览
下图展示了使用自定义材质创建的3D建筑物效果,墙面纹理清晰可见,建筑物结构完整:
贴图资源获取
-
访问 AmbientCG 纹理资源网站:
网站:https://ambientcg.com/list?q=building&sort=popular -
在搜索栏输入”building”筛选建筑类纹理:
-
选择一个喜欢的下载
下载后放在res://assets/texttures/
文件夹中
制作材质
新建材质
res://assets/materials/
文件夹右键 新建资源
选择 standard_material_3d
创建
添加贴图
双击 res://assets/materials/building_standard_material_3d.tres
打开材质属性,把贴图拖动到 Texture 属性上
修改创建建筑物的方法
侧面设置贴图参数
# 创建侧面(连接底部和顶部的四边形面)
for i in range(point_count):
var next_i = (i + 1) % point_count # 下一个点的索引(循环)
# 侧面四边形分解为两个三角形
# 第一个三角形
surface_tool.set_uv(Vector2(0, 0))
surface_tool.add_vertex(prism_points[i])
surface_tool.set_uv(Vector2(1, 0))
surface_tool.add_vertex(prism_points[next_i])
surface_tool.set_uv(Vector2(0, 1))
surface_tool.add_vertex(prism_points[point_count + i])
# 第二个三角形
surface_tool.set_uv(Vector2(1, 0))
surface_tool.add_vertex(prism_points[next_i])
surface_tool.set_uv(Vector2(1, 1))
surface_tool.add_vertex(prism_points[point_count + next_i])
surface_tool.set_uv(Vector2(0, 1))
surface_tool.add_vertex(prism_points[point_count + i])
修复原来的bug
原来创建建筑物的方法存在的问题是,建筑物有内凹部分时,上下底面会有多于的三角形面被创建。
# 使用Godot内置的三角剖分算法
var triangles = Geometry2D.triangulate_polygon(points_2d)
if triangles.size() == 0:
print("错误:无法对多边形进行三角剖分")
return null
完整代码
static func create_building_from_base_and_height(base_points: Array, height: float, material: StandardMaterial3D) -> MeshInstance3D:
# 验证输入
if base_points.size() < 3:
print("错误:至少需要3个点来构成多边形")
return null
# 将3D点投影到2D平面进行三角剖分
var points_2d = []
for point in base_points:
points_2d.append(Vector2(point.x, point.z))
# 使用Godot内置的三角剖分算法
var triangles = Geometry2D.triangulate_polygon(points_2d)
if triangles.size() == 0:
print("错误:无法对多边形进行三角剖分")
return null
var point_count = base_points.size()
# 计算建筑物尺寸用于UV缩放
var min_point = base_points[0]
var max_point = base_points[0]
for point in base_points:
min_point = min_point.min(point)
max_point = max_point.max(point)
var building_size = max_point - min_point
material.uv1_scale = Vector3(building_size.x / 10, building_size.y / 10, building_size.z / 10)
# 计算棱柱体的所有顶点
var prism_points = []
# 底部点(底面多边形)
for i in range(point_count):
prism_points.append(base_points[i])
# 顶部点(底面多边形加上高度)
for i in range(point_count):
var top_point = base_points[i] + Vector3(0, height, 0)
prism_points.append(top_point)
# 使用SurfaceTool创建棱柱体
var surface_tool = SurfaceTool.new()
surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)
# 创建底面(使用三角剖分结果)
for i in range(0, triangles.size(), 3):
var index1 = triangles[i]
var index2 = triangles[i + 1]
var index3 = triangles[i + 2]
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index1])
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index2])
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index3])
# 创建顶面(使用相同的三角剖分)
for i in range(0, triangles.size(), 3):
var index1 = triangles[i] + point_count
var index2 = triangles[i + 1] + point_count
var index3 = triangles[i + 2] + point_count
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index1])
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index2])
surface_tool.set_uv(Vector2.ZERO)
surface_tool.add_vertex(prism_points[index3])
# 创建侧面(连接底部和顶部的四边形面)
for i in range(point_count):
var next_i = (i + 1) % point_count # 下一个点的索引(循环)
# 侧面四边形分解为两个三角形
# 第一个三角形
surface_tool.set_uv(Vector2(0, 0))
surface_tool.add_vertex(prism_points[i])
surface_tool.set_uv(Vector2(1, 0))
surface_tool.add_vertex(prism_points[next_i])
surface_tool.set_uv(Vector2(0, 1))
surface_tool.add_vertex(prism_points[point_count + i])
# 第二个三角形
surface_tool.set_uv(Vector2(1, 0))
surface_tool.add_vertex(prism_points[next_i])
surface_tool.set_uv(Vector2(1, 1))
surface_tool.add_vertex(prism_points[point_count + next_i])
surface_tool.set_uv(Vector2(0, 1))
surface_tool.add_vertex(prism_points[point_count + i])
# 生成法线(确保法线方向正确)
surface_tool.generate_normals()
# 生成网格
var array_mesh = surface_tool.commit()
# 创建节点和材质
var prism_mesh_instance = MeshInstance3D.new()
prism_mesh_instance.mesh = array_mesh
material.cull_mode = BaseMaterial3D.CULL_DISABLED # 禁用背面剔除
material.albedo_color = Color(0.9,0.9,0.9)
material.uv1_scale = Vector3(2,1,2)
# 正确应用材质
prism_mesh_instance.material_override = material
print("基于不规则多边形底面创建棱柱体完成!点数:", point_count)
return prism_mesh_instance
修改 res://scripts/environment.gd
中创建建筑物方法
材质加载方法
var material : StandardMaterial3D = load("res://assets/materials/building_standard_material_3d.tres")
func create_buildings():
var material = load("res://assets/materials/building_standard_material_3d.tres")
for way in ways:
# 检查是否为建筑物
if way.tags.get("building") and way.node_ids.size() > 2:
# 确保路径是闭合的
if way.node_ids[0] != way.node_ids[-1]:
continue
# 获取建筑物高度
var height = 5.0
if way.tags.get("height"):
height = float(way.tags.height)
elif way.tags.get("building:levels"):
height = float(way.tags["building:levels"]) * 3.0
# 收集建筑物顶点
var base_points = []
for i in range(way.node_ids.size() - 1):
var node_id = way.node_ids[i]
var node = nodes.get(str(node_id))
if node:
var point = functions.latlon_to_vector3(node.lat, node.lon)
point.y += 0.1 # 增加抬高量
base_points.append(point)
if base_points.size() < 3:
continue
var building = functions.create_building_from_base_and_height(base_points,height,material)
add_child(building)