Spaces:
Sleeping
Sleeping
| # 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() | |
| def sdf_sphere(p: wp.vec3, r: float): | |
| return wp.length(p) - r | |
| 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) | |
| def sdf_plane(p: wp.vec3, plane: wp.vec4): | |
| return plane[0] * p[0] + plane[1] * p[1] + plane[2] * p[2] + plane[3] | |
| def op_union(d1: float, d2: float): | |
| return wp.min(d1, d2) | |
| def op_subtract(d1: float, d2: float): | |
| return wp.max(-d1, d2) | |
| def op_intersect(d1: float, d2: float): | |
| return wp.max(d1, d2) | |
| # simple scene | |
| 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 | |
| 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)) | |
| 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 | |
| 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() | |