Fix softmax_rgb_blend() when mesh is outside zfar

Summary:
This fixes two small issues with blending.py:softmax_rgb_blend():
  1) zfar and znear attributes are propagated from the camera settings instead of just using default settings of znear=1.0 and zfar=100.0
  2) A check is added to prevent arithmetic overflow in softmax_rgb_blend()

This is a fix in response to https://github.com/facebookresearch/pytorch3d/issues/334
where meshes rendererd using a SoftPhongShader with faces_per_pixel=1 appear black.  This only occurs when the scale of the mesh is large (vertex values > 100, where 100 is the default value of zfar).  This fix allows the caller to increase the value of cameras.zfar to match the scale of her/his mesh.

Reviewed By: nikhilaravi

Differential Revision: D23517541

fbshipit-source-id: ab8631ce9e5f2149f140b67b13eff857771b8807
This commit is contained in:
Steve Branson 2020-09-09 13:20:57 -07:00 committed by Facebook GitHub Bot
parent 6eb158e548
commit f8ea5906c0
5 changed files with 72 additions and 3 deletions

View File

@ -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)

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -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)