qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto. Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
###########################################################################
# Example Ray March
#
# Shows how to implement an SDF ray marching based renderer. Please see
# https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
# for reference on different distance functions.
#
###########################################################################
import warp as wp
wp.init()
@wp.func
def sdf_sphere(p: wp.vec3, r: float):
return wp.length(p) - r
@wp.func
def sdf_box(upper: wp.vec3, p: wp.vec3):
qx = wp.abs(p[0]) - upper[0]
qy = wp.abs(p[1]) - upper[1]
qz = wp.abs(p[2]) - upper[2]
e = wp.vec3(wp.max(qx, 0.0), wp.max(qy, 0.0), wp.max(qz, 0.0))
return wp.length(e) + wp.min(wp.max(qx, wp.max(qy, qz)), 0.0)
@wp.func
def sdf_plane(p: wp.vec3, plane: wp.vec4):
return plane[0] * p[0] + plane[1] * p[1] + plane[2] * p[2] + plane[3]
@wp.func
def op_union(d1: float, d2: float):
return wp.min(d1, d2)
@wp.func
def op_subtract(d1: float, d2: float):
return wp.max(-d1, d2)
@wp.func
def op_intersect(d1: float, d2: float):
return wp.max(d1, d2)
# simple scene
@wp.func
def sdf(p: wp.vec3):
sphere_1 = wp.vec3(0.0, 0.75, 0.0)
d = op_subtract(sdf_sphere(p - sphere_1, 0.75), sdf_box(wp.vec3(1.0, 0.5, 0.5), p))
# ground plane
d = op_union(d, sdf_plane(p, wp.vec4(0.0, 1.0, 0.0, 1.0)))
return d
@wp.func
def normal(p: wp.vec3):
eps = 1.0e-5
# compute gradient of the SDF using finite differences
dx = sdf(p + wp.vec3(eps, 0.0, 0.0)) - sdf(p - wp.vec3(eps, 0.0, 0.0))
dy = sdf(p + wp.vec3(0.0, eps, 0.0)) - sdf(p - wp.vec3(0.0, eps, 0.0))
dz = sdf(p + wp.vec3(0.0, 0.0, eps)) - sdf(p - wp.vec3(0.0, 0.0, eps))
return wp.normalize(wp.vec3(dx, dy, dz))
@wp.func
def shadow(ro: wp.vec3, rd: wp.vec3):
t = float(0.0)
s = float(1.0)
for _ in range(64):
d = sdf(ro + t * rd)
t = t + wp.clamp(d, 0.0001, 2.0)
h = wp.clamp(4.0 * d / t, 0.0, 1.0)
s = wp.min(s, h * h * (3.0 - 2.0 * h))
if t > 8.0:
return 1.0
return s
@wp.kernel
def draw(cam_pos: wp.vec3, cam_rot: wp.quat, width: int, height: int, pixels: wp.array(dtype=wp.vec3)):
tid = wp.tid()
x = tid % width
y = tid // width
# compute pixel coordinates
sx = (2.0 * float(x) - float(width)) / float(height)
sy = (2.0 * float(y) - float(height)) / float(height)
# compute view ray
ro = cam_pos
rd = wp.quat_rotate(cam_rot, wp.normalize(wp.vec3(sx, sy, -2.0)))
t = float(0.0)
# ray march
for _ in range(128):
d = sdf(ro + rd * t)
t = t + d
if d < 0.01:
p = ro + rd * t
n = normal(p)
l = wp.normalize(wp.vec3(0.6, 0.4, 0.5))
# half-vector
h = wp.normalize(l - rd)
diffuse = wp.dot(n, l)
specular = wp.clamp(wp.dot(n, h), 0.0, 1.0) ** 80.0
fresnel = 0.04 + 0.96 * wp.clamp(1.0 - wp.dot(h, l), 0.0, 1.0) ** 5.0
intensity = 2.0
result = (
wp.vec3(0.6, 0.6, 0.59) * (diffuse * (1.0 - fresnel) + specular * fresnel * 10.0) * shadow(p, l) * intensity
)
# gamma
pixels[tid] = wp.vec3(result[0] ** 2.2, result[1] ** 2.2, result[2] ** 2.2)
else:
pixels[tid] = wp.vec3(0.4, 0.45, 0.5) * 1.5
class Example:
def __init__(self, **kwargs):
self.width = 2048
self.height = 1024
self.cam_pos = (-1.25, 1.0, 2.0)
self.cam_rot = wp.quat_rpy(-0.5, -0.5, 0.0)
self.pixels = wp.zeros(self.width * self.height, dtype=wp.vec3)
def update(self):
pass
def render(self):
with wp.ScopedTimer("render"):
wp.launch(
kernel=draw,
dim=self.width * self.height,
inputs=[self.cam_pos, self.cam_rot, self.width, self.height, self.pixels],
)
if __name__ == "__main__":
import matplotlib.pyplot as plt
example = Example()
example.render()
plt.imshow(
example.pixels.numpy().reshape((example.height, example.width, 3)), origin="lower", interpolation="antialiased"
)
plt.show()