Skip to content

Commit f8ea590

Browse files
Steve Bransonfacebook-github-bot
Steve Branson
authored andcommitted
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 #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
1 parent 6eb158e commit f8ea590

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

pytorch3d/renderer/blending.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def softmax_rgb_blend(
186186
z_inv = (zfar - fragments.zbuf) / (zfar - znear) * mask
187187
# pyre-fixme[16]: `Tuple` has no attribute `values`.
188188
# pyre-fixme[6]: Expected `Tensor` for 1st param but got `float`.
189-
z_inv_max = torch.max(z_inv, dim=-1).values[..., None]
189+
z_inv_max = torch.max(z_inv, dim=-1).values[..., None].clamp(min=eps)
190190
# pyre-fixme[6]: Expected `Tensor` for 1st param but got `float`.
191191
weights_num = prob_map * torch.exp((z_inv - z_inv_max) / blend_params.gamma)
192192

pytorch3d/renderer/mesh/shader.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,11 @@ def forward(self, fragments, meshes, **kwargs) -> torch.Tensor:
117117
cameras=cameras,
118118
materials=materials,
119119
)
120-
images = softmax_rgb_blend(colors, fragments, blend_params)
120+
znear = kwargs.get("znear", getattr(cameras, "znear", 1.0))
121+
zfar = kwargs.get("zfar", getattr(cameras, "zfar", 100.0))
122+
images = softmax_rgb_blend(
123+
colors, fragments, blend_params, znear=znear, zfar=zfar
124+
)
121125
return images
122126

123127

@@ -214,7 +218,11 @@ def forward(self, fragments, meshes, **kwargs) -> torch.Tensor:
214218
cameras=cameras,
215219
materials=materials,
216220
)
217-
images = softmax_rgb_blend(pixel_colors, fragments, self.blend_params)
221+
znear = kwargs.get("znear", getattr(cameras, "znear", 1.0))
222+
zfar = kwargs.get("zfar", getattr(cameras, "zfar", 100.0))
223+
images = softmax_rgb_blend(
224+
pixel_colors, fragments, self.blend_params, znear=znear, zfar=zfar
225+
)
218226
return images
219227

220228

758 Bytes
Loading
Loading

tests/test_render_meshes.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
HardFlatShader,
3131
HardGouraudShader,
3232
HardPhongShader,
33+
SoftPhongShader,
3334
SoftSilhouetteShader,
3435
TexturedSoftPhongShader,
3536
)
@@ -981,3 +982,63 @@ def test_texture_map_atlas(self):
981982
)
982983

983984
self.assertClose(rgb, image_ref, atol=0.05)
985+
986+
def test_simple_sphere_outside_zfar(self):
987+
"""
988+
Test output when rendering a sphere that is beyond zfar with a SoftPhongShader.
989+
This renders a sphere of radius 500, with the camera at x=1500 for different
990+
settings of zfar. This is intended to check 1) setting cameras.zfar propagates
991+
to the blender and that the rendered sphere is (soft) clipped if it is beyond
992+
zfar, 2) make sure there are no numerical precision/overflow errors associated
993+
with larger world coordinates
994+
"""
995+
device = torch.device("cuda:0")
996+
997+
# Init mesh
998+
sphere_mesh = ico_sphere(5, device)
999+
verts_padded = sphere_mesh.verts_padded() * 500
1000+
faces_padded = sphere_mesh.faces_padded()
1001+
feats = torch.ones_like(verts_padded, device=device)
1002+
textures = TexturesVertex(verts_features=feats)
1003+
sphere_mesh = Meshes(verts=verts_padded, faces=faces_padded, textures=textures)
1004+
1005+
R, T = look_at_view_transform(1500, 0.0, 0.0)
1006+
1007+
# Init shader settings
1008+
materials = Materials(device=device)
1009+
lights = PointLights(device=device)
1010+
lights.location = torch.tensor([0.0, 0.0, +1000.0], device=device)[None]
1011+
1012+
raster_settings = RasterizationSettings(
1013+
image_size=256, blur_radius=0.0, faces_per_pixel=1
1014+
)
1015+
for zfar in (10000.0, 100.0):
1016+
cameras = FoVPerspectiveCameras(
1017+
device=device, R=R, T=T, aspect_ratio=1.0, fov=60.0, zfar=zfar
1018+
)
1019+
rasterizer = MeshRasterizer(
1020+
cameras=cameras, raster_settings=raster_settings
1021+
)
1022+
blend_params = BlendParams(1e-4, 1e-4, (0, 0, 1.0))
1023+
1024+
shader = SoftPhongShader(
1025+
lights=lights,
1026+
cameras=cameras,
1027+
materials=materials,
1028+
blend_params=blend_params,
1029+
)
1030+
renderer = MeshRenderer(rasterizer=rasterizer, shader=shader)
1031+
images = renderer(sphere_mesh)
1032+
rgb = images[0, ..., :3].squeeze().cpu()
1033+
1034+
filename = "test_simple_sphere_outside_zfar_%d.png" % int(zfar)
1035+
1036+
# Load reference image
1037+
image_ref = load_rgb_image(filename, DATA_DIR)
1038+
1039+
if DEBUG:
1040+
Image.fromarray((rgb.numpy() * 255).astype(np.uint8)).save(
1041+
DATA_DIR / ("DEBUG_" + filename)
1042+
)
1043+
1044+
self.assertClose(rgb, image_ref, atol=0.05)

0 commit comments

Comments
 (0)