mirror of
https://github.com/facebookresearch/pytorch3d.git
synced 2025-08-02 20:02:49 +08:00
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:
parent
6eb158e548
commit
f8ea5906c0
@ -186,7 +186,7 @@ def softmax_rgb_blend(
|
|||||||
z_inv = (zfar - fragments.zbuf) / (zfar - znear) * mask
|
z_inv = (zfar - fragments.zbuf) / (zfar - znear) * mask
|
||||||
# pyre-fixme[16]: `Tuple` has no attribute `values`.
|
# pyre-fixme[16]: `Tuple` has no attribute `values`.
|
||||||
# pyre-fixme[6]: Expected `Tensor` for 1st param but got `float`.
|
# 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`.
|
# 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)
|
weights_num = prob_map * torch.exp((z_inv - z_inv_max) / blend_params.gamma)
|
||||||
|
|
||||||
|
@ -117,7 +117,11 @@ class SoftPhongShader(nn.Module):
|
|||||||
cameras=cameras,
|
cameras=cameras,
|
||||||
materials=materials,
|
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
|
return images
|
||||||
|
|
||||||
|
|
||||||
@ -214,7 +218,11 @@ class SoftGouraudShader(nn.Module):
|
|||||||
cameras=cameras,
|
cameras=cameras,
|
||||||
materials=materials,
|
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
|
return images
|
||||||
|
|
||||||
|
|
||||||
|
BIN
tests/data/test_simple_sphere_outside_zfar_100.png
Normal file
BIN
tests/data/test_simple_sphere_outside_zfar_100.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 758 B |
BIN
tests/data/test_simple_sphere_outside_zfar_10000.png
Normal file
BIN
tests/data/test_simple_sphere_outside_zfar_10000.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
@ -30,6 +30,7 @@ from pytorch3d.renderer.mesh.shader import (
|
|||||||
HardFlatShader,
|
HardFlatShader,
|
||||||
HardGouraudShader,
|
HardGouraudShader,
|
||||||
HardPhongShader,
|
HardPhongShader,
|
||||||
|
SoftPhongShader,
|
||||||
SoftSilhouetteShader,
|
SoftSilhouetteShader,
|
||||||
TexturedSoftPhongShader,
|
TexturedSoftPhongShader,
|
||||||
)
|
)
|
||||||
@ -981,3 +982,63 @@ class TestRenderMeshes(TestCaseMixin, unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertClose(rgb, image_ref, atol=0.05)
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user