【Godot4.3】用2D网格模拟一点透视

概述

空间的透视是可以在二维平面上参数化计算和模拟的。本篇基于CanvasItem绘制函数draw_colored_polygon()自带的UV坐标和贴图功能,实现基础的平行透视效果。
或者可以叫做一点透视,由一个消失点决定物体的透视效果。

测试代码

extends Node2D

var rect = Rect2(-50,-50,60,60)  # 透视图形
var dis_p                        # 消失点

var thickness:float = 60.0  # 厚度
var scale_rate:float           # 缩放比例
var uvs = Points2D.Vec2Arr("0,0 0,1 1,1 1,0")

@export var texture:Texture2D:
	set(val):
		texture = val
		queue_redraw()


func _ready() -> void:
	dis_p = get_viewport_rect().get_center()  # 消失点

func _process(delta: float) -> void:
	# 矩形跟随鼠标移动
	rect.position = get_global_mouse_position() - rect.size/2.0
	queue_redraw()

func _draw() -> void:
	var c = rect.get_center()       # 矩形中心
	var dir = c.direction_to(dis_p) # 由矩形中心向消失点的方向
	var dis = c.distance_to(dis_p)  
	scale_rate = (dis - thickness)/dis
	
	# 求偏移矩形
	var move = Transform2D().translated(-c)
	var bottom_rect = move * rect
	bottom_rect = Transform2D().scaled(Vector2.ONE * scale_rate).translated(dir * thickness) * bottom_rect
	bottom_rect = bottom_rect * move
	
	# 求侧边多边形
	var rect_points = get_rect_points(rect) 
	var bottom_rect_points = get_rect_points(bottom_rect)
	
	var side_top = PackedVector2Array([rect_points[0],bottom_rect_points[0],bottom_rect_points[1],rect_points[1]])
	var side_left = PackedVector2Array([rect_points[0],bottom_rect_points[0],bottom_rect_points[3],rect_points[3]])
	var side_right = PackedVector2Array([rect_points[1],bottom_rect_points[1],bottom_rect_points[2],rect_points[2]])
	var side_bottom = PackedVector2Array([rect_points[2],bottom_rect_points[2],bottom_rect_points[3],rect_points[3]])
	
	
	var rect_4:Array[Rect2] = ShapeTests.get_rect_4_parts(get_viewport_rect()) # 四象限划分
	
	draw_line(c,dis_p,Color.ORANGE_RED,1)
	
	
	draw_colored_polygon(bottom_rect_points,Color.AQUAMARINE,uvs,texture) # 底面
	
	var sides:Array[PackedVector2Array]
	
	if rect_4[0].has_point(c):  # 在第一象限
		sides.append_array([side_top,side_left,side_right,side_bottom])
	if rect_4[1].has_point(c):  # 在第二象限
		sides.append_array([side_top,side_right,side_left,side_bottom])
	if rect_4[2].has_point(c):  # 在第三象限
		sides.append_array([side_right,side_bottom,side_top,side_left])
	if rect_4[3].has_point(c):  # 在第四象限
		sides.append_array([side_left,side_bottom,side_top,side_right])
	
	for rec in sides:
		draw_colored_polygon(rec,Color.CADET_BLUE,uvs,texture)
	
	draw_colored_polygon(rect_points,Color.AQUAMARINE,uvs,texture)        # 原始矩形
	
	
	#draw_rect(rect,Color.AQUAMARINE,false,1)
	#draw_rect(bottom_rect,Color.AQUAMARINE,false,1)
	


# 返回矩形四个角点
func get_rect_points(rect:Rect2) -> PackedVector2Array:
	var pots:PackedVector2Array
	var pos = rect.position;	var end = rect.end;	var size = rect.size
	pots = [	pos,pos + Vector2(size.x,0),	end,end - Vector2(size.x,0)]
	return pots

绘制效果:

改进版本

extends Node2D

var rect = Rect2(-50,-50,60,60)  # 透视图形
var dis_p                        # 消失点

var thickness_rate:float = 0.2  # 厚度比例
var scale_rate:float = 0.8       # 缩放比例
var uvs = Points2D.Vec2Arr("0,0 0,1 1,1 1,0")   # 顶点UV坐标

## 纹理
@export var texture:Texture2D:
	set(val):
		texture = val
		queue_redraw()


func _ready() -> void:
	dis_p = get_viewport_rect().get_center()  # 消失点

func _process(delta: float) -> void:
	# 矩形跟随鼠标移动
	rect.position = get_global_mouse_position() - rect.size/2.0
	queue_redraw()

func _draw() -> void:
	var c = rect.get_center()       # 矩形中心
	var dir = c.direction_to(dis_p) # 由矩形中心向消失点的方向
	var dis = c.distance_to(dis_p)
	
	# 求偏移矩形
	var move = Transform2D().translated(-c)
	var bottom_rect = move * rect
	bottom_rect = Transform2D().scaled(Vector2.ONE * scale_rate) * bottom_rect
	bottom_rect = bottom_rect * move
	bottom_rect.position = c.lerp(dis_p,thickness_rate) - bottom_rect.size/2.0
	
	# 求矩形的点集
	var r1_pots = ShapeTests.get_rect_points(rect) 
	var r2_pots = ShapeTests.get_rect_points(bottom_rect)

	# 绘制底面
	draw_colored_polygon(r2_pots,Color.AQUAMARINE,uvs,texture)
	
	var sides = ShapeTests.get_rect_rect_sides(rect,bottom_rect)  # 获取测边多边形
	var order = ShapeTests.get_sides_order(c,dis_p)               # 获取绘制顺序
	# 按顺序绘制侧边面
	for i in order:
		draw_colored_polygon(sides[i],Color.CADET_BLUE,uvs,texture)
	# 绘制原始矩形
	draw_colored_polygon(r1_pots,Color.AQUAMARINE,uvs,texture)

部分代码已经总结为函数,放入ShapeTests函数库,总结的函数如下:

# 求两个矩形相连的侧边多边形
func get_rect_rect_sides(rect1:Rect2,rect2:Rect2) -> Array[PackedVector2Array]:
	var sides:Array[PackedVector2Array]
	var r1_pots = get_rect_points(rect1) 
	var r2_pots = get_rect_points(rect2)
	
	var side_top = PackedVector2Array([r1_pots[0],r2_pots[0],r2_pots[1],r1_pots[1]])
	var side_left = PackedVector2Array([r1_pots[0],r2_pots[0],r2_pots[3],r1_pots[3]])
	var side_right = PackedVector2Array([r1_pots[1],r2_pots[1],r2_pots[2],r1_pots[2]])
	var side_bottom = PackedVector2Array([r1_pots[2],r2_pots[2],r2_pots[3],r1_pots[3]])
	
	sides.append_array([side_top,side_right,side_bottom,side_left])
	return sides

# 返回一个点p在另一个点c所划分的四象限的哪个象限
func get_pos_quadrant(p:Vector2,c:Vector2) -> int:
	var quadrant:int
	var ang = c.direction_to(p).angle()
	if ang> -PI and ang <= - PI * 0.5:  # 在第一象限
		quadrant = 0
	if ang> - PI * 0.5 and ang<= 0:    # 在第二象限
		quadrant = 1
	if ang> 0 and ang<= PI * 0.5:      # 在第三象限
		quadrant = 2
	if ang> PI * 0.5 and ang<= PI:   # 在第四象限
		quadrant = 3
	return quadrant
	
# 返回各个象限绘制的顺序
func get_sides_order(c:Vector2,dis_p:Vector2) -> Array[int]:
	var sides_order:Array[int]
	match get_pos_quadrant(c,dis_p):
		0:
			sides_order.append_array([0,3,1,2])
		1:
			sides_order.append_array([0,1,2,3])
		2:
			sides_order.append_array([1,2,3,0])
		3:
			sides_order.append_array([2,3,0,1])
	return sides_order

以下是改进版本的测试效果:
请添加图片描述
改进版本更符合我最初的设想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巽星石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值