提高光线追踪功能的性能

Ste*_*ini 13 python performance

我在python中有一个简单的光线跟踪器.渲染图像200x200需要4分钟,这对我来说绝对太过分了.我想改善这种情况.

一些要点:我为每个像素拍摄多条光线(以提供抗锯齿),每个像素总共有16条光线.200x200x16总计640000条光线.必须测试每条光线对场景中多个Sphere对象的影响.雷也是一个相当微不足道的对象

class Ray(object):
    def __init__(self, origin, direction):
        self.origin = numpy.array(origin)
        self.direction = numpy.array(direction)
Run Code Online (Sandbox Code Playgroud)

Sphere稍微复杂一些,并且带有hit/nohit的逻辑:

class Sphere(object):
    def __init__(self, center, radius, color):
        self.center = numpy.array(center)
        self.radius = numpy.array(radius)
        self.color = color

    @profile 
    def hit(self, ray):
        temp = ray.origin - self.center
        a = numpy.dot(ray.direction, ray.direction)
        b = 2.0 * numpy.dot(temp, ray.direction)
        c = numpy.dot(temp, temp) - self.radius * self.radius
        disc = b * b - 4.0 * a * c

        if (disc < 0.0):
            return None
        else:
            e = math.sqrt(disc)
            denom = 2.0 * a
            t = (-b - e) / denom 
            if (t > 1.0e-7):
                normal = (temp + t * ray.direction) / self.radius
                hit_point = ray.origin + t * ray.direction
                return ShadeRecord.ShadeRecord(normal=normal, 
                                               hit_point=hit_point, 
                                               parameter=t, 
                                               color=self.color)           

            t = (-b + e) / denom

            if (t > 1.0e-7):
                normal = (temp + t * ray.direction) / self.radius                hit_point = ray.origin + t * ray.direction
                return ShadeRecord.ShadeRecord(normal=normal, 
                                               hit_point=hit_point, 
                                               parameter=t, 
                                               color=self.color)       

        return None    
Run Code Online (Sandbox Code Playgroud)

现在,我运行了一些分析,看起来最长的处理时间是在hit()函数中

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  2560000  118.831    0.000  152.701    0.000 raytrace/objects/Sphere.py:12(hit)
  1960020   42.989    0.000   42.989    0.000 {numpy.core.multiarray.array}
        1   34.566   34.566  285.829  285.829 raytrace/World.py:25(render)
  7680000   33.796    0.000   33.796    0.000 {numpy.core._dotblas.dot}
  2560000   11.124    0.000  163.825    0.000 raytrace/World.py:63(f)
   640000   10.132    0.000  189.411    0.000 raytrace/World.py:62(hit_bare_bones_object)
   640023    6.556    0.000  170.388    0.000 {map}
Run Code Online (Sandbox Code Playgroud)

这并不让我感到惊讶,我希望尽可能地减少这个值.我转到行分析,结果是

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    12                                               @profile
    13                                               def hit(self, ray):
    14   2560000     27956358     10.9     19.2          temp = ray.origin - self.center
    15   2560000     17944912      7.0     12.3          a = numpy.dot(ray.direction, ray.direction)
    16   2560000     24132737      9.4     16.5          b = 2.0 * numpy.dot(temp, ray.direction)
    17   2560000     37113811     14.5     25.4          c = numpy.dot(temp, temp) - self.radius * self.radius
    18   2560000     20808930      8.1     14.3          disc = b * b - 4.0 * a * c
    19                                                   
    20   2560000     10963318      4.3      7.5          if (disc < 0.0):
    21   2539908      5403624      2.1      3.7              return None
    22                                                   else:
    23     20092        75076      3.7      0.1              e = math.sqrt(disc)
    24     20092       104950      5.2      0.1              denom = 2.0 * a
    25     20092       115956      5.8      0.1              t = (-b - e) / denom
    26     20092        83382      4.2      0.1              if (t > 1.0e-7):
    27     20092       525272     26.1      0.4                  normal = (temp + t * ray.direction) / self.radius
    28     20092       333879     16.6      0.2                  hit_point = ray.origin + t * ray.direction
    29     20092       299494     14.9      0.2                  return ShadeRecord.ShadeRecord(normal=normal, hit_point=hit_point, parameter=t, color=self.color)
Run Code Online (Sandbox Code Playgroud)

因此,似乎大部分时间都花在了这段代码上:

        temp = ray.origin - self.center
        a = numpy.dot(ray.direction, ray.direction)
        b = 2.0 * numpy.dot(temp, ray.direction)
        c = numpy.dot(temp, temp) - self.radius * self.radius
        disc = b * b - 4.0 * a * c
Run Code Online (Sandbox Code Playgroud)

我真的没有看到很多优化的地方.您是否知道如何在不使用C的情况下使此代码更高效?

sho*_*lte 8

查看代码,看起来您的主要问题是您的代码行被调用了2560000次.无论您在该代码中执行什么样的工作,这都会花费很多时间.但是,使用numpy,你可以将很多这项工作聚合成少量的numpy调用.

首先要做的是将你的光线组合成大数组.不使用具有1x3向量作为原点和方向的Ray对象,而是使用具有命中检测所需的所有光线的Nx3阵列.点击功能的顶部最终会如下所示:

temp = rays.origin - self.center
b = 2.0 * numpy.sum(temp * rays.direction,1)
c = numpy.sum(numpy.square(temp), 1) - self.radius * self.radius
disc = b * b - 4.0 * c
Run Code Online (Sandbox Code Playgroud)

对于下一部分,您可以使用

possible_hits = numpy.where(disc >= 0.0)
a = a[possible_hits]
disc = disc[possible_hits]
...
Run Code Online (Sandbox Code Playgroud)

继续只通过判别测试的值.您可以通过这种方式轻松获得数量级的性能提升.


phk*_*ler 7

1)光线跟踪很有趣但是如果你完全关心性能,请转储python并切换到C.不是C++,除非你是某种超级专家,只是C.

2)具有多个(20个或更多)对象的场景中的大胜利是使用空间索引来减少交叉点测试的数量.流行的选择是kD-trees,OctTrees,AABB.

3)如果你是认真的,请查看ompf.org - 这是它的资源.

4)不要和python一起询问优化问题 - 大多数人可以通过一个拥有10万个三角形的室内场景每秒拍摄1百万到2百万条光线......每个核心.

我喜欢Python和光线跟踪,但绝不会考虑将它们组合在一起.在这种情况下,正确的优化是切换语言.


Ben*_*son 1

使用这样的代码,您可能会受益于记住常见的子表达式,例如self.radius * self.radiusasself.radius21 / self.radiusas self.one_over_radius。Python 解释器的开销可能会主导这些微不足道的改进。