diff --git a/pytorch3d/renderer/blending.py b/pytorch3d/renderer/blending.py index 3fbdd5c5..4bc4822b 100644 --- a/pytorch3d/renderer/blending.py +++ b/pytorch3d/renderer/blending.py @@ -186,7 +186,7 @@ def softmax_rgb_blend( z_inv = (zfar - fragments.zbuf) / (zfar - znear) * mask # pyre-fixme[16]: `Tuple` has no attribute `values`. # pyre-fixme[6]: Expected `Tensor` for 1st param but got `float`. - z_inv_max = torch.max(z_inv, dim=-1).values[..., None] + z_inv_max = torch.max(z_inv, dim=-1).values[..., None].clamp(min=eps) # pyre-fixme[6]: Expected `Tensor` for 1st param but got `float`. weights_num = prob_map * torch.exp((z_inv - z_inv_max) / blend_params.gamma) diff --git a/pytorch3d/renderer/mesh/shader.py b/pytorch3d/renderer/mesh/shader.py index b0945373..c1b5b791 100644 --- a/pytorch3d/renderer/mesh/shader.py +++ b/pytorch3d/renderer/mesh/shader.py @@ -117,7 +117,11 @@ class SoftPhongShader(nn.Module): cameras=cameras, materials=materials, ) - images = softmax_rgb_blend(colors, fragments, blend_params) + znear = kwargs.get("znear", getattr(cameras, "znear", 1.0)) + zfar = kwargs.get("zfar", getattr(cameras, "zfar", 100.0)) + images = softmax_rgb_blend( + colors, fragments, blend_params, znear=znear, zfar=zfar + ) return images @@ -214,7 +218,11 @@ class SoftGouraudShader(nn.Module): cameras=cameras, materials=materials, ) - images = softmax_rgb_blend(pixel_colors, fragments, self.blend_params) + znear = kwargs.get("znear", getattr(cameras, "znear", 1.0)) + zfar = kwargs.get("zfar", getattr(cameras, "zfar", 100.0)) + images = softmax_rgb_blend( + pixel_colors, fragments, self.blend_params, znear=znear, zfar=zfar + ) return images diff --git a/tests/data/test_simple_sphere_outside_zfar_100.png b/tests/data/test_simple_sphere_outside_zfar_100.png new file mode 100644 index 00000000..6aa47236 Binary files /dev/null and b/tests/data/test_simple_sphere_outside_zfar_100.png differ diff --git a/tests/data/test_simple_sphere_outside_zfar_10000.png b/tests/data/test_simple_sphere_outside_zfar_10000.png new file mode 100644 index 00000000..f53459af Binary files /dev/null and b/tests/data/test_simple_sphere_outside_zfar_10000.png differ diff --git a/tests/test_render_meshes.py b/tests/test_render_meshes.py index 0b4ab155..4f0c2ba2 100644 --- a/tests/test_render_meshes.py +++ b/tests/test_render_meshes.py @@ -30,6 +30,7 @@ from pytorch3d.renderer.mesh.shader import ( HardFlatShader, HardGouraudShader, HardPhongShader, + SoftPhongShader, SoftSilhouetteShader, TexturedSoftPhongShader, ) @@ -981,3 +982,63 @@ class TestRenderMeshes(TestCaseMixin, unittest.TestCase): ) self.assertClose(rgb, image_ref, atol=0.05) + + def test_simple_sphere_outside_zfar(self): + """ + Test output when rendering a sphere that is beyond zfar with a SoftPhongShader. + This renders a sphere of radius 500, with the camera at x=1500 for different + settings of zfar. This is intended to check 1) setting cameras.zfar propagates + to the blender and that the rendered sphere is (soft) clipped if it is beyond + zfar, 2) make sure there are no numerical precision/overflow errors associated + with larger world coordinates + """ + device = torch.device("cuda:0") + + # Init mesh + sphere_mesh = ico_sphere(5, device) + verts_padded = sphere_mesh.verts_padded() * 500 + faces_padded = sphere_mesh.faces_padded() + feats = torch.ones_like(verts_padded, device=device) + textures = TexturesVertex(verts_features=feats) + sphere_mesh = Meshes(verts=verts_padded, faces=faces_padded, textures=textures) + + R, T = look_at_view_transform(1500, 0.0, 0.0) + + # Init shader settings + materials = Materials(device=device) + lights = PointLights(device=device) + lights.location = torch.tensor([0.0, 0.0, +1000.0], device=device)[None] + + raster_settings = RasterizationSettings( + image_size=256, blur_radius=0.0, faces_per_pixel=1 + ) + for zfar in (10000.0, 100.0): + cameras = FoVPerspectiveCameras( + device=device, R=R, T=T, aspect_ratio=1.0, fov=60.0, zfar=zfar + ) + rasterizer = MeshRasterizer( + cameras=cameras, raster_settings=raster_settings + ) + blend_params = BlendParams(1e-4, 1e-4, (0, 0, 1.0)) + + shader = SoftPhongShader( + lights=lights, + cameras=cameras, + materials=materials, + blend_params=blend_params, + ) + renderer = MeshRenderer(rasterizer=rasterizer, shader=shader) + images = renderer(sphere_mesh) + rgb = images[0, ..., :3].squeeze().cpu() + + filename = "test_simple_sphere_outside_zfar_%d.png" % int(zfar) + + # Load reference image + image_ref = load_rgb_image(filename, DATA_DIR) + + if DEBUG: + Image.fromarray((rgb.numpy() * 255).astype(np.uint8)).save( + DATA_DIR / ("DEBUG_" + filename) + ) + + self.assertClose(rgb, image_ref, atol=0.05)