예제 #1
0
def cal_look_at_mat(ro, ta, roll):
    ww = (ta - ro).normalized()
    uu = ts.cross(ww, ts.vec3(ts.sin(roll), ts.cos(roll), 0.0)).normalized()
    vv = ts.cross(uu, ww).normalized()

    return ts.mat([uu[0], vv[0], ww[0]], [uu[1], vv[1], ww[1]],
                  [uu[2], vv[2], ww[2]])
예제 #2
0
    def do_render(self):
        model = self.model
        scene = model.scene
        L2W = model.L2W
        a = scene.camera.untrans_pos(L2W @ self.vertex(0).pos)
        b = scene.camera.untrans_pos(L2W @ self.vertex(1).pos)
        c = scene.camera.untrans_pos(L2W @ self.vertex(2).pos)
        A = scene.uncook_coor(a)
        B = scene.uncook_coor(b)
        C = scene.uncook_coor(c)
        B_A = B - A
        C_B = C - B
        A_C = A - C
        ilB_A = 1 / ts.length(B_A)
        ilC_B = 1 / ts.length(C_B)
        ilA_C = 1 / ts.length(A_C)
        B_A *= ilB_A
        C_B *= ilC_B
        A_C *= ilA_C
        BxA = ts.cross(B, A) * ilB_A
        CxB = ts.cross(C, B) * ilC_B
        AxC = ts.cross(A, C) * ilA_C
        normal = ts.normalize(ts.cross(a - c, a - b))
        light_dir = scene.camera.untrans_dir(scene.light_dir[None])
        pos = (a + b + c) / 3
        color = scene.opt.render_func(pos, normal, ts.vec3(0.0), light_dir)
        color = scene.opt.pre_process(color)

        Ak = 1 / (ts.cross(A, C_B) + CxB)
        Bk = 1 / (ts.cross(B, A_C) + AxC)
        Ck = 1 / (ts.cross(C, B_A) + BxA)

        W = 1
        M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W))
        for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
            AB = ts.cross(X, B_A) + BxA
            BC = ts.cross(X, C_B) + CxB
            CA = ts.cross(X, A_C) + AxC
            if AB <= 0 and BC <= 0 and CA <= 0:
                zindex = pos.z  #(Ak * a.z * BC + Bk * b.z * CA + Ck * c.z * AB)
                zstep = zindex - ti.atomic_max(scene.zbuf[X], zindex)
                if zstep >= 0:
                    scene.img[X] = color
예제 #3
0
 def do_render(self, scene):
     W = 1
     A = scene.uncook_coor(scene.camera.untrans_pos(self.a))
     B = scene.uncook_coor(scene.camera.untrans_pos(self.b))
     M, N = int(ti.floor(min(A, B) - W)), int(ti.ceil(max(A, B) + W))
     for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
         P = B - A
         udf = (ts.cross(X, P) + ts.cross(B, A))**2 / P.norm_sqr()
         XoP = ts.dot(X, P)
         AoB = ts.dot(A, B)
         if XoP > B.norm_sqr() - AoB:
             udf = (B - X).norm_sqr()
         elif XoP < AoB - A.norm_sqr():
             udf = (A - X).norm_sqr()
         if udf < 0:
             scene.img[X] = ts.vec3(1.0)
         elif udf < W**2:
             t = ts.smoothstep(udf, 0, W**2)
             ti.atomic_min(scene.img[X], ts.vec3(t))
예제 #4
0
 def render(self, renderer):
     width = 1
     A = self.begin
     B = self.end
     A, B = min(A, B), max(A, B)
     mold = tg.normalize(B - A)
     for X in ti.grouped(
             ti.ndrange((A.x - width, B.x + width),
                        (A.y - width, B.y + width))):
         udf = abs(tg.cross(X - A, mold))
         renderer.image[int(X)] = tg.vec3(tg.smoothstep(udf, width, 0))
    def get_ray_direction(self, orig, view_dir, x: float, y: float):
        ''' Compute ray direction for perspecive camera.

        Args:
            orig (tl.vec3): Camera position
            view_dir (tl.vec3): View direction, normalized
            x (float): Image coordinate in [0,1] along width
            y (float): Image coordinate in [0,1] along height

        Returns:
            tl.vec3: Ray direction from camera origin to pixel specified through `x` and `y`
        '''
        u = x - 0.5
        v = y - 0.5

        up = tl.vec3(0.0, 1.0, 0.0)
        right = tl.cross(view_dir, up).normalized()
        up = tl.cross(right, view_dir).normalized()
        near_h = 2.0 * ti.tan(self.fov_rad) * self.near
        near_w = near_h * self.aspect
        near_m = orig + self.near * view_dir
        near_pos = near_m + u * near_w * right + v * near_h * up

        return (near_pos - orig).normalized()
예제 #6
0
    def do_render(self, scene):
        a = scene.camera.untrans_pos(self.a)
        b = scene.camera.untrans_pos(self.b)
        c = scene.camera.untrans_pos(self.c)
        A = scene.uncook_coor(a)
        B = scene.uncook_coor(b)
        C = scene.uncook_coor(c)
        B_A = B - A
        C_B = C - B
        A_C = A - C
        ilB_A = 1 / ts.length(B_A)
        ilC_B = 1 / ts.length(C_B)
        ilA_C = 1 / ts.length(A_C)
        B_A *= ilB_A
        C_B *= ilC_B
        A_C *= ilA_C
        BxA = ts.cross(B, A) * ilB_A
        CxB = ts.cross(C, B) * ilC_B
        AxC = ts.cross(A, C) * ilA_C
        normal = ts.normalize(ts.cross(a - c, a - b))
        light_dir = scene.camera.untrans_dir(scene.light_dir[None])
        pos = (a + b + c) * (1 / 3)
        dir = ts.vec3(0.0)
        color = scene.opt.render_func(pos, normal, dir, light_dir)
        color = scene.opt.pre_process(color)

        W = 0.4
        M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W))
        for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
            AB = ts.cross(X, B_A) + BxA
            BC = ts.cross(X, C_B) + CxB
            CA = ts.cross(X, A_C) + AxC
            udf = max(AB, BC, CA)
            if udf < 0:
                scene.img[X] = color
            elif udf < W:
                t = ts.smoothstep(udf, W, 0)
                ti.atomic_max(scene.img[X], t * color)
예제 #7
0
 def nrm(self):
     posa, posb, posc = self.pos
     normal = ts.cross(posa - posc, posa - posb)
     return normal, normal, normal
예제 #8
0
def render_triangle(model, camera, face):
    scene = model.scene
    L2W = model.L2W
    _1 = ti.static(min(1, model.faces.m - 1))
    _2 = ti.static(min(2, model.faces.m - 1))
    ia, ib, ic = model.vi[face[0, 0]], model.vi[face[1, 0]], model.vi[face[2,
                                                                           0]]
    ta, tb, tc = model.vt[face[0, _1]], model.vt[face[1,
                                                      _1]], model.vt[face[2,
                                                                          _1]]
    na, nb, nc = model.vn[face[0, _2]], model.vn[face[1,
                                                      _2]], model.vn[face[2,
                                                                          _2]]
    a = camera.untrans_pos(L2W @ ia)
    b = camera.untrans_pos(L2W @ ib)
    c = camera.untrans_pos(L2W @ ic)

    # NOTE: the normal computation indicates that # a front-facing face should
    # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW);
    # this is to be compatible with obj model loading.
    normal = ts.normalize(ts.cross(a - b, a - c))
    pos = (a + b + c) / 3
    view_pos = (a + b + c) / 3
    if ti.static(camera.type == camera.ORTHO):
        view_pos = ts.vec3(0.0, 0.0, 1.0)
    if ts.dot(view_pos, normal) <= 0:
        # shading
        color = ts.vec3(0.0)
        for light in ti.static(scene.lights):
            color += scene.opt.render_func(pos, normal, ts.vec3(0.0), light)
        color = scene.opt.pre_process(color)
        A = camera.uncook(a)
        B = camera.uncook(b)
        C = camera.uncook(c)
        scr_norm = 1 / ts.cross(A - C, B - A)
        B_A = (B - A) * scr_norm
        C_B = (C - B) * scr_norm
        A_C = (A - C) * scr_norm

        W = 1
        # screen space bounding box
        M, N = int(ti.floor(min(A, B, C) - W)), int(ti.ceil(max(A, B, C) + W))
        M.x, N.x = min(max(M.x, 0),
                       camera.img.shape[0]), min(max(N.x, 0),
                                                 camera.img.shape[1])
        M.y, N.y = min(max(M.y, 0),
                       camera.img.shape[0]), min(max(N.y, 0),
                                                 camera.img.shape[1])
        for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
            # barycentric coordinates using the area method
            X_A = X - A
            w_C = ts.cross(B_A, X_A)
            w_B = ts.cross(A_C, X_A)
            w_A = 1 - w_C - w_B
            # draw
            in_screen = w_A >= 0 and w_B >= 0 and w_C >= 0 and 0 < X[
                0] < camera.img.shape[0] and 0 < X[1] < camera.img.shape[1]
            if not in_screen:
                continue
            zindex = 1 / (a.z * w_A + b.z * w_B + c.z * w_C)
            if zindex < ti.atomic_max(camera.zbuf[X], zindex):
                continue

            coor = (ta * w_A + tb * w_B + tc * w_C)
            camera.img[X] = color * model.texSample(coor)
예제 #9
0
def render_triangle(model, camera, face):
    posa, posb, posc = face.pos   # Position
    texa, texb, texc = face.tex   # TexCoord
    nrma, nrmb, nrmc = face.nrm   # Normal

    pos_center = (posa + posb + posc) / 3
    if ti.static(camera.type == camera.ORTHO):
        pos_center = ts.vec3(0.0, 0.0, 1.0)

    # NOTE: the normal computation indicates that a front-facing face should
    # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW);
    # this is to be compatible with obj model loading.
    if ts.dot(pos_center, ts.cross(posa - posc, posa - posb)) >= 0:
        tan, bitan = compute_tangent(posb - posa, posc - posa, texb - texa, texc - texa)  # TODO: node-ize this

        clra = [posa, texa, nrma]  # TODO: interpolate tan and bitan? merge with nrm?
        clrb = [posb, texb, nrmb]
        clrc = [posc, texc, nrmc]

        A = camera.uncook(posa)
        B = camera.uncook(posb)
        C = camera.uncook(posc)
        scr_norm = ts.cross(A - C, B - A)
        if scr_norm != 0:  # degenerate to 'line' if zero
            B_A = (B - A) / scr_norm
            A_C = (A - C) / scr_norm

            shake = ts.vec2(0.0)
            if ti.static(camera.fb.n_taa):
                for i, s in ti.static(enumerate(map(ti.Vector, TAA_SHAKES[:camera.fb.n_taa]))):
                    if camera.fb.itaa[None] == i:
                        shake = s * 0.5

            # screen space bounding box
            M = int(ti.floor(min(A, B, C) - 1))
            N = int(ti.ceil(max(A, B, C) + 1))
            M = ts.clamp(M, 0, ti.Vector(camera.fb.res))
            N = ts.clamp(N, 0, ti.Vector(camera.fb.res))
            for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
                # barycentric coordinates using the area method
                X_A = X - A + shake
                w_C = ts.cross(B_A, X_A)
                w_B = ts.cross(A_C, X_A)
                w_A = 1 - w_C - w_B

                # draw
                eps = ti.get_rel_eps() * 0.2
                is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps
                if not is_inside:
                    continue

                # https://gitee.com/zxtree2006/tinyrenderer/blob/master/our_gl.cpp
                if ti.static(camera.type != camera.ORTHO):
                    bclip = ts.vec3(w_A / posa.z, w_B / posb.z, w_C / posc.z)
                    bclip /= bclip.x + bclip.y + bclip.z
                    w_A, w_B, w_C = bclip

                depth = (posa.z * w_A + posb.z * w_B + posc.z * w_C)
                if camera.fb.atomic_depth(X, depth):
                    continue

                posx, texx, nrmx = [a * w_A + b * w_B + c * w_C for a, b, c in zip(clra, clrb, clrc)]
                color = ti.static(model.material.pixel_shader(model, posx, texx, nrmx, tan, bitan))
                if ti.static(isinstance(color, dict)):
                    camera.fb.update(X, color)
                else:
                    camera.fb.update(X, dict(img=color))
예제 #10
0
def render_triangle(model, camera, face):
    scene = model.scene
    L2W = model.L2W
    posa, posb, posc = model.pos[face[0, 0]], model.pos[face[1, 0]], model.pos[
        face[2, 0]]
    texa, texb, texc = model.tex[face[0, 1]], model.tex[face[1, 1]], model.tex[
        face[2, 1]]
    nrma, nrmb, nrmc = model.nrm[face[0, 2]], model.nrm[face[1, 2]], model.nrm[
        face[2, 2]]
    posa = camera.untrans_pos(L2W @ posa)
    posb = camera.untrans_pos(L2W @ posb)
    posc = camera.untrans_pos(L2W @ posc)
    nrma = camera.untrans_dir(L2W.matrix @ nrma)
    nrmb = camera.untrans_dir(L2W.matrix @ nrmb)
    nrmc = camera.untrans_dir(L2W.matrix @ nrmc)

    pos_center = (posa + posb + posc) / 3
    if ti.static(camera.type == camera.ORTHO):
        pos_center = ts.vec3(0.0, 0.0, 1.0)

    dpab = posa - posb
    dpac = posa - posc
    dtab = texa - texb
    dtac = texa - texc

    normal = ts.cross(dpab, dpac)
    tan, bitan = compute_tangent(-dpab, -dpac, -dtab, -dtac)

    # NOTE: the normal computation indicates that a front-facing face should
    # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW);
    # this is to be compatible with obj model loading.
    if ts.dot(pos_center, normal) <= 0:

        clra = model.vertex_shader(posa, texa, nrma, tan, bitan)
        clrb = model.vertex_shader(posb, texb, nrmb, tan, bitan)
        clrc = model.vertex_shader(posc, texc, nrmc, tan, bitan)

        A = camera.uncook(posa)
        B = camera.uncook(posb)
        C = camera.uncook(posc)
        scr_norm = 1 / ts.cross(A - C, B - A)
        B_A = (B - A) * scr_norm
        C_B = (C - B) * scr_norm
        A_C = (A - C) * scr_norm

        # screen space bounding box
        M = int(ti.floor(min(A, B, C) - 1))
        N = int(ti.ceil(max(A, B, C) + 1))
        M = ts.clamp(M, 0, ti.Vector(camera.res))
        N = ts.clamp(N, 0, ti.Vector(camera.res))
        for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
            # barycentric coordinates using the area method
            X_A = X - A
            w_C = ts.cross(B_A, X_A)
            w_B = ts.cross(A_C, X_A)
            w_A = 1 - w_C - w_B
            # draw
            eps = ti.get_rel_eps() * 0.2
            is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps
            if not is_inside:
                continue
            zindex = 1 / (posa.z * w_A + posb.z * w_B + posc.z * w_C)
            if zindex < ti.atomic_max(camera.fb['idepth'][X], zindex):
                continue

            clr = [
                a * w_A + b * w_B + c * w_C
                for a, b, c in zip(clra, clrb, clrc)
            ]
            camera.fb.update(X, model.pixel_shader(*clr))
예제 #11
0
def render_triangle(model, camera, face):
    scene = model.scene
    L2C = model.L2C[None]  # Local to Camera, i.e. ModelView in OpenGL
    posa, posb, posc = face.pos
    texa, texb, texc = face.tex
    nrma, nrmb, nrmc = face.nrm
    posa = (L2C @ ts.vec4(posa, 1)).xyz
    posb = (L2C @ ts.vec4(posb, 1)).xyz
    posc = (L2C @ ts.vec4(posc, 1)).xyz
    nrma = (L2C @ ts.vec4(nrma, 0)).xyz
    nrmb = (L2C @ ts.vec4(nrmb, 0)).xyz
    nrmc = (L2C @ ts.vec4(nrmc, 0)).xyz

    pos_center = (posa + posb + posc) / 3
    if ti.static(camera.type == camera.ORTHO):
        pos_center = ts.vec3(0.0, 0.0, 1.0)

    dpab = posa - posb
    dpac = posa - posc
    dtab = texa - texb
    dtac = texa - texc

    normal = ts.cross(dpab, dpac)

    # NOTE: the normal computation indicates that a front-facing face should
    # be COUNTER-CLOCKWISE, i.e., glFrontFace(GL_CCW);
    # this is to be compatible with obj model loading.
    if ts.dot(pos_center, normal) <= 0:
        tan, bitan = compute_tangent(-dpab, -dpac, -dtab,
                                     -dtac)  # TODO: node-ize this

        clra = model.vertex_shader(
            posa, texa, nrma, tan,
            bitan)  # TODO: interpolate tan and bitan? merge with nrm?
        clrb = model.vertex_shader(posb, texb, nrmb, tan, bitan)
        clrc = model.vertex_shader(posc, texc, nrmc, tan, bitan)

        A = camera.uncook(posa)
        B = camera.uncook(posb)
        C = camera.uncook(posc)
        scr_norm = ts.cross(A - C, B - A)
        if scr_norm != 0:  # degenerate to 'line' if zero
            B_A = (B - A) / scr_norm
            A_C = (A - C) / scr_norm

            shake = ts.vec2(0.0)
            if ti.static(camera.fb.n_taa):
                for i, s in ti.static(
                        enumerate(map(ti.Vector,
                                      TAA_SHAKES[:camera.fb.n_taa]))):
                    if camera.fb.itaa[None] == i:
                        shake = s * 0.5

            # screen space bounding box
            M = int(ti.floor(min(A, B, C) - 1))
            N = int(ti.ceil(max(A, B, C) + 1))
            M = ts.clamp(M, 0, ti.Vector(camera.fb.res))
            N = ts.clamp(N, 0, ti.Vector(camera.fb.res))
            for X in ti.grouped(ti.ndrange((M.x, N.x), (M.y, N.y))):
                # barycentric coordinates using the area method
                X_A = X - A + shake
                w_C = ts.cross(B_A, X_A)
                w_B = ts.cross(A_C, X_A)
                w_A = 1 - w_C - w_B

                # draw
                eps = ti.get_rel_eps() * 0.2
                is_inside = w_A >= -eps and w_B >= -eps and w_C >= -eps
                if not is_inside:
                    continue

                # https://gitee.com/zxtree2006/tinyrenderer/blob/master/our_gl.cpp
                if ti.static(camera.type != camera.ORTHO):
                    bclip = ts.vec3(w_A / posa.z, w_B / posb.z, w_C / posc.z)
                    bclip /= bclip.x + bclip.y + bclip.z
                    w_A, w_B, w_C = bclip

                depth = (posa.z * w_A + posb.z * w_B + posc.z * w_C)
                if camera.fb.atomic_depth(X, depth):
                    continue

                clr = [
                    a * w_A + b * w_B + c * w_C
                    for a, b, c in zip(clra, clrb, clrc)
                ]
                camera.fb.update(X, model.pixel_shader(*clr))