Homework0 : sdf测试

论坛里大神太多了,给大家整点简单的。
包含简单的光照渲染和soft-min,debug了半天lookat函数也没搞对(明明在我的renderer里是对的,不知道是哪里错了,从Ray tracing in One weekend里抄了一份光线生成的代码过来。
代码在这里
https://paste.ubuntu.com/p/JvmP4Wbg59/

#! /usr/bin/env python
#! coding:utf-8
import taichi as ti
import numpy as np
import math
import sys
ti.init(arch=ti.gpu)

x = 800
y = 400
size = ti.Vector([x, y])
img = ti.Vector(3, dt=ti.f32, shape=(x, y))

MAX_MARCHING_STEPS = 255
MIN_DIST = 0.0
MAX_DIST = 100.0
EPSILON = 0.0001
ambient = ti.Vector([0.5, 0.5, 0.5])
light_intensity = ti.Vector([0.7, 0.7, 0.7])
k_d = ti.Vector([0.7, 0.2, 0.2])
k_s = ti.Vector([1.0, 1.0, 1.0])
time = ti.var(dt=ti.f32, shape=())


def normalize(n):
    s = 1.0 / math.sqrt(np.dot(n, n))
    return n * s


lookfrom = np.array([8.5, 4.5, 6.0], dtype=np.float32)
lookat = np.array([0.0, 0.0, 0.0], dtype=np.float32)
up = np.array([0.0, 1.0, 0.0], dtype=np.float32)
view_dir = lookfrom - lookat

w = normalize(view_dir)
u = normalize(np.cross(up, w))
v = np.cross(w, u)
FOV = 45.0
aspect = y / x
fov_theta = FOV * math.pi / 180
half_width = math.tan(fov_theta / 2)
half_height = half_width * aspect


origin = ti.Vector(3, dt=ti.f32, shape=1)
lower_left_corner = ti.Vector(3, dt=ti.f32, shape=1)
horizontal = ti.Vector(3, dt=ti.f32, shape=1)
vertical = ti.Vector(3, dt=ti.f32, shape=1)

origin[0] = lookfrom
lower_left_corner[0] = \
    lookfrom - (half_width * u + half_height * v + w)
horizontal[0] = (u * 2 * half_width)
vertical[0] = (v * 2 * half_height)


@ti.func
def sphereSDF(p):  # sample point
    return p.norm() - 1.0


@ti.func
def cubeSDF(p):
    d = ti.abs(p) - ti.Vector([1.0, 1.0, 1.0])
    insideDistance = min(max(d[0], max(d[1], d[2])), 0.0)
    outsideDistance = (ti.max(d, 0.0)).norm()
    return insideDistance + outsideDistance


# https://www.iquilezles.org/www/articles/smin/smin.htm
@ti.func
def smooth_min(lhs, rhs, k):  # float,float,float
    h = max(k-abs(lhs-rhs), 0.0)/k
    return min(lhs, rhs) - h*h*k*(1.0/4.0)


@ti.func
def unionSDF(distanceA, distanceB):
    return smooth_min(distanceA, distanceB, 0.1)


@ti.func
def sceneSDF(p):  # sample point
    return unionSDF(sphereSDF(p / 1.2) * 1.2, cubeSDF(p + ti.Vector([0.0, 2 * ti.sin(time[None]), 0.0])))


@ti.func
def unit_vector(v):
    k = 1 / (ti.sqrt(v.dot(v)))
    return k * v


@ti.func
def shortest_distance_marching(origin, direction, start, end):
    depth = start  # no parrallel
    res = 0.0
    for i in range(MAX_MARCHING_STEPS):
        dist = sceneSDF(origin + depth * direction)
        if(dist < EPSILON):
            res = depth
            break
        else:
            depth += dist
            if(depth >= end):
                res = end
                break
    return res


# @ti.func
# def get_ray_direction(fov, size, fragCoords):  # size ti.Vector(2)
#     half_height = ti.tan((fov / 180.0 * math.pi)/2.0)
#     half_width = half_height * 2.0
#     w = ti.Vector([0.0, 0.0, 1.0])
#     u = fragCoords[0] / size[0]
#     v = fragCoords[1] / size[1]
#     lower_left_cornel = -\
#         (half_width * ti.Vector([1.0, 0.0, 0.0]) +
#          half_height * ti.Vector([0.0, 1.0, 0.0]) + w)
#     direction = lower_left_cornel + half_width * 2 * \
#         ti.Vector([1.0, 0.0, 0.0]) * u + half_height * \
#         2 * v * ti.Vector([0.0, 1.0, 0.0])
#     return unit_vector(direction)


@ti.func
def estimate_normal(p):  # sample point
    return unit_vector(
        ti.Vector([
            sceneSDF(ti.Vector([p[0]+EPSILON, p[1], p[2]])) -
            sceneSDF(ti.Vector([p[0]-EPSILON, p[1], p[2]])),
            sceneSDF(ti.Vector([p[0], p[1]+EPSILON, p[2]])) -
            sceneSDF(ti.Vector([p[0], p[1]-EPSILON, p[2]])),
            sceneSDF(ti.Vector([p[0], p[1], p[2]+EPSILON])) -
            sceneSDF(ti.Vector([p[0], p[1], p[2]-EPSILON])),
        ])
    )


@ti.func
def BlinnPhong(k_d, k_s, shinness, p, eye_pos, light_pos, light_intensity):
    N = estimate_normal(p)
    L = unit_vector(light_pos - p)
    V = unit_vector(eye_pos - p)
    H = unit_vector(L+V)

    dotLN = L.dot(N)
    dotHN = H.dot(N)

    res = light_intensity
    if dotLN < 0.0:
        res = ti.Vector([0.0, 0.0, 0.0])
    else:
        if(dotHN < 0.0):
            res *= k_d * dotLN
        else:
            res *= (k_d * dotLN + k_s * pow(dotHN, shinness))
    return res + ambient * k_d


# @ti.func
# def view_mat(origin, worldup, lookat):
#     zaxis = unit_vector(origin - lookat)
#     xaxis = unit_vector(worldup.cross(zaxis))
#     yaxis = zaxis.cross(xaxis)
#     rotation = ti.Matrix(rows=[xaxis, yaxis, zaxis])
#     rotation = ti.Matrix([
#         [xaxis[0], xaxis[1], xaxis[2], 0.0],
#         [yaxis[0], yaxis[1], yaxis[2], 0.0],
#         [zaxis[0], zaxis[1], zaxis[2], 0.0],
#         [0.0, 0.0, 0.0, 1.0]]
#     )

#     translation = ti.Matrix(rows=[
#         [1.0, 0.0, 0.0, -origin[0]],
#         [0.0, 1.0, 0.0, -origin[1]],
#         [0.0, 0.0, 1.0, -origin[2]],
#         [0.0, 0.0, 0.0, 1.0]]
#     )
#     res = rotation @ translation
#     return res


@ti.kernel
def render():
    time[None] += 0.05
    light1_pos = ti.Vector(
        [4.0 * ti.sin(time), 0.0, 4.0 * ti.cos(time)])
    light2_pos = ti.Vector(
        [-4.0, 2.0, 4.0])
    for i, j in img:
        frag = ti.Vector([i, j])
        uu = float(i) / float(x)
        vv = float(j) / float(y)
        ray_dir = lower_left_corner[0] + uu * \
            horizontal[0] + vv * vertical[0] - origin[0]
        ray_dir = unit_vector(ray_dir)
        distance = shortest_distance_marching(
            origin[0], ray_dir, MIN_DIST, MAX_DIST)
        if(distance > MAX_DIST - EPSILON):
            img[i, j] = ti.Vector([0.0, 0.0, 0.0])
        else:
            # img[i, j] = ti.Vector([1.0, 0.0, 0.0])
            img[i, j] = BlinnPhong(k_d, k_s, 32.0, origin[0]+ray_dir * distance, origin[0],
                                   light2_pos, light_intensity)
            img[i, j] += BlinnPhong(k_d, k_s, 32.0, origin[0]+ray_dir * distance, origin[0],
                                    light1_pos, light_intensity)


gui = ti.GUI("SDF", (x, y))
while not gui.get_event(ti.GUI.ESCAPE):
    render()
    pixels = img.to_numpy()
    pixels = np.clip(0, 1, pixels)
    gui.set_image(pixels)
    gui.show()

4 Likes

啊,这里的SDF是Spartial Distribution Function吗? :grinning:

signed distance function噢

试试看ti.core.toggle_advanced_optimization(False)?