pygame-community / pygame-ce

🐍🎮 pygame - Community Edition is a FOSS Python library for multimedia applications (like games). Built on top of the excellent SDL library.
https://pyga.me
822 stars 131 forks source link

`Texture.draw_quads()` breaks the perspective #2422

Open yunline opened 1 year ago

yunline commented 1 year ago

Original Image:

tnt_side

Expected Output Image :

image

Code ```py import numpy as np import pygame np.set_printoptions(precision=3, suppress=True) sf=pygame.display.set_mode((640,480)) IMG_SIZE = 512 img=pygame.image.load("tnt_side.png") img=pygame.transform.scale(img, (IMG_SIZE,IMG_SIZE)) img=pygame.surfarray.pixels3d(img) quad=np.array([[10,10],[200,140],[240,260],[100,400]]) uv=np.array([[0,0],[1,0],[1,1],[0,1]])*IMG_SIZE # ref https://blog.csdn.net/REstrat/article/details/126987198 # 此处的src是原坐标数组,dst是目标坐标数组 def WarpPerspectiveMatrix(src, dst): assert src.shape[0] == dst.shape[0] and src.shape[0] >= 4 nums = src.shape[0] # 4组对应点,每组2行8列 # 总共8行8列的数组,对应公式(3)中最左边的矩阵 A = np.zeros((2 * nums, 8)) # 4组对应点,每组2行1列 # 总共8行1列,对应公式(3)中最右边的矩阵 B = np.zeros((2 * nums, 1)) # 矩阵赋值,0下标为x值,1下标为y值 for i in range(0, nums): A_i = src[i,:] B_i = dst[i,:] A[2*i, :] = [A_i[0], A_i[1], 1, 0, 0, 0, -A_i[0]*B_i[0], -A_i[1]*B_i[0]] B[2*i] = B_i[0] A[2*i+1, :] = [0, 0 , 0, A_i[0], A_i[1], 1, -A_i[0]*B_i[1], -A_i[1]*B_i[1]] B[2*i+1] = B_i[1] # 转换为矩阵 A = np.mat(A) # 求解未知数值,.I表示求逆 warpMatrix = A.I * B # 矩阵后处理,主要是将a33的1值赋值回去 warpMatrix = np.array(warpMatrix).T[0] warpMatrix = np.insert(warpMatrix, warpMatrix.shape[0], values=1.0, axis=0) warpMatrix = warpMatrix.reshape((3, 3)) return warpMatrix mat=WarpPerspectiveMatrix(quad,uv) def map_point(point): o=mat.dot((*point,1)) return round(o[0]/o[2]),round(o[1]/o[2]) arr=pygame.surfarray.pixels3d(sf) xmin=min(quad[:,0]) xmax=max(quad[:,0]) ymin=min(quad[:,1]) ymax=max(quad[:,1]) for y in range(ymin,ymax+1): for x in range(xmin,xmax+1): p=map_point((x,y)) if -1

Output Image (using Texture.draw_quads()):

image

Code ```py import pygame from pygame._sdl2 import video win = video.Window() ren = video.Renderer(win) img=pygame.image.load("tnt_side.png") tex = video.Texture.from_surface(ren, img) quad=[[10,10],[200,140],[240,260],[100,400]] uv=[[0,0],[1,0],[1,1],[0,1]] tex.draw_quad(*quad,*uv) ren.present() while 1: pygame.event.get() ```
raytopianprojects commented 1 year ago

I've ran across this too and it seems to be due the use of affine transformations in SDL. https://discourse.libsdl.org/t/am-i-using-sdl-rendergeometry-correctly/32995

https://en.wikipedia.org/wiki/Texture_mapping#Affine_texture_mapping Perspective_correct_texture_mapping svg