Godot 城市模拟 – 008 为建筑物添加贴图

效果预览

下图展示了使用自定义材质创建的3D建筑物效果,墙面纹理清晰可见,建筑物结构完整:
file

贴图资源获取

  1. 访问 ​​AmbientCG​​ 纹理资源网站:
    网站:https://ambientcg.com/list?q=building&sort=popular

  2. 在搜索栏输入"building"筛选建筑类纹理:
    file

  3. 选择一个喜欢的下载
    下载后放在 res://assets/texttures/ 文件夹中
    file

制作材质

新建材质

res://assets/materials/ 文件夹右键 新建资源
file

file

选择 standard_material_3d 创建

添加贴图

双击 res://assets/materials/building_standard_material_3d.tres 打开材质属性,把贴图拖动到 Texture 属性上
file

修改创建建筑物的方法

侧面设置贴图参数

# 创建侧面(连接底部和顶部的四边形面)
    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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小武的开发空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值