Skip to content

Commit 39f49c2

Browse files
nikhilaravifacebook-github-bot
authored andcommitted
Utils for converting rasterization fragments of clipped meshes back to unclipped
Summary: This diff adds utils functions for converting rasterization fragments of the clipped mesh into fragments expressed in terms of the original unclipped mesh. The face indices and barycentric coordinates are converted in this step. The pixel to triangle distances are handled in the rasterizer which is updated in the next diff in the stack. Reviewed By: jcjohnson Differential Revision: D26169539 fbshipit-source-id: ba451d3facd60ef88a8ffaf25fd04ca07b449ceb
1 parent 23279c5 commit 39f49c2

File tree

2 files changed

+120
-1
lines changed

2 files changed

+120
-1
lines changed

pytorch3d/renderer/mesh/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
22

33

4-
from .clip import ClipFrustum, ClippedFaces, clip_faces
4+
from .clip import (
5+
ClipFrustum,
6+
ClippedFaces,
7+
clip_faces,
8+
convert_clipped_rasterization_to_original_faces,
9+
)
510
from .rasterize_meshes import rasterize_meshes
611
from .rasterizer import MeshRasterizer, RasterizationSettings
712
from .renderer import MeshRenderer

pytorch3d/renderer/mesh/clip.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,117 @@ def clip_faces(
598598
clipped_faces_neighbor_idx=clipped_faces_neighbor_idx,
599599
)
600600
return clipped_faces
601+
602+
603+
def convert_clipped_rasterization_to_original_faces(
604+
pix_to_face_clipped, bary_coords_clipped, clipped_faces: ClippedFaces
605+
) -> Tuple[torch.Tensor, torch.Tensor]:
606+
"""
607+
Convert rasterization Fragments (expressed as pix_to_face_clipped,
608+
bary_coords_clipped, dists_clipped) of clipped Meshes computed using clip_faces()
609+
to the corresponding rasterization Fragments where barycentric coordinates and
610+
face indices are in terms of the original unclipped Meshes. The distances are
611+
handled in the rasterizer C++/CUDA kernels (i.e. for Cases 1/3 the distance
612+
can be used directly and for Case 4 triangles the distance of the pixel to
613+
the closest of the two subdivided triangles is used).
614+
615+
Args:
616+
pix_to_face_clipped: LongTensor of shape (N, image_size, image_size,
617+
faces_per_pixel) giving the indices of the nearest faces at each pixel,
618+
sorted in ascending z-order. Concretely
619+
``pix_to_face_clipped[n, y, x, k] = f`` means that ``faces_verts_clipped[f]``
620+
is the kth closest face (in the z-direction) to pixel (y, x). Pixels that
621+
are hit by fewer than faces_per_pixel are padded with -1.
622+
bary_coords_clipped: FloatTensor of shape
623+
(N, image_size, image_size, faces_per_pixel, 3) giving the barycentric
624+
coordinates in world coordinates of the nearest faces at each pixel, sorted
625+
in ascending z-order. Concretely, if ``pix_to_face_clipped[n, y, x, k] = f``
626+
then ``[w0, w1, w2] = bary_coords_clipped[n, y, x, k]`` gives the
627+
barycentric coords for pixel (y, x) relative to the face defined by
628+
``unproject(face_verts_clipped[f])``. Pixels hit by fewer than
629+
faces_per_pixel are padded with -1.
630+
clipped_faces: an instance of ClippedFaces class giving the auxillary variables
631+
for converting rasterization outputs from clipped to unclipped Meshes.
632+
633+
Returns:
634+
3-tuple: (pix_to_face_unclipped, bary_coords_unclipped, dists_unclipped) that
635+
have the same definition as (pix_to_face_clipped, bary_coords_clipped,
636+
dists_clipped) except that they pertain to faces_verts_unclipped instead of
637+
faces_verts_clipped (i.e the original meshes as opposed to the modified meshes)
638+
"""
639+
faces_clipped_to_unclipped_idx = clipped_faces.faces_clipped_to_unclipped_idx
640+
641+
# If no clipping or culling then return inputs
642+
if faces_clipped_to_unclipped_idx is None:
643+
return pix_to_face_clipped, bary_coords_clipped
644+
645+
device = pix_to_face_clipped.device
646+
647+
# Convert pix_to_face indices to now refer to the faces in the unclipped Meshes.
648+
# Init empty tensor to fill in all the background values which have pix_to_face=-1.
649+
empty = torch.full(pix_to_face_clipped.shape, -1, device=device, dtype=torch.int64)
650+
pix_to_face_unclipped = torch.where(
651+
pix_to_face_clipped != -1,
652+
faces_clipped_to_unclipped_idx[pix_to_face_clipped],
653+
empty,
654+
)
655+
656+
# For triangles that were clipped into smaller triangle(s), convert barycentric
657+
# coordinates from being in terms of the clipped triangle to being in terms of the
658+
# original unclipped triangle.
659+
660+
# barycentric_conversion is a (T, 3, 3) tensor such that
661+
# alpha_unclipped[i, :] = barycentric_conversion[i, :, :]*alpha_clipped[i, :]
662+
barycentric_conversion = clipped_faces.barycentric_conversion
663+
664+
# faces_clipped_to_conversion_idx is an (F_clipped,) shape tensor mapping each output
665+
# face to the applicable row of barycentric_conversion (or set to -1 if conversion is
666+
# not needed)
667+
faces_clipped_to_conversion_idx = clipped_faces.faces_clipped_to_conversion_idx
668+
669+
if barycentric_conversion is not None:
670+
bary_coords_unclipped = bary_coords_clipped.clone()
671+
672+
# Select the subset of faces that require conversion, where N is the sum
673+
# number of case3/case4 triangles that are in the closest k triangles to some
674+
# rasterized pixel.
675+
pix_to_conversion_idx = torch.where(
676+
pix_to_face_clipped != -1,
677+
faces_clipped_to_conversion_idx[pix_to_face_clipped],
678+
empty,
679+
)
680+
faces_to_convert_mask = pix_to_conversion_idx != -1
681+
N = faces_to_convert_mask.sum().item()
682+
683+
# Expand to (N, H, W, K, 3) to be the same shape as barycentric coordinates
684+
faces_to_convert_mask_expanded = faces_to_convert_mask[:, :, :, :, None].expand(
685+
-1, -1, -1, -1, 3
686+
)
687+
688+
# An (N,) dim tensor of indices into barycentric_conversion
689+
conversion_idx_subset = pix_to_conversion_idx[faces_to_convert_mask]
690+
691+
# An (N, 3, 1) tensor of barycentric coordinates in terms of the clipped triangles
692+
bary_coords_clipped_subset = bary_coords_clipped[faces_to_convert_mask_expanded]
693+
bary_coords_clipped_subset = bary_coords_clipped_subset.reshape((N, 3, 1))
694+
695+
# An (N, 3, 3) tensor storing matrices to convert from clipped to unclipped
696+
# barycentric coordinates
697+
bary_conversion_subset = barycentric_conversion[conversion_idx_subset]
698+
699+
# An (N, 3, 1) tensor of barycentric coordinates in terms of the unclipped triangle
700+
bary_coords_unclipped_subset = bary_conversion_subset.bmm(
701+
bary_coords_clipped_subset
702+
)
703+
704+
bary_coords_unclipped_subset = bary_coords_unclipped_subset.reshape([N * 3])
705+
bary_coords_unclipped[
706+
faces_to_convert_mask_expanded
707+
] = bary_coords_unclipped_subset
708+
709+
# dists for case 4 faces will be handled in the rasterizer
710+
# so no need to modify them here.
711+
else:
712+
bary_coords_unclipped = bary_coords_clipped
713+
714+
return pix_to_face_unclipped, bary_coords_unclipped

0 commit comments

Comments
 (0)