import math import pytest import torch import kernels ptd = kernels.get_kernel("phanerozoic/pathtracer-diff", version=1, trust_remote_code=True) requires_cuda = pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA required") def quad(a, b, c, d): return [[a, b, c], [a, c, d]] def add_box(verts, faces, mat, lo, hi, mat_id): x0, y0, z0 = lo x1, y1, z1 = hi base = len(verts) verts.extend([(x0, y0, z0), (x1, y0, z0), (x1, y0, z1), (x0, y0, z1), (x0, y1, z0), (x1, y1, z0), (x1, y1, z1), (x0, y1, z1)]) i = lambda k: base + k for q in [quad(i(0), i(1), i(2), i(3)), # bottom quad(i(4), i(7), i(6), i(5)), # top quad(i(0), i(4), i(5), i(1)), # z0 quad(i(3), i(2), i(6), i(7)), # z1 quad(i(0), i(3), i(7), i(4)), # x0 quad(i(1), i(5), i(6), i(2))]: # x1 faces.extend(q) mat.extend([mat_id, mat_id]) def furnace_scene(albedo=0.5, emission=0.25): verts, faces, mat = [], [], [] add_box(verts, faces, mat, (-2, -2, -2), (2, 2, 2), 0) return ptd.Scene(verts, faces, mat, albedo=[[albedo] * 3], emission=[[emission] * 3]) def cornell(with_box=True, albedo=None, emission=None): verts, faces, mat = [], [], [] def wall(a, b, c, d, m): base = len(verts) verts.extend([a, b, c, d]) faces.extend(quad(base, base + 1, base + 2, base + 3)) mat.extend([m, m]) X, Y, Z = 5.56, 5.488, 5.592 wall((0, 0, 0), (X, 0, 0), (X, 0, Z), (0, 0, Z), 0) # floor wall((0, Y, 0), (0, Y, Z), (X, Y, Z), (X, Y, 0), 0) # ceiling wall((0, 0, Z), (X, 0, Z), (X, Y, Z), (0, Y, Z), 0) # back wall((0, 0, 0), (0, 0, Z), (0, Y, Z), (0, Y, 0), 1) # left (red) wall((X, 0, 0), (X, Y, 0), (X, Y, Z), (X, 0, Z), 2) # right (green) wall((2.13, Y - 0.001, 2.27), (3.43, Y - 0.001, 2.27), (3.43, Y - 0.001, 3.32), (2.13, Y - 0.001, 3.32), 3) # light if with_box: add_box(verts, faces, mat, (1.3, 0.0, 0.65), (2.4, 1.65, 1.7), 0) if albedo is None: albedo = [[0.73, 0.73, 0.73], [0.65, 0.05, 0.05], [0.12, 0.45, 0.15], [0.78, 0.78, 0.78]] if emission is None: emission = [[0, 0, 0], [0, 0, 0], [0, 0, 0], [18.4, 15.6, 8.0]] return ptd.Scene(verts, faces, mat, albedo=albedo, emission=emission) def cornell_camera(): return ptd.Camera(position=(2.78, 2.73, -8.0), look_at=(2.78, 2.73, 2.8), vfov_deg=39.0) @requires_cuda @pytest.mark.kernels_ci def test_furnace_analytic_zero_variance(): """Closed uniform box, BRDF sampling only: with cosine sampling the diffuse throughput multiplier is exactly the albedo, so every path returns exactly E * sum_{k 0.0, (vid, an, fd) assert abs(an - fd) / abs(fd) < 0.35, (vid, an, fd) @requires_cuda @pytest.mark.kernels_ci def test_validation(): verts, faces, mat = [], [], [] add_box(verts, faces, mat, (0, 0, 0), (1, 1, 1), 0) with pytest.raises(ValueError): ptd.Scene(verts, faces, mat, albedo=[[0.5] * 3] * 65, emission=[[0.0] * 3] * 65) scene = ptd.Scene(verts, faces, mat, albedo=[[0.5] * 3], emission=[[1.0] * 3]) cam = ptd.Camera(position=(0.5, 0.5, 0.5), look_at=(0.5, 0.5, 1.0)) with pytest.raises(ValueError): ptd.render(scene, cam, 8, 8, spp=1, max_bounces=17) with pytest.raises(ValueError): ptd.render(scene, cam, 8, 8, spp=1, estimator="mis", nee=True) with pytest.raises(ValueError): ptd.Scene(verts, faces, mat, albedo=[[0.5] * 3], emission=[[1.0] * 3], material_types=[7]) env = torch.full((4, 8, 3), 1.0) scene_env = ptd.Scene(verts, faces, mat, albedo=[[0.5] * 3], emission=[[1.0] * 3], env=env) with pytest.raises(ValueError): ptd.render(scene_env, cam, 8, 8, spp=1, estimator="nee") img = ptd.render(scene, cam, 8, 8, spp=2, max_bounces=2) assert img.shape == (8, 8, 3) and img.dtype == torch.float32 assert torch.isfinite(img).all()