Raymarcher 很慢

问题描述

我正在 pygame 中制作一个 raymarcher,它超级慢。我想不出办法让它更快。我尝试使用多个线程,但没有用。我尝试了所有我能想到的方法,但没有奏效。如果有人能给我一个答案以加快速度,我将不胜感激。

raymarcher.py:

import pygame as pg
import math
import threading as t

def clamp(num,sml,lrg):
    return max(sml,min(num,lrg))

class Vector3:
    def __init__(self,x,y,z):
        self.x = x
        self.y = y
        self.z = z
    
    def __add__(self,other):
        return Vector3(self.x + other.x,self.y + other.y,self.z + other.z)
    
    def __sub__(self,other):
        return Vector3(self.x - other.x,self.y - other.y,self.z - other.z)
    
    def __repr__(self):
        return f"({self.x},{self.y},{self.z})"
    
    @staticmethod
    def norm(pos):
        return (pos.x ** 2 + pos.y ** 2 + pos.z ** 2) ** 0.5
    
    def magnitude(self):
        return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
    
    def normalize(self):
        m = self.magnitude()
        if m > 0:
            return Vector3(self.x / m,self.y / m,self.z / m)

class Vector2:
    def __init__(self,y):
        self.x = x
        self.y = y
    
    def __add__(self,other):
        return Vector2(self.x + other.x,self.y + other.y)
    
    def __sub__(self,other):
        return Vector2(self.x - other.x,self.y - other.y)
    
    def __repr__(self):
        return f"({self.x},{self.y})"
    
    @staticmethod
    def norm(pos):
        return (pos.x ** 2 + pos.y ** 2) ** 0.5
    
    def magnitude(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)
        
    def normalize(self):
        m = self.magnitude()
        if m > 0:
            return Vector2(self.x / m,self.y / m)

class Color:
    def __init__(self,r,g,b):
        self.r = r
        self.g = g
        self.b = b
    
    @staticmethod
    def hex(hexCol):
        if hexCol[0] == "#":
            if len(hexCol) == 7:
                red = int(hex(int("0x" + hexCol[1:2],16)))
                green = int(hex(int("0x" + hexCol[3:4],16)))
                blue = int(hex(int("0x" + hexCol[5:6],16)))
            return Color(red,green,blue)
        else:
            raise Exception("Invalid hex color")
    else:
        raise Exception("Hex color doesn't start with '#'")

    def __mul__(self,other):
        return Color(self.r * other,self.g * other,self.b * other)
    
    def __repr__(self):
        return f"({self.r},{self.g},{self.b})"

class Ray:
    def __init__(self,pos,ang):
        self.pos = pos
        self.point = pos
        self.angle = ang
    
    def advance(self,amount):
        self.point += Vector3(math.sin(self.angle.x) * amount,math.sin(self.angle.z))

class Sphere:
    def __init__(self,radius,color):
        self.pos = pos
        self.radius = radius
        self.color = color
    
    def getdist(self,point):
        return Vector3.norm(point - self.pos) - self.radius

class Light:
    def __init__(self,pos):
        self.pos = pos

WIDTH = 800
HEIGHT = 600

screen = pg.display.set_mode((WIDTH,HEIGHT))

shapes = [Sphere(Vector3(0,10),2,Color(255,0))]

def Raymarch():
    tiMetaken = 0
    left = 0
    
    y = 0
    while y < HEIGHT:
        x = 0
        while x < WIDTH:
            r = Ray(Vector3(x - WIDTH // 2,y - HEIGHT // 2,0),Vector3(0,0))
            dist = 0
            collides = False
            i = 0
            while i < 10:
                distance = min([s.getdist(r.point) / 10 for s in shapes])
                if distance < 5:
                    collides = True
                    break
                r.advance(distance)
                i += 1
            finalPos = r.point
            
            if collides:
                dists = [[s.getdist(finalPos),s.color] for s in shapes]
                dists.sort(key=lambda x: x[0])
                color = dists[0][1]
            else:
                color = Color(255,255,255)
            screen.set_at((x,y),(clamp(round(color.r),255),clamp(round(color.g),clamp(round(color.b),255)))
            x += 1
            tiMetaken += 1
            left = round(tiMetaken / (WIDTH * HEIGHT) * 100,1)
            leftPrint = f"{left}%"
            print(leftPrint,end="\r")
        y += 1

thread = t.Thread(target=Raymarch,daemon=True)
thread.start()

running = True
while running:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
    
    pg.display.update()

解决方法

我修改了 def RayMarch 以仅在每行末尾打印:

def RayMarch():
    timeTaken = 0
    left = 0
    
    y = 0
    while y < HEIGHT:
        total_prints = ''
        
        ...
        
            total_prints += f"{left}%" + '\r'
        y += 1

        print(total_prints)

似乎 print() 命令需要很长时间。