【问题标题】:Raymarcher is SLOOOW [duplicate]Raymarcher 很慢 [重复]
【发布时间】:2021-04-27 21:15:47
【问题描述】:

我正在 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, x, 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, 0, math.sin(self.angle.z))

class Sphere:
    def __init__(self, pos, 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, 0, 10), 2, Color(255, 0, 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, 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), 0, 255), clamp(round(color.g), 0, 255), clamp(round(color.b), 0, 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()

【问题讨论】:

  • 您是否使用分析器对其进行了分析?我建议使用snakeviz 的cProfile,尽管this question 有其他选择。
  • 我如何获得它们?
  • 我想通了,但接下来呢?\
  • snakeviz 不起作用
  • 当然很慢,因为所有的渲染都是在 CPU 上完成的。对于最先进的解决方案,您必须使用 GPU。对于这样的任务,即使是 python 也是一个糟糕的选择。

标签: python-3.x pygame


【解决方案1】:

我将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() 命令需要很长时间。

【讨论】:

    猜你喜欢
    • 2015-01-05
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 2021-12-29
    相关资源
    最近更新 更多