渲染开始的等待时间超级慢

在光线追踪作业中, 运行代码后会弹出一个未响应的python窗口, 这个未响应窗口会等待4-5分钟才开始渲染图像(场景中有大约70个三角形, 且未用加速结构), 但等窗口开始响应以后图像会很快的渲染出来.

问题是我发现在窗口未响应的时候python是在单个CPU线程上跑的, 代码跟官方提供的很相似, 只是加了个三角形class, 在ShootRay()方法 的外面套上了@ti.kernel 请问这种情况为什么会发生?

– PS: 但是当渲染完一次过后关闭窗口后不改变参数的情况下再次运行, 窗口未响应的时间几乎没有.

– PS: 但是当渲染完一次过后关闭窗口后不改变参数的情况下再次运行, 窗口未响应的时间几乎没有.

看这个描述,只有在第一次运行时会很慢,很可能是编译速度的问题,之后因为有offline cache不用重编译,所以没有很长的响应时间
方便贴一下一个最小可复现代码吗?

感谢回复, 下面是一个可以执行的代码, 并附带了一个模型, main函数就是最下面那段While(1)
其中FillTriangles类里面的for循环我只让他循环了4次, 不然第一次运行就要等好久.
我曾试图把Hittable_list中的object变量先设置成空的, 在开始运行后再往里面添加, 但在hit()函数中, 好像taichi只能支持static range, 所以以失败告终. - 很好奇各位大佬是怎么在运行时读取物体的?

import taichi as ti
import numpy as np
ti.init(arch=ti.cuda)
#ti.init(arch=[ti.opengl, ti.metal])


import taichi as ti

PI = 3.14159265

@ti.func
def rand3():
    return ti.Vector([ti.random(),ti.random(),ti.random()])

#为完全漫反射做的随机向量生成函数
#生成了一个中心在0, 半径为1的球
@ti.func
def rand_sphere_point():
    p = 2.0 * rand3() - ti.Vector([1, 1, 1])
    while p.norm() >= 1.0:
        p = 2.0 * rand3() - ti.Vector([1, 1, 1])#意图返回一个圆, 如果返回值在圆外, 就从新随机
    return (p.normalized())

@ti.func
def reflect(v, normal):
    return v - 2 * v.dot(normal) * normal

@ti.func
def refract(uv, n, etai_over_etat):
    cos_theta = min(n.dot(-uv), 1.0)
    r_out_perp = etai_over_etat * (uv + cos_theta * n)
    r_out_parallel = -ti.sqrt(abs(1.0 - r_out_perp.dot(r_out_perp))) * n
    return r_out_perp + r_out_parallel

@ti.func
def reflectance(cosine, ref_idx):
    # Use Schlick's approximation for reflectance.
    r0 = (1 - ref_idx) / (1 + ref_idx)
    r0 = r0 * r0
    return r0 + (1 - r0) * pow((1 - cosine), 5)

#一个存放障碍物的列表
@ti.data_oriented
class Hittable_list:
    def __init__(self):
        self.objects = []
    def add(self, obj):
        self.objects.append(obj)
    def clear(self):
        self.objects = []

    @ti.func
    def hit(self, ray, t_min=0.001, t_max=10e8):
        closest_t = t_max
        is_hit = False
        front_face = False
        hit_point = ti.Vector([0.0, 0.0, 0.0])
        hit_point_normal = ti.Vector([0.0, 0.0, 0.0])
        color = ti.Vector([0.0, 0.0, 0.0])
        material = 1
        for index in ti.static(range(len(self.objects))):
            is_hit_tmp, root_tmp, hit_point_tmp, hit_point_normal_tmp, front_face_tmp, material_tmp, color_tmp =  self.objects[index].Hit(ray, t_min, closest_t)
            if is_hit_tmp:
                closest_t = root_tmp
                is_hit = is_hit_tmp
                hit_point = hit_point_tmp
                hit_point_normal = hit_point_normal_tmp
                front_face = front_face_tmp
                material = material_tmp
                color = color_tmp
        return is_hit, hit_point, hit_point_normal, front_face, material, color,closest_t

#定义射线
@ti.data_oriented
class Ray:
    def __init__(self,start,dir) -> None:
        self.start = start
        self.dir = dir
    @ti.func
    def at(self,t) -> ti.Vector:
        return self.start+self.dir*t

#定义球,三角,三角组成的物体的class   
@ti.data_oriented
class Sphere:
    def __init__(self,center,radius,material,color):
        self.center = center
        self.radius = radius
        self.material = material
        self.color = color
        

    @ti.func
    def Hit(self, ray, t_min=0.001, t_max=10e8):
        oc = ray.start - self.center #虎书P92
        a = ray.dir.dot(ray.dir)
        b = 2.0 * oc.dot(ray.dir)
        c = oc.dot(oc) - self.radius * self.radius
        discriminant = b * b - 4 * a * c
        is_hit = False
        front_face = False
        root = 0.0
        hit_point =  ti.Vector([0.0, 0.0, 0.0])
        hit_point_normal = ti.Vector([0.0, 0.0, 0.0])
        if discriminant > 0:
            sqrtd = ti.sqrt(discriminant)
            root = (-b - sqrtd) / (2 * a)
            if root < t_min or root > t_max:
                root = (-b + sqrtd) / (2 * a)
                if root >= t_min and root <= t_max:
                    is_hit = True
            else:
                is_hit = True
        if is_hit:
            hit_point = ray.at(root)
            hit_point_normal = (hit_point - self.center) / self.radius
            #总是把法线朝外
            if ray.dir.dot(hit_point_normal) < 0:
                front_face = True
            else:
                hit_point_normal = -hit_point_normal
        return is_hit, root, hit_point, hit_point_normal, front_face, self.material, self.color

@ti.data_oriented
class Triangle:
    def __init__(self,ptA,ptB,ptC,material,color):
        self.ptA = ptA
        self.ptB = ptB
        self.ptC = ptC
        self.material = material
        self.color = color
        
    @ti.func       
    def Hit(self, ray, t_min=0.001, t_max=10e8):
        
        is_HitPlane = False
        is_hit = False
        front_face = False
        hit_point =  ti.Vector([0.0, 0.0, 0.0])
        hit_point_normal = ti.Vector([0.0, 0.0, 0.0])

        edgeAB = self.ptA - self.ptB
        edgeAC = self.ptA - self.ptC
        ptAMinusStart = self.ptA - ray.start
        a = edgeAB[0]
        b = edgeAB[1]
        c = edgeAB[2]
        d = edgeAC[0]
        e = edgeAC[1]
        f = edgeAC[2]
        g = ray.dir[0]
        h = ray.dir[1]
        i = ray.dir[2]
        j = ptAMinusStart[0]
        k = ptAMinusStart[1]
        l = ptAMinusStart[2]
        
        M = a*(e*i-h*f) + b*(g*f-d*i) + c*(d*h-e*g)+0.0000001
        #compute t
        t = -(f*(a*k-j*b) + e*(j*c-a*l) + d*(b*l-k*c))/M
       
        is_HitPlane = t>t_min and t<t_max
        
        #color = ti.Vector([0.0,0.0,0.0])#重心坐标
        if(is_HitPlane):
            gamma = (i*(a*k-j*b) + h*(j*c-a*l) + g*(b*l-k*c))/M
            if(gamma > 0 and gamma < 1):
                beta = (j*(e*i-h*f) + k*(g*f-d*i) + l*(d*h-e*g))/M
                if(beta > 0 and beta < 1 - gamma):
                    is_hit = True
                    #color = ti.Vector([gamma,beta,1.0-gamma-beta])
                    #重心坐标
                    

        if(is_hit):
            hit_point = ray.at(t)
            hit_point_normal = ((self.ptC - self.ptB).cross(self.ptA - self.ptC)).normalized()
            if ray.dir.dot(hit_point_normal) < 0:
                front_face = True
            else:
                hit_point_normal = -hit_point_normal

        return is_hit, t, hit_point, hit_point_normal, front_face, self.material, self.color
        #return is_hit, t, hit_point, hit_point_normal, front_face, self.material,color
        
@ti.data_oriented
class Object:
    def __init__(self,boundFrom,boundTo,transform):
        boundCenter = (boundFrom + boundTo) /2.0
        boundRVector = abs(boundTo - boundCenter)
        boundR = max(max(boundRVector[0],boundRVector[1]),boundRVector[2])
        self.boundSphere = Sphere(center=boundCenter+transform, radius=boundR, material=1, color=ti.Vector([0,0,0]))
        self.triangles = Hittable_list()
        self.transform = transform

    def FillTriangles(self,ptPos,faceID,material,color):
        #for index in range(len(faceID)):
        for index in range(1,4):
            i = index - 1
            self.triangles.add(Triangle(ptA=ptPos[faceID[i][0]-1]+self.transform, ptB=ptPos[faceID[i][1]-1]+self.transform, ptC=ptPos[faceID[i][2]-1]+self.transform, material=material, color=color))
            #print("Created",ptPos[faceID[i][0]-1]+self.transform,ptPos[faceID[i][2]-1]+self.transform,ptPos[faceID[i][2]-1]+self.transform)
        #print(len(self.triangles.objects))
    
    @ti.func
    def Hit(self,ray, t_min, closest_t):
        is_hit = False
        root = 0.0
        hit_point =  ti.Vector([0.0, 0.0, 0.0])
        hit_point_normal = ti.Vector([0.0, 0.0, 0.0])
        front_face = False
        color = ti.Vector([0.0, 0.0, 0.0])
        material = 1
        is_hit, root1, hit_point1, hit_point_normal1, front_face1, material1, color1 =  self.boundSphere.Hit(ray, t_min, closest_t)
        
        if is_hit:
            is_hit, hit_point, hit_point_normal, front_face, material, color,root = self.triangles.hit(ray,0.00001,closest_t)    
        return is_hit, root, hit_point, hit_point_normal, front_face, material, color

#######场景物体class定义结束##########
    

@ti.data_oriented
class Camera:
    def __init__(self,lookFrom,lookAt,up,FOV,aspect_ratio):
        #先定义好摄像机自身的一些属性, 不固定的值用field
        self.lookfrom = ti.Vector.field(3, dtype=ti.f32, shape=())
        self.lookat = ti.Vector.field(3, dtype=ti.f32, shape=())
        self.up = ti.Vector.field(3, dtype=ti.f32, shape=())
        self.fov = FOV
        self.aspect_ratio = aspect_ratio
        
        #传参
        self.lookfrom[None] = lookFrom
        self.lookat[None] = lookAt
        self.up[None] = up
        
        #定义屏幕的属性
        self.cam_LLC = ti.Vector.field(3, dtype=ti.f32, shape=())#摄像机的左下角
        self.cam_horizontal = ti.Vector.field(3, dtype=ti.f32, shape=())
        self.cam_vertical = ti.Vector.field(3, dtype=ti.f32, shape=())
        self.reset()

    @ti.kernel
    def reset(self):
        #求出平面的宽和高(这里认为屏幕跟摄像机的距离为1)
        half_height = ti.tan(self.fov / 2.0)
        half_width = self.aspect_ratio * half_height
        
        #算出平面的uvw来确定朝向
        w = (self.lookfrom[None] - self.lookat[None]).normalized()
        u = (self.up[None].cross(w)).normalized()
        v = w.cross(u)
        
        #算出平面边角位置和宽/高向量
        self.cam_LLC[None] = self.lookfrom[None] - half_width * u - half_height * v - w
        self.cam_horizontal[None] = 2 * half_width * u
        self.cam_vertical[None] = 2 * half_height * v

    @ti.func
    def getRay(self,u,v):
        return Ray(self.lookfrom[None],self.cam_LLC[None]+u*self.cam_horizontal[None]+v*self.cam_vertical[None] - self.lookfrom[None])
        #返回一个射线, 起点是摄像机, 看向平面的左下角+一定比例的u,v组合, 最后再减去原点


def AddObject(scene): 
    scene.add(Sphere(center=ti.Vector([-0.9, 0, -2]), radius=0.5, material=3, color=ti.Vector([1, 1, 1])))
    scene.add(Sphere(center=ti.Vector([0.8, 1.7, 0.5]), radius=0.4, material=2, color=ti.Vector([0.8, 0.6, 0.2])))
 
    # #添加墙壁
    scene.add(Sphere(center=ti.Vector([0, 102.5, -1]), radius=100.0, material=1, color=ti.Vector([0.8, 0.8, 0.8])))
    scene.add(Sphere(center=ti.Vector([0, 1, 101]), radius=100.0, material=1, color=ti.Vector([0.8, 0.8, 0.8])))
    scene.add(Sphere(center=ti.Vector([0, 1, -106]), radius=100.0, material=1, color=ti.Vector([0.8, 0.8, 0.8])))
    scene.add(Sphere(ti.Vector([0, -500.5, -1]),500.0, 1,ti.Vector([0.8, 0.8, 0.8])))
    scene.add(Sphere(center=ti.Vector([101.5, 0, -1]), radius=100.0, material=1, color=ti.Vector([0.0, 0.6, 0.0])))
    scene.add(Sphere(center=ti.Vector([-101.5, 0, -1]), radius=100.0, material=1, color=ti.Vector([0.6, 0.0, 0.0])))
        #添加灯光
    scene.add(Sphere(ti.Vector([0, 5.4, -1]),3.0,0, ti.Vector([10.0, 10.0, 10.0])))

    #AddTeaPot
    teaPot = Object(boundFrom=ti.Vector([-0.995900333, -0.457885206, -0.610513628]),boundTo=ti.Vector([0.927217543, 0.457885206, 0.610513628]),transform=ti.Vector([0,0.2,-0.5]))
    teaPotPtPos = [ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([-0.0801299289,-0.228942603,0.610513628]),ti.Vector([-0.538015127,-0.457885206,0]),ti.Vector([-0.0801299289,-0.457885206,0.457885206]),ti.Vector([-0.0801299289,-0.228942603,0.610513628]),ti.Vector([0.530383706,-0.228942603,0]),ti.Vector([-0.0801299289,-0.457885206,0.457885206]),ti.Vector([0.377755284,-0.457885206,0]),ti.Vector([-0.538015127,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.457885206]),ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([-0.0801299289,-0.228942603,0.610513628]),ti.Vector([-0.0801299289,0.228942633,0.457885206]),ti.Vector([0.377755284,0.228942633,0]),ti.Vector([-0.0801299289,-0.228942603,0.610513628]),ti.Vector([0.530383706,-0.228942603,0]),ti.Vector([-0.507489443,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.427359521]),ti.Vector([-0.538015127,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.457885206]),ti.Vector([-0.0801299289,0.228942633,0.427359521]),ti.Vector([0.3472296,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.457885206]),ti.Vector([0.377755284,0.228942633,0]),ti.Vector([-0.0801299289,-0.228942603,-0.610513628]),ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([-0.0801299289,-0.457885206,-0.457885206]),ti.Vector([-0.538015127,-0.457885206,0]),ti.Vector([0.530383706,-0.228942603,0]),ti.Vector([-0.0801299289,-0.228942603,-0.610513628]),ti.Vector([0.377755284,-0.457885206,0]),ti.Vector([-0.0801299289,-0.457885206,-0.457885206]),ti.Vector([-0.0801299289,0.228942633,-0.457885206]),ti.Vector([-0.538015127,0.228942633,0]),ti.Vector([-0.0801299289,-0.228942603,-0.610513628]),ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([0.377755284,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.457885206]),ti.Vector([0.530383706,-0.228942603,0]),ti.Vector([-0.0801299289,-0.228942603,-0.610513628]),ti.Vector([-0.0801299289,0.228942633,-0.427359521]),ti.Vector([-0.507489443,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.457885206]),ti.Vector([-0.538015127,0.228942633,0]),ti.Vector([0.3472296,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.427359521]),ti.Vector([0.377755284,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.457885206]),ti.Vector([-0.0801299289,-0.457885206,-0.457885206]),ti.Vector([-0.538015127,-0.457885206,0]),ti.Vector([0.377755284,-0.457885206,0]),ti.Vector([-0.0801299289,-0.457885206,0.457885206]),ti.Vector([-0.568540871,0.114471316,0]),ti.Vector([-0.538015127,0.183154047,0]),ti.Vector([-0.904323339,0.0457885265,0]),ti.Vector([-0.995900333,0.0457885265,0]),ti.Vector([-0.538015127,0.183154047,0]),ti.Vector([-0.568540871,0.114471316,0]),ti.Vector([-0.995900333,0.0457885265,0]),ti.Vector([-0.904323339,0.0457885265,0]),ti.Vector([-0.904323339,0.0457885265,0]),ti.Vector([-0.995900333,0.0457885265,0]),ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([-0.660117865,-0.320519626,0]),ti.Vector([-0.995900333,0.0457885265,0]),ti.Vector([-0.904323339,0.0457885265,0]),ti.Vector([-0.660117865,-0.320519626,0]),ti.Vector([-0.690643549,-0.228942603,0]),ti.Vector([0.438806653,-0.0686827898,0]),ti.Vector([0.438806653,-0.320519626,0]),ti.Vector([0.744063497,0.228942633,0]),ti.Vector([0.927217543,0.228942633,0]),ti.Vector([0.438806653,-0.320519626,0]),ti.Vector([0.438806653,-0.0686827898,0]),ti.Vector([0.927217543,0.228942633,0]),ti.Vector([0.744063497,0.228942633,0]),ti.Vector([0.744063497,0.228942633,0]),ti.Vector([0.927217543,0.228942633,0]),ti.Vector([0.774589121,0.228942633,0]),ti.Vector([0.896691918,0.228942633,0]),ti.Vector([0.927217543,0.228942633,0]),ti.Vector([0.744063497,0.228942633,0]),ti.Vector([0.896691918,0.228942633,0]),ti.Vector([0.774589121,0.228942633,0]),ti.Vector([0.301441103,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.381571025]),ti.Vector([0.3472296,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.427359521]),ti.Vector([-0.0801299289,0.228942633,0.381571025]),ti.Vector([0.301441103,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.427359521]),ti.Vector([0.3472296,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.381571025]),ti.Vector([-0.461700946,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.427359521]),ti.Vector([-0.507489443,0.228942633,0]),ti.Vector([-0.461700946,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.381571025]),ti.Vector([-0.507489443,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.427359521]),ti.Vector([-0.0190785639,0.320519626,0]),ti.Vector([-0.0801299289,0.320519626,-0.061051365]),ti.Vector([0.316703916,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,-0.396833837]),ti.Vector([-0.0801299289,0.320519626,0.061051365]),ti.Vector([-0.0190785639,0.320519626,0]),ti.Vector([-0.0801299289,0.228942633,0.396833837]),ti.Vector([0.316703916,0.228942633,0]),ti.Vector([-0.0801299289,0.320519626,-0.061051365]),ti.Vector([-0.14118129,0.320519626,0]),ti.Vector([-0.0801299289,0.228942633,-0.396833837]),ti.Vector([-0.476963758,0.228942633,0]),ti.Vector([-0.14118129,0.320519626,0]),ti.Vector([-0.0801299289,0.320519626,0.061051365]),ti.Vector([-0.476963758,0.228942633,0]),ti.Vector([-0.0801299289,0.228942633,0.396833837]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0190785639,0.320519626,0]),ti.Vector([-0.0801299289,0.320519626,-0.061051365]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.320519626,0.061051365]),ti.Vector([-0.0190785639,0.320519626,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.320519626,-0.061051365]),ti.Vector([-0.14118129,0.320519626,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.0801299289,0.457885206,0]),ti.Vector([-0.14118129,0.320519626,0]),ti.Vector([-0.0801299289,0.320519626,0.061051365])]
    teaPotFaceID = [ti.Vector([1,2,4]),ti.Vector([5,8,6]),ti.Vector([9,12,10]),ti.Vector([13,16,14]),ti.Vector([17,20,18]),ti.Vector([21,24,22]),ti.Vector([25,28,26]),ti.Vector([29,32,30]),ti.Vector([33,36,34]),ti.Vector([37,40,38]),ti.Vector([41,44,42]),ti.Vector([45,48,46]),ti.Vector([49,52,50]),ti.Vector([53,56,54]),ti.Vector([57,60,58]),ti.Vector([61,64,62]),ti.Vector([65,68,66]),ti.Vector([69,72,70]),ti.Vector([73,76,74]),ti.Vector([77,79,78]),ti.Vector([81,83,82]),ti.Vector([85,88,86]),ti.Vector([89,92,90]),ti.Vector([93,96,94]),ti.Vector([97,100,98]),ti.Vector([101,104,102]),ti.Vector([105,108,106]),ti.Vector([109,112,110]),ti.Vector([113,116,114]),ti.Vector([117,119,118]),ti.Vector([121,123,122]),ti.Vector([125,127,126]),ti.Vector([129,131,130]),ti.Vector([130,131,132]),ti.Vector([126,127,128]),ti.Vector([122,123,124]),ti.Vector([118,119,120]),ti.Vector([116,113,115]),ti.Vector([112,109,111]),ti.Vector([108,105,107]),ti.Vector([104,101,103]),ti.Vector([100,97,99]),ti.Vector([96,93,95]),ti.Vector([92,89,91]),ti.Vector([88,85,87]),ti.Vector([82,83,84]),ti.Vector([78,79,80]),ti.Vector([76,73,75]),ti.Vector([72,69,71]),ti.Vector([68,65,67]),ti.Vector([64,61,63]),ti.Vector([60,57,59]),ti.Vector([56,53,55]),ti.Vector([52,49,51]),ti.Vector([48,45,47]),ti.Vector([44,41,43]),ti.Vector([40,37,39]),ti.Vector([36,33,35]),ti.Vector([32,29,31]),ti.Vector([28,25,27]),ti.Vector([24,21,23]),ti.Vector([20,17,19]),ti.Vector([16,13,15]),ti.Vector([12,9,11]),ti.Vector([8,5,7]),ti.Vector([4,1,3])]
    teaPot.FillTriangles(teaPotPtPos,teaPotFaceID,1,ti.Vector([0.0, 0.2, 0.85]))
    scene.add(teaPot)
    


#主要循环
@ti.kernel
def ShootRay():
    for i,j in pixels:
        u = (i+ti.random())/width
        v = (j+ti.random())/height
        color = ti.Vector([0.0,0.0,0.0])
        for n in range(SPP):
            ray = camera.getRay(u,v)
            ray.dir = ray.dir
            ray.start = ray.start
            color += CalRayColor(ray)
        color/=SPP
        pixels[i,j] += color
            
      
#每一条path,照搬Taichi公开课
@ti.func
def CalRayColor(ray):
    #为了让TaiChi实现递归, 使用类似Houdini Solver的方法 - While不停修改同一个变量
    colorBuffer = ti.Vector([0.0, 0.0, 0.0])
    brightness = ti.Vector([1.0, 1.0, 1.0])
    scattered_origin = ray.start
    scattered_direction = ray.dir
    
    for n in range(depth):
        if ti.random()>p_RR:    #使用俄罗斯轮盘更快收敛
            break
        is_hit, hit_point, hit_point_normal, front_face, material, color, root = scene.hit(Ray(scattered_origin,scattered_direction))
        if is_hit:
            if material == 0:#光源直接返回亮度和颜色
                colorBuffer = color * brightness    #只有打到灯, brightness的颜色才会传递给buffer
                break
            else:
                if material == 1:#完全漫反射
                    target = hit_point + hit_point_normal
                    target += rand_sphere_point()   #因为上面加上了normal, 所以这个球不会随机到物体里面. 而且由于这个是个球, 加上后就不用乘上cos来算光照亮度了
                    scattered_direction = target - hit_point
                    scattered_origin = hit_point
                    brightness *= color
                elif material == 2 or material == 4:#镜面材质/磨砂材质
                    fuzz = 0.0
                    if material == 4:
                        fuzz = 0.2
                    scattered_direction = reflect(scattered_direction.normalized(),
                                                  hit_point_normal)
                    scattered_direction += fuzz * rand_sphere_point()
                    scattered_origin = hit_point
                    if scattered_direction.dot(hit_point_normal) < 0:
                        break
                    else:
                        brightness *= color
                elif material == 3: #介电质(透明物体)
                    refraction_ratio = 1.5
                    if front_face:
                        refraction_ratio = 1 / refraction_ratio
                    cos_theta = min(-scattered_direction.normalized().dot(hit_point_normal), 1.0)
                    sin_theta = ti.sqrt(1 - cos_theta * cos_theta)
                    # total internal reflection
                    if refraction_ratio * sin_theta > 1.0 or reflectance(cos_theta, refraction_ratio) > ti.random():
                        scattered_direction = reflect(scattered_direction.normalized(), hit_point_normal)
                    else:
                        scattered_direction = refract(scattered_direction.normalized(), hit_point_normal, refraction_ratio)
                    scattered_origin = hit_point
                    brightness *= color
                
                brightness /= p_RR  #除以RR来弥补能量缺失
    return colorBuffer

##移动摄像机方法
def MoveCamera():
    pressed = False
    for e in gui.get_events(gui.PRESS):
        if(e.key == gui.ESCAPE):
            gui.running = False
        elif(e.key == "s"):
            camera.lookfrom[None] += ti.Vector([0.0,0.0,-1.0])*speed.value
            camera.lookat[None] += ti.Vector([0.0,0.0,-1.0])*speed.value
            pressed = True
        elif(e.key == "w"):
            camera.lookfrom[None] += ti.Vector([0.0,0.0,1.0])*speed.value
            camera.lookat[None] += ti.Vector([0.0,0.0,1.0])*speed.value
            pressed = True
        elif(e.key == "a"):
            camera.lookfrom[None] += ti.Vector([1.0,0.0,0.0])*speed.value
            camera.lookat[None] += ti.Vector([1.0,0.0,0.0])*speed.value
            pressed = True
        elif(e.key == "d"):
            camera.lookfrom[None] += ti.Vector([-1.0,0.0,0.0])*speed.value
            camera.lookat[None] += ti.Vector([-1.0,0.0,0.0])*speed.value
            pressed = True
        elif(e.key == "q"):
            camera.lookfrom[None] += ti.Vector([0.0,1.0,0.0])*speed.value
            camera.lookat[None] += ti.Vector([0.0,1.0,0.0])*speed.value
            pressed = True
        elif(e.key == "e"):
            camera.lookfrom[None] += ti.Vector([0.0,-1.0,0.0])*speed.value
            camera.lookat[None] += ti.Vector([0.0,-1.0,0.0])*speed.value
            pressed = True
    if(pressed):
        camera.reset()
        pixels.fill(0)
        cnt[None] = 0


#定义屏幕参数
aspectRatio = 1 
width = 720
height = int(width/aspectRatio)
pixels = ti.Vector.field(3,ti.f32,shape=(width,height))

#定义摄像机参数
FOV = PI/3.0
camera = Camera(ti.Vector([0,1,-5]),ti.Vector([0,1,-1]),ti.Vector([0,1,0]),FOV,aspectRatio)
SPP = 30
depth = 15
p_RR = 0.9

#定义场景
scene = Hittable_list()
AddObject(scene) 

#创建画布
gui = ti.GUI("Ray Tracing", res=(width,height))
pixels.fill(0)
cnt = ti.field(dtype=ti.i32,shape=())
cnt[None] = 0

speed = gui.slider('speed', 0, 1.0, step=0.05)
speed.value = 0.2


while(1):
    MoveCamera() 
    ShootRay()
    cnt[None] += 1
    gui.set_image(np.sqrt(pixels.to_numpy()/cnt[None]))
    gui.show()    

    
        


你可以在while 1循环前面运行一次shootray函数,并记一下时间,差不多就是你的编译时间。
之所以会有这么长的编译时间,是因为你的写法把所有三角形硬编码进了代码里,导致存在多少个三角形,就会编译多少次三角形求交的代码,这是static for存在的问题,循环多少次就会编译多少代码,换成range for之类的就行了。
如果想要动态增减物体,我目前想到的最好是用struct储存物体数据,然后再维护一下场内物体数量。
具体实现可以参考一下我的相对论X光线追踪,或者看看hackathon第一名烧风的代码。

2 个赞

好的, 感谢大佬

感谢大佬解答, 用了读取obj文件的格式后快了很多
但还是有个问题, 就是不用static for就会报错,
比如下面这个光栅的代码, 我想遍历所有的三角形, 并在遍历三角的时候使用kernel

    def drawScene(self):
        for modelIndex in (range(len(self.scene))):
            self.drawModel(modelIndex)

    @ti.kernel
    def drawModel(self,modelIndex:ti.template()):
        for triangleIndex in ti.static(range(len(self.scene[modelIndex].faceList))):
            triangle = self.scene[modelIndex].faceList[triangleIndex]
            ****** 把三角形画到屏幕上*****

但如果把static去掉就会报错, 看其他帖说是因为kernel里面不支持动态index, 那不用static就用不了list, 用了static又特别慢, 好像死循环了 :melting_face::
File “d:\VFX\computerGraphic\TaiChi\taichi_rasterization\new Arch\functions.py”, line 68, in drawModel:
triangle = self.scene[modelIndex].faceList[triangleIndex]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Traceback (most recent call last):
File “D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\ast\ast_transformer_utils.py”, line 25, in call
return method(ctx, node)
File “D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\ast\ast_transformer.py”, line 238, in build_Subscript
node.ptr = impl.subscript(ctx.ast_builder, node.value.ptr,
File “D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\util.py”, line 301, in wrapped
return func(*args, **kwargs)
File “D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\impl.py”, line 164, in subscript
return value.getitem(_indices)
TypeError: list indices must be integers or slices, not Expr

换成taichi的field就可以了,Python的list只能static for

大佬太感谢了, 都忘记了有Struct field了 :cold_sweat:

又打扰大佬了, 但代码似乎在循环中又出现了问题… 完全没有头绪怎么解决:

这是最后文件读取出来的数据:

    self.face = ti.Vector.field(3,ti.i32,shape=(facecount,))
    self.vert = ti.Struct.field({
                            "vertPos": ti.math.vec4,
                            "normal": ti.math.vec3,
                            "texcoord" : ti.math.vec3
                        }, shape=(vertcount,))

接着我想要渲染场景, 于是遍历场景中的物体, 调用drawModelFieldNew方法:

  def drawScene(self):
      for modelIndex in (range(len(self.scene))):
          self.drawModelFieldNew(modelIndex)
@ti.kernel
def drawModelFieldNew(self,modelIndex:ti.template()):
    obj = self.scene[modelIndex]
    for face in self.scene[modelIndex].face:
        pos0 = get2DPos(obj.vert[face[0]].vertPos[0,0:]*ti.Vector([500,500,500,1])-ti.Vector([0,0,600,0]),self.M_mvp)

但这个方法的pos0部分出现问题了:

taichi.lang.exception.TaichiCompilationError:
File "d:\VFX\computerGraphic\TaiChi\taichi_rasterization\new Arch\functions.py", line 161, in drawModelFieldNew:
            pos0 = get2DPos(obj.vert[face[0]].vertPos[0,0:]*ti.Vector([500,500,5
                                     ^^^^^^^
Traceback (most recent call last):
  File "D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\ast\ast_transformer_utils.py", line 25, in __call__
    return method(ctx, node)
  File "D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\ast\ast_transformer.py", line 238, in build_Subscript
    node.ptr = impl.subscript(ctx.ast_builder, node.value.ptr,
  File "D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\util.py", line 301, in wrapped
    return func(*args, **kwargs)
  File "D:\Software\Anaconda3\envs\TaiChiNew\lib\site-packages\taichi\lang\impl.py", line 248, in subscript
    assert value.is_tensor()
AssertionError

明明.face是一个vector场啊, for出来的face应该是一个vector, 然后我用[0]获取点index应该也没毛病啊. 为什么会出现assertion Error呢?

解决了, 自己for了一遍才发现他循环出来的结果竟然是一个int??
之前没发现以为一直找不到在for循环里print的方法, 但又单独写了一个print的for就能打印出来了?