docstr: Test a mesh with a texture map as a per face atlas is loaded and rendered correctly.
  Also check that the backward pass for texture atlas rendering is differentiable.
docstr_tok: ["Test","a","mesh","with","a","texture","map","as","a","per","face","atlas","is","loaded","and","rendered","correctly",".","Also","check","that","the","backward","pass","for","texture","atlas","rendering","is","differentiable","."]
code: def _texture_map_atlas(self, rasterizer_type):
      """
      Test a mesh with a texture map as a per face atlas is loaded and rendered correctly.
      Also check that the backward pass for texture atlas rendering is differentiable.
      """
      device = torch.device("cuda:0")
  
      obj_filename = TUTORIAL_DATA_DIR / "cow_mesh/cow.obj"
  
      # Load mesh and texture as a per face texture atlas.
      verts, faces, aux = load_obj(
          obj_filename,
          device=device,
          load_textures=True,
          create_texture_atlas=True,
          texture_atlas_size=8,
          texture_wrap=None,
      )
      atlas = aux.texture_atlas
      mesh = Meshes(
          verts=[verts],
          faces=[faces.verts_idx],
          textures=TexturesAtlas(atlas=[atlas]),
      )
  
      # Init rasterizer settings
      R, T = look_at_view_transform(2.7, 0, 0)
      cameras = FoVPerspectiveCameras(device=device, R=R, T=T)
  
      raster_settings = RasterizationSettings(
          image_size=512,
          blur_radius=0.0,
          faces_per_pixel=1,
          cull_backfaces=True,
          perspective_correct=rasterizer_type.__name__
          == "MeshRasterizerOpenGL",
      )
  
      # Init shader settings
      materials = Materials(
          device=device, specular_color=((0, 0, 0),), shininess=0.0
      )
      blend_params = BlendParams(0.5, 1e-4, (1.0, 1.0, 1.0))
      lights = PointLights(device=device)
  
      # Place light behind the cow in world space. The front of
      # the cow is facing the -z direction.
      lights.location = torch.tensor([0.0, 0.0, 2.0], device=device)[
          None
      ]
  
      # The HardPhongShader can be used directly with atlas textures.
      rasterizer = rasterizer_type(
          cameras=cameras, raster_settings=raster_settings
      )
      if rasterizer_type == MeshRasterizer:
          shader = HardPhongShader(
              device=device,
              blend_params=blend_params,
              cameras=cameras,
              lights=lights,
              materials=materials,
          )
      else:
          shader = SplatterPhongShader(
              device=device,
              blend_params=blend_params,
              cameras=cameras,
              lights=lights,
              materials=materials,
          )
  
      renderer = MeshRenderer(rasterizer, shader)
  
      images = renderer(mesh)
      rgb = images[0, ..., :3].squeeze()
  
      # Load reference image
      image_ref = load_rgb_image(
          f"test_texture_atlas_8x8_back_{rasterizer_type.__name__}.png",
          DATA_DIR,
      )
  
      if DEBUG:
          Image.fromarray(
              (rgb.detach().cpu().numpy() * 255).astype(np.uint8)
          ).save(
              DATA_DIR
              / f"DEBUG_texture_atlas_8x8_back_{rasterizer_type.__name__}.png"
          )
  
      self.assertClose(rgb.cpu(), image_ref, atol=0.05)
  
      # Check gradients are propagated
      # correctly back to the texture atlas.
      # Because of how texture sampling is implemented
      # for the texture atlas it is not possible to get
      # gradients back to the vertices.
      atlas.requires_grad = True
      mesh = Meshes(
          verts=[verts],
          faces=[faces.verts_idx],
          textures=TexturesAtlas(atlas=[atlas]),
      )
      raster_settings = RasterizationSettings(
          image_size=512,
          blur_radius=0.0001,
          faces_per_pixel=5
          if rasterizer_type.__name__ == "MeshRasterizer"
          else 1,
          cull_backfaces=rasterizer_type.__name__ == "MeshRasterizer",
          clip_barycentric_coords=True,
      )
      images = renderer(mesh, raster_settings=raster_settings)
      images[0, ...].sum().backward()
  
      fragments = rasterizer(mesh, raster_settings=raster_settings)
      if rasterizer_type == MeshRasterizer:
          # Some of the bary coordinates are outside the
          # [0, 1] range as expected because the blur is > 0.
          self.assertTrue(fragments.bary_coords.ge(1.0).any())
      self.assertIsNotNone(atlas.grad)
      self.assertTrue(atlas.grad.sum().abs() > 0.0)
  
imports: import os
  import unittest
  from collections import namedtuple
  from itertools import product
  import numpy
  import torch
  from PIL import Image
  from pytorch3d.io import load_obj, load_objs_as_meshes
  from pytorch3d.renderer import AmbientLights, FoVOrthographicCameras, FoVPerspectiveCameras, look_at_view_transform, Materials, MeshRasterizer, MeshRenderer, MeshRendererWithFragments, OrthographicCameras, PerspectiveCameras, PointLights, RasterizationSettings, TexturesAtlas, TexturesUV, TexturesVertex
  from pytorch3d.renderer.fisheyecameras import FishEyeCameras
  from pytorch3d.renderer.mesh.shader import BlendParams, HardFlatShader, HardGouraudShader, HardPhongShader, SoftPhongShader, SoftSilhouetteShader, SplatterPhongShader, TexturedSoftPhongShader
  from pytorch3d.renderer.opengl import MeshRasterizerOpenGL
  from pytorch3d.structures.meshes import join_meshes_as_batch, join_meshes_as_scene, Meshes
  from pytorch3d.utils.ico_sphere import ico_sphere
  from pytorch3d.utils.torus import torus
  from .common_testing import get_pytorch3d_dir, get_tests_dir, load_rgb_image, TestCaseMixin
label_desc: Use image node_id 28 for calling the TestRenderMeshes obj's underlying member method code with example usage: obj._texture_map_atlas(rasterizer_type) without return types