论坛里大神太多了,给大家整点简单的。
包含简单的光照渲染和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()