Skip to main content
 首页 » 编程设计

python之如何改进我在 pygame 中对 3d 空间的模拟

2024年08月12日35mate10pro

至于我之前的问题,我试图在 pygame 中模拟 3d 空间。到目前为止,我想出了一个非常简单的想法,即使用第三个坐标作为分母来“压缩”(很确定这里有一些我不知道的术语)屏幕中心周围最远的点并减小它们的大小.

有人可以建议对这个想法进行简单的改进吗?我觉得我可以调整用于投影的分母(请参阅代码)以创建更准确的模拟方式。

如果您运行下面的代码,您将得到一个不错的模拟(比方说)一艘宇宙飞船经过一些恒星(按 w 或 s)。如果它们走得太远,它们就会消失,然后会创建一个新的。但是,如果我应用旋转(a 或 d),很明显模拟效果不佳,因为我并没有真正将 3d 点投影到 2d 屏幕上。

import pygame 
import random 
import numpy as np 
 
pygame.init() 
run=True 
 
#screensize 
screensize = (width,height)=(600,600) 
center=(int(width/2),int(height/2)) 
screen = pygame.display.set_mode(screensize) 
 
#delta mov 
ds=0.1 
do=0.0001 
 
#Stars 
points=[] 
for i in range(1000): 
    n1 = random.randrange(-5000,5000) 
    n2 = random.randrange(-5000,5000) 
    n3 = random.randrange(-30,30) 
    points.append([n1,n2,n3]) 
 
while run: 
    pygame.time.delay(20) 
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            run=False 
 
    ################## keys 
    keys=pygame.key.get_pressed() 
 
    if keys[pygame.K_w]: 
        for p in points: 
            p[2]-=ds 
    if keys[pygame.K_s]: 
        for p in points: 
            p[2]+=ds 
 
    if keys[pygame.K_a] or keys[pygame.K_d]: 
        if keys[pygame.K_a]: 
            for p in points: 
                p[0]=np.cos(-do)*p[0]-np.sin(-do)*p[2] 
                p[2]=np.sin(-do)*p[0]+np.cos(-do)*p[2] 
        else: 
            for p in points: 
                p[0]=np.cos(do)*p[0]-np.sin(do)*p[2] 
                p[2]=np.sin(do)*p[0]+np.cos(do)*p[2] 
 
 
    ###############################projection################### 
 
    for p in points: 
        #this is to create new stars 
        if p[2]<=-30 or p[2]>=30: 
            p[0] = random.randrange(-5000,5000) 
            p[1] = random.randrange(-5000,5000) 
            p[2] =30 
        else: 
            #this is to ignore stars which are behind the ship 
            if p[2]<=0: 
                pass 
            else: 
                try: 
                    #THIS IS THE PROJECTION I USE, I TAKE THE RADIUS BECAUSE I GUESS I'LL NEED IT... BUT I DON'T USE IT XD 
                    r = ((p[0]**2+p[1]**2+p[2]**2)**(1/2)) 
                    pygame.draw.circle(screen,(255,255,0),(int(p[0]/p[2]+center[0]),int(p[1]/p[2]+center[1])),int(10/p[2])) 
                #this is to prevent division by cero and alike 
                except Exception as e: 
                    pass 
 
    pygame.display.update() 
    screen.fill((0,0,0)) 
 
 
pygame.quit() 

请您参考如下方法:

在一般情况下是通过Homogeneous coordinates实现的.您的方法接近于此。

我推荐操作Cartesian coordinates ,其中 3 个维度具有相同的比例。
模拟 Perspective projection当你画点时。
这意味着您必须计算 Homogeneous coordinates 的 w 分量取决于点的深度(z 坐标)(例如 w = p[2] * 30/5000)并执行 x 的“透视划分”, y 和 z 组件由 w 组件绘制点之前。例如:

#delta mov 
ds=10 
do=0.01 
 
#Stars 
points=[] 
for i in range(1000): 
    n1 = random.randrange(-5000,5000) 
    n2 = random.randrange(-5000,5000) 
    n3 = random.randrange(-5000,5000) 
    points.append([n1,n2,n3]) 
 
while run: 
    pygame.time.delay(20) 
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            run=False 
 
    ################## keys 
    keys=pygame.key.get_pressed() 
 
    if keys[pygame.K_w]: 
        for p in points: 
            p[2]-=ds 
    if keys[pygame.K_s]: 
        for p in points: 
            p[2]+=ds 
 
    if keys[pygame.K_a] or keys[pygame.K_d]: 
        if keys[pygame.K_a]: 
            for p in points: 
                p[0], p[2] = np.cos(-do)*p[0]-np.sin(-do)*p[2], np.sin(-do)*p[0]+np.cos(-do)*p[2] 
        else: 
            for p in points: 
                p[0], p[2] = np.cos(do)*p[0]-np.sin(do)*p[2], np.sin(do)*p[0]+np.cos(do)*p[2] 
 
    ###############################projection################### 
 
    screen.fill((0,0,0)) 
    for p in points: 
        #this is to create new stars 
        if p[2]<=-5000 or p[2]>=5000: 
            p[0], p[1], p[2] = random.randrange(-5000,5000), random.randrange(-5000,5000), 5000 
        else: 
            #this is to ignore stars which are behind the ship 
            if p[2]<=0: 
                pass 
            else: 
                w = p[2] * 30 / 5000 
                pygame.draw.circle(screen,(255,255,0),(int(p[0]/w+center[0]),int(p[1]/w+center[1])),int(10/w)) 
 
    pygame.display.update() 

此外,旋转不正确。当你做的时候

p[0]=np.cos(-do)*p[0]-np.sin(-do)*p[2] 
p[2]=np.sin(-do)*p[0]+np.cos(-do)*p[2] 

第一行的p[0]改变了,但是第二行应该使用原来的值。
做“元组”赋值来解决问题:

p[0], p[2] = np.cos(-do)*p[0]-np.sin(-do)*p[2], np.sin(-do)*p[0]+np.cos(-do)*p[2]