【Godot4.3】模拟多边形阴影的方法和函数

概述

在绘图函数的使用过冲中,已经总结出了CanvasShape这样的图形类,并通过将其资源化,可以方便的用于自定义节点。

初期的CanvasShape也加入了多边形阴影的绘制选项,不过是简单的复制+偏移形式。本篇在简单复制+偏移形式阴影的基础上,讨论了多边形的平行光阴影、点光源阴影等模拟原理和在Godot4.3中的实际求解。最后给出一个初步的既适用于凸多边形也适用于凹多边形的平行光阴影求解函数。

一些阴影类型与求解原理

偏移阴影

通过复制和偏移多边形一定距离,可以获得一个简单的阴影,让物体看起来悬浮了起来。

在这里插入图片描述

平行光阴影

模拟平行光照射的阴影,可以在复制和偏移多边形的基础上,进一步求原图形和偏移阴影两者的并集(一个凹多边形),再求这个凹多边形的凸多边形,就可以获得平行光阴影的多边形。

在这里插入图片描述

远距离时无法求并集的修正

但是这种方式在阴影偏移距离过大,原图形和阴影两者没有交集时,无法获得正确的并集。也就无法得到正确的平行光阴影。

在这里插入图片描述

修正的方式也很简单,在原图形和偏移阴影之间,创建一个连接两者轴对齐包围盒(Rect2)中心点的窄矩形。然后求三者的并集,再求凸多边形,这样无论多远的偏移距离都可以实现求解。

在这里插入图片描述

凹多边形求阴影的修正

在原始图形为凹多边形时,用上面的方法会求解出多余的部分。

在这里插入图片描述

修正思路也很简单,就是先将凹多边形拆解为多个凸多边形(或者获取三角化的三角形集合),然后分别求阴影图形,最后再求并集。

在这里插入图片描述

点光源阴影

在平行光求阴影的原理基础上,实际上就是依据点光源与形状的距离,设定偏移阴影进行一定的缩放。其余求解思路几乎没有变化。

在这里插入图片描述

顶点着色用于创建渐变透明

真实的阴影会随着距离逐渐变淡。在计算机里就是逐渐变透明。利用绘图函数的顶点着色功能,可以创建逐渐渐变的透明阴影效果。

测试

以下是一些测试阶段的截图,初期基于简单的矩形进行试验。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

求多边形的平行光阴影函数

为了简化调用和尽可能的复用,我将求多边形平行光阴影封装为了一个函数。凹多边、凸多边形阴影的求解用这个函数都可以求解。

只需要传入多边形顶点数组,设定阴影偏移的角度和距离,便可以直接获得阴影多边形的顶点数据。

# 求多边形的平行光阴影
# 20249701:18:06
# 作者:巽星石

func polygon_dir_light_shadow(
	shape:PackedVector2Array, # 要求阴影的图形
	angle:float = 45.0,              # 阴影方向(与X轴正方向夹角)
	length:float = 10.0,             # 阴影长度
) -> PackedVector2Array:
	var result:PackedVector2Array # 最终阴影结果
	var polygons = Geometry2D.decompose_polygon_in_convex(shape)  # 分解为多个凸多边形
	if polygons.size() > 1:  # shape为凹多边形
		var shadows:Array[PackedVector2Array] = []
		# 逐个求阴影
		for polygon in polygons:
			shadows.append(polygon_dir_light_shadow(polygon,angle,length))
		# 逐个求并集
		result = shadows[0]
		for i in range(1,shadows.size()):
			result = Geometry2D.merge_polygons(result,shadows[i])[0]
	elif polygons.size() == 1:  # shape为凸多边形
		var dir = Vector2.RIGHT.rotated(deg_to_rad(angle))  # 阴影的偏转方向
		var offset = dir * length  # 阴影相对原图形的偏移
		# 1.获取偏移的阴影
		var shadow:PackedVector2Array = Transform2D().translated(offset) * shape.duplicate()  # 应用偏移后的阴影多边形
		var shape_rect:Rect2 = get_rect(shape)
		var shadow_rect:Rect2 = get_rect(shadow)
		
		# 2.获取连接阴影和原图形之间的矩形
		var link_rect = CanvasRect.new()
		link_rect.height = 3
		link_rect.width = shadow_length
		var link_rect_points = Transform2D().rotated(deg_to_rad(angle)).translated(offset/2.0 + shape_rect.get_center()) * link_rect.points
		
		# 3.求原始图形和连接矩形之间的并集
		var union1 = Geometry2D.merge_polygons(shape,link_rect_points)[0]
		# 4.求并集1和偏移阴影之间的并集
		var union = Geometry2D.merge_polygons(union1,shadow)[0]
		# 5.求凸多边形
		result = Geometry2D.convex_hull(union)
	return result

# 获取图形的矩形
func get_rect(shape:PackedVector2Array) -> Rect2:
	# 拆分出X坐标和Y坐标数组
	var x_arr = []
	var y_arr = []
	for p in shape:
		x_arr.append(p.x)
		y_arr.append(p.y)
	# 最小值构成Rect2的offset
	var pos = Vector2(x_arr.min(),y_arr.min())
	# 最大值 - pos = Rect2 的 size
	var siz = Vector2(x_arr.max(),y_arr.max()) - pos
	return Rect2(pos,siz)

凹多边形阴影实例

开启show_debug后,会绘制被拆分的每个凸多边形,及其偏移阴影,连接矩形等。

凸多边形阴影实例

后续

  • 目前在测试场景中编写,后续会直接放入CanvasShape基类中,覆盖原有的阴影选项和绘制方式。
  • 所有CanvasShape及其子类型都可以直接使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巽星石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值