19 minute read

UV map์— texture image๋ฅผ ๋Œ€์‘์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image image image

UV mapping์ด๋ž€ ๋ญ˜๊นŒ์š”?

UV mapping is the 3D modeling process of projecting a 3D modelโ€™s surface to a 2D image for texture mapping. The letters โ€œUโ€ and โ€œVโ€ denote the axes of the 2D texture because โ€œXโ€, โ€œYโ€, and โ€œZโ€ are already used to denote the axes of the 3D object in model space.

UV mapping์—์„œ ๊ฐ€์žฅ ๊ธฐ์–ตํ•ด์•ผํ•  ์ 

  • The UV mapping process involves assigning pixels in the image to surface mappings on the polygon.
  • When a model is created as a polygon mesh using a 3D modeller, UV coordinates (also known as texture coordinates) can be generated for each vertex in the mesh.
  • Once the model is unwrapped, the artist can paint a texture on each triangle individually, using the unwrapped mesh as a template.
  • UV coordinates are optionally applied per face.[2] This means a shared spatial vertex position can have different UV coordinates for each of its triangles, so adjacent triangles can be cut apart and positioned on different areas of the texture map.
  • ์ถœ์ฒ˜: ์œ„ํ‚คํ”ผ๋””์•„ UV Mapping ์„ค๋ช…

SuGaR๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ gaussian๋“ค๋กœ๋ถ€ํ„ฐ texture image์™€ uv๊ฐ€ ์–ด๋–ป๊ฒŒ extract๋˜๋Š”์ง€ ์•Œ์•„๋ด…์‹œ๋‹ค.

  • ๋จผ์ € ๋‹ค์Œ ์‚ฌ์ง„์€ 10000 x 10000 ์˜ ํ”ฝ์…€์„ ๊ฐ€์ง€๋Š” texture image์ž…๋‹ˆ๋‹ค.

    image

  • ์ด texture image๋Š” ๋จผ์ € ์ •์˜ํ•œ uv coordinates์œ„์˜ ์ขŒํ‘œ์—์„œ ์ •์˜๋˜์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    image

  • ๊ทธ๋ฆฌ๊ณ  ์ด texture๋Š” gaussian render๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.

    image

gaussians renderers๋กœ ๋งŒ๋“ค์–ด์ง„ texture๊ฐ€ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์–ด์กŒ๋Š”์ง€ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ด…์‹œ๋‹ค.

  • ๋จผ์ € blender์—์„œ OBJ ํŒŒ์ผ์„ ์—ด์–ด์ฃผ๊ณ , Material Preview ๋ชจ๋“œ๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค. image
  • ๊ทธ๋ฆฌ๊ณ  ์šฐ์ธกํ•˜๋‹จ์— ์šฐํด๋ฆญ์„ ํ•˜์—ฌ Scene Statistics๋ฅผ ์ผœ์ค˜์„œ, Vertices, Faces์˜ ๊ฐœ์ˆ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. image
  • ์šฐ์ธกํ•˜๋‹จ์—์„œ Verts: 1,023,699๊ฐœ / Faces: 1,997,996๊ฐœ์ž„์„ ํ™•์ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. image
  • ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด, refined_mesh์˜ faces๋Š” 1,997,996๊ฐœ vertices๋Š” 1,025,032๊ฐœ์ธ๊ฑธ ๊ฐ์•ˆํ•ด์„œ default sqaure_size๊ฐ€ 10์ผ ๋•Œ, uv map ํฌ๊ธฐ๊ฐ€ 10000x10000๋กœ ๋‚˜์˜จ ๊ฒƒ์ธ๋ฐ ์–ด๋–ป๊ฒŒ ๊ณ„์‚ฐ๋œ ๊ฒƒ์ธ์ง€ ์•„๋ž˜์˜ sugar_model.py์—์„œ extract_texture_image_and_uv_from_gaussian ํ•จ์ˆ˜๋ฅผ ๋ณด๊ณ  ์ดํ•ดํ•ด๋ด…์‹œ๋‹ค.
  • ๋จผ์ € ์ „์ฒด ์ฝ”๋“œ๋ฅผ ์ญ‰ ํ›‘์–ด๋ณด์„ธ์š”.
def extract_texture_image_and_uv_from_gaussians(
    rc:SuGaR,
    square_size:int=10,
    n_sh=-1,
    texture_with_gaussian_renders=True,
    ):
    
    from pytorch3d.renderer import (
    AmbientLights,
    MeshRenderer,
    SoftPhongShader,
    )
    from pytorch3d.renderer.blending import BlendParams
    
    if square_size < 3:
        raise ValueError("square_size must be >= 3")
    
    surface_mesh = rc.surface_mesh
    verts = surface_mesh.verts_list()[0]
    faces = surface_mesh.faces_list()[0]
    faces_verts = verts[faces]
    
    n_triangles = len(faces)
    n_gaussians_per_triangle = rc.n_gaussians_per_surface_triangle
    n_squares = n_triangles // 2 + 1
    n_square_per_axis = int(np.sqrt(n_squares) + 1)
    texture_size = square_size * (n_square_per_axis)
    
    if n_sh==-1:
        n_sh = rc.sh_coordinates.shape[1]
    faces_features = rc.sh_coordinates[:, :n_sh].reshape(n_triangles, n_gaussians_per_triangle, n_sh * 3)
    n_features = faces_features.shape[-1]
    
    if texture_with_gaussian_renders:
        n_features = 3
    
    # Build faces UV.
    # Each face will have 3 corresponding vertices in the UV map
    faces_uv = torch.arange(3 * n_triangles, device=rc.device).view(n_triangles, 3)  # n_triangles, 3
    
    # Build corresponding vertices UV
    vertices_uv = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))
    bottom_verts_uv = torch.cat(
        [vertices_uv[n_square_per_axis:-1, None], vertices_uv[:-n_square_per_axis-1, None], vertices_uv[n_square_per_axis+1:, None]],
        dim=1)
    top_verts_uv = torch.cat(
        [vertices_uv[1:-n_square_per_axis, None], vertices_uv[:-n_square_per_axis-1, None], vertices_uv[n_square_per_axis+1:, None]],
        dim=1)
    
    vertices_uv = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))[:, None]
    u_shift = torch.tensor([[1, 0]], dtype=torch.int32, device=rc.device)[:, None]
    v_shift = torch.tensor([[0, 1]], dtype=torch.int32, device=rc.device)[:, None]
    bottom_verts_uv = torch.cat(
        [vertices_uv + u_shift, vertices_uv, vertices_uv + u_shift + v_shift],
        dim=1)
    top_verts_uv = torch.cat(
        [vertices_uv + v_shift, vertices_uv, vertices_uv + u_shift + v_shift],
        dim=1)
    
    verts_uv = torch.cat([bottom_verts_uv, top_verts_uv], dim=1)
    verts_uv = verts_uv * square_size
    verts_uv[:, 0] = verts_uv[:, 0] + torch.tensor([[-2, 1]], device=rc.device)
    verts_uv[:, 1] = verts_uv[:, 1] + torch.tensor([[2, 1]], device=rc.device)
    verts_uv[:, 2] = verts_uv[:, 2] + torch.tensor([[-2, -3]], device=rc.device)
    verts_uv[:, 3] = verts_uv[:, 3] + torch.tensor([[1, -1]], device=rc.device)
    verts_uv[:, 4] = verts_uv[:, 4] + torch.tensor([[1, 3]], device=rc.device)
    verts_uv[:, 5] = verts_uv[:, 5] + torch.tensor([[-3, -1]], device=rc.device)
    verts_uv = verts_uv.reshape(-1, 2) / texture_size
    
    # ---Build texture image
    # Start by computing pixel indices for each triangle
    texture_img = torch.zeros(texture_size, texture_size, n_features, device=rc.device)    
    pixel_idx_inside_bottom_triangle = torch.zeros(0, 2, dtype=torch.int32, device=rc.device)
    pixel_idx_inside_top_triangle = torch.zeros(0, 2, dtype=torch.int32, device=rc.device)
    for tri_i in range(0, square_size-1):
        for tri_j in range(0, tri_i+1):
            pixel_idx_inside_bottom_triangle = torch.cat(
                [pixel_idx_inside_bottom_triangle, torch.tensor([[tri_i, tri_j]], dtype=torch.int32, device=rc.device)], dim=0)
    for tri_i in range(0, square_size):
        for tri_j in range(tri_i+1, square_size):
            pixel_idx_inside_top_triangle = torch.cat(
                [pixel_idx_inside_top_triangle, torch.tensor([[tri_i, tri_j]], dtype=torch.int32, device=rc.device)], dim=0)
    
    bottom_triangle_pixel_idx = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))[:, None] * square_size + pixel_idx_inside_bottom_triangle[None]
    top_triangle_pixel_idx = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))[:, None] * square_size + pixel_idx_inside_top_triangle[None]
    triangle_pixel_idx = torch.cat(
        [bottom_triangle_pixel_idx[:, None], 
        top_triangle_pixel_idx[:, None]],
        dim=1).view(-1, bottom_triangle_pixel_idx.shape[-2], 2)[:n_triangles]
    
    # Then we compute the barycentric coordinates of each pixel inside its corresponding triangle
    bottom_triangle_pixel_bary_coords = pixel_idx_inside_bottom_triangle.clone().float()
    bottom_triangle_pixel_bary_coords[..., 0] = -(bottom_triangle_pixel_bary_coords[..., 0] - (square_size - 2))
    bottom_triangle_pixel_bary_coords[..., 1] = (bottom_triangle_pixel_bary_coords[..., 1] - 1)
    bottom_triangle_pixel_bary_coords = (bottom_triangle_pixel_bary_coords + 0.) / (square_size - 3)
    bottom_triangle_pixel_bary_coords = torch.cat(
        [1. - bottom_triangle_pixel_bary_coords.sum(dim=-1, keepdim=True), bottom_triangle_pixel_bary_coords],
        dim=-1)
    top_triangle_pixel_bary_coords = pixel_idx_inside_top_triangle.clone().float()
    top_triangle_pixel_bary_coords[..., 0] = (top_triangle_pixel_bary_coords[..., 0] - 1)
    top_triangle_pixel_bary_coords[..., 1] = -(top_triangle_pixel_bary_coords[..., 1] - (square_size - 1))
    top_triangle_pixel_bary_coords = (top_triangle_pixel_bary_coords + 0.) / (square_size - 3)
    top_triangle_pixel_bary_coords = torch.cat(
        [1. - top_triangle_pixel_bary_coords.sum(dim=-1, keepdim=True), top_triangle_pixel_bary_coords],
        dim=-1)
    triangle_pixel_bary_coords = torch.cat(
        [bottom_triangle_pixel_bary_coords[None],
        top_triangle_pixel_bary_coords[None]],
        dim=0)  # 2, n_pixels_per_triangle, 3
    
    all_triangle_bary_coords = triangle_pixel_bary_coords[None].expand(n_squares, -1, -1, -1).reshape(-1, triangle_pixel_bary_coords.shape[-2], 3)
    all_triangle_bary_coords = all_triangle_bary_coords[:len(faces_verts)]
    
    pixels_space_positions = (all_triangle_bary_coords[..., None] * faces_verts[:, None]).sum(dim=-2)[:, :, None]
    
    gaussian_centers = rc.points.reshape(-1, 1, rc.n_gaussians_per_surface_triangle, 3)
    gaussian_inv_scaled_rotation = rc.get_covariance(return_full_matrix=True, return_sqrt=True, inverse_scales=True).reshape(-1, 1, rc.n_gaussians_per_surface_triangle, 3, 3)
    
    # Compute the density field as a sum of local gaussian opacities
    shift = (pixels_space_positions - gaussian_centers)
    warped_shift = gaussian_inv_scaled_rotation.transpose(-1, -2) @ shift[..., None]
    neighbor_opacities = (warped_shift[..., 0] * warped_shift[..., 0]).sum(dim=-1).clamp(min=0., max=1e8)
    neighbor_opacities = torch.exp(-1. / 2 * neighbor_opacities) # / rc.n_gaussians_per_surface_triangle
    
    pixel_features = faces_features[:, None].expand(-1, neighbor_opacities.shape[1], -1, -1).gather(
        dim=-2,
        index=neighbor_opacities[..., None].argmax(dim=-2, keepdim=True).expand(-1, -1, -1, 3)
        )[:, :, 0, :]
        
    # pixel_alpha = neighbor_opacities.sum(dim=-1, keepdim=True)
    texture_img[(triangle_pixel_idx[..., 0], triangle_pixel_idx[..., 1])] = pixel_features

    texture_img = texture_img.transpose(0, 1)
    texture_img = SH2RGB(texture_img.flip(0))
    
    faces_per_pixel = 1
    max_faces_per_bin = 50_000
    mesh_raster_settings = RasterizationSettings(
        image_size=(rc.image_height, rc.image_width),
        blur_radius=0.0, 
        faces_per_pixel=faces_per_pixel,
        # max_faces_per_bin=max_faces_per_bin
    )
    lights = AmbientLights(device=rc.device)
    rasterizer = MeshRasterizer(
            cameras=rc.nerfmodel.training_cameras.p3d_cameras[0], 
            raster_settings=mesh_raster_settings,
    )
    renderer = MeshRenderer(
        rasterizer=rasterizer,
        shader=SoftPhongShader(
            device=rc.device, 
            cameras=rc.nerfmodel.training_cameras.p3d_cameras[0],
            lights=lights,
            blend_params=BlendParams(background_color=(0.0, 0.0, 0.0)),
        )
    )
    texture_idx = torch.cartesian_prod(
        torch.arange(texture_size, device=rc.device), 
        torch.arange(texture_size, device=rc.device)
        ).reshape(texture_size, texture_size, 2
                    )
    texture_idx = torch.cat([texture_idx, torch.zeros_like(texture_idx[..., 0:1])], dim=-1)
    texture_counter = torch.zeros(texture_size, texture_size, 1, device=rc.device)
    idx_textures_uv = TexturesUV(
        maps=texture_idx[None].float(), #texture_img[None]),
        verts_uvs=verts_uv[None],
        faces_uvs=faces_uv[None],
        sampling_mode='nearest',
        )
    idx_mesh = Meshes(
        verts=[rc.surface_mesh.verts_list()[0]],   
        faces=[rc.surface_mesh.faces_list()[0]],
        textures=idx_textures_uv,
        )
    
    for cam_idx in range(len(rc.nerfmodel.training_cameras)):
        p3d_cameras = rc.nerfmodel.training_cameras.p3d_cameras[cam_idx]
        
        # Render rgb img
        rgb_img = rc.render_image_gaussian_rasterizer(
            camera_indices=cam_idx,
            sh_deg=0,  #rc.sh_levels-1,
            compute_color_in_rasterizer=True,  #compute_color_in_rasterizer,
        ).clamp(min=0, max=1)
        
        fragments = renderer.rasterizer(idx_mesh, cameras=p3d_cameras)
        idx_img = renderer.shader(fragments, idx_mesh, cameras=p3d_cameras)[0, ..., :2]
        # print("Idx img:", idx_img.shape, idx_img.min(), idx_img.max())
        update_mask = fragments.zbuf[0, ..., 0] > 0
        idx_to_update = idx_img[update_mask].round().long() 

        use_average = True
        if not use_average:
            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = rgb_img[update_mask]
        else:
            no_initialize_mask = texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])][..., 0] != 0
            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = no_initialize_mask[..., None] * texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])]

            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] + rgb_img[update_mask]
            texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])] = texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])] + 1

    if use_average:
        texture_img = texture_img / texture_counter.clamp(min=1)        
    
    return verts_uv, faces_uv, texture_img
  • ์ด์ œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์„ ์ฐจ๊ทผ์ฐจ๊ทผ ์•Œ์•„๊ฐ€๋ด…์‹œ๋‹ค.

    1. UV ์ขŒํ‘œ ๊ณ„์‚ฐ

  • vertices_uv ์ƒ์„ฑ: ๋จผ์ €, ๊ฐ ์ •์‚ฌ๊ฐํ˜• ์…€์˜ ์ •์  UV ์ขŒํ‘œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. torch.cartesian_prod๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ •์ ์˜ ์ขŒํ‘œ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    n_triangles = 1997996  # ์ฃผ์–ด์ง„ faces์˜ ๊ฐœ์ˆ˜
    n_squares = n_triangles // 2 + 1
    n_square_per_axis = int(np.sqrt(n_squares) + 1)  # n_square_per_axis ๊ณ„์‚ฐ
    vertices_uv = torch.cartesian_prod(
      torch.arange(n_square_per_axis, device=rc.device), 
      torch.arange(n_square_per_axis, device=rc.device)
    )
    
  • n_triangles = 1,997,996์ด๋ฉด n_squares = 998999, ๋”ฐ๋ผ์„œ n_square_per_axis = 1000 (์ •์ˆ˜๋กœ ์˜ฌ๋ฆผ)
  • vertices_uv๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    tensor([[   0,    0],
          [   0,    1],
          [   0,    2],
          ...,
          [ 999,  997],
          [ 999,  998],
          [ 999,  999]], device=rc.device)
    

2. faces_uv ์ƒ์„ฑ

  • ๊ฐ ์‚ผ๊ฐํ˜• ๋ฉด์˜ UV ์ขŒํ‘œ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. faces_uv๋Š” ๊ฐ ์‚ผ๊ฐํ˜•์˜ ์ •์ ๋“ค์ด UV ๋งต์—์„œ ์–ด๋–ค ์ขŒํ‘œ๋ฅผ ๊ฐ€์ง€๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    faces_uv = torch.arange(3 * n_triangles, device=rc.device).view(n_triangles, 3)
    
  • n_triangles = 1,997,996์ผ ๋•Œ, faces_uv๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:
tensor([[       0,        1,        2],
        [       3,        4,        5],
        [       6,        7,        8],
        ...,
        [5993985, 5993986, 5993987],
        [5993988, 5993989, 5993990],
        [5993991, 5993992, 5993993]], device=rc.device)

3. vertices_uv ์ •๋ฆฌ ๋ฐ ์กฐ์ •

  • ๊ฐ ์ •์‚ฌ๊ฐํ˜• ์…€์„ ๋‘ ๊ฐœ์˜ ์‚ผ๊ฐํ˜• (Top๊ณผ Bottom)์œผ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ทธ ์ •์ ๋“ค์˜ UV ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
u_shift = torch.tensor([[1, 0]], dtype=torch.int32, device=rc.device)[:, None]
v_shift = torch.tensor([[0, 1]], dtype=torch.int32, device=rc.device)[:, None]
bottom_verts_uv = torch.cat(
    [vertices_uv + u_shift, vertices_uv, vertices_uv + u_shift + v_shift],
    dim=1
)
top_verts_uv = torch.cat(
    [vertices_uv + v_shift, vertices_uv, vertices_uv + u_shift + v_shift],
    dim=1
)
  • ๊ฐ ์ •์‚ฌ๊ฐํ˜• ์…€์— ๋Œ€ํ•ด Bottom ์‚ผ๊ฐํ˜•๊ณผ Top ์‚ผ๊ฐํ˜•์˜ ์ •์  UV ์ขŒํ‘œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:
    • Bottom ์‚ผ๊ฐํ˜•:
      tensor([[[   1,    0], [   0,    0], [   1,    1]],
          [[   2,    0], [   1,    0], [   2,    1]],
          [[   3,    0], [   2,    0], [   3,    1]],
          ...,
          [[ 998,  999], [ 997,  999], [ 998, 1000]],
          [[ 999,  999], [ 998,  999], [ 999, 1000]],
          [[1000,  999], [ 999,  999], [1000, 1000]]], device=rc.device)
      
    • Top ์‚ผ๊ฐํ˜•:
      tensor([[[   0,    1], [   0,    0], [   1,    1]],
          [[   1,    1], [   1,    0], [   2,    1]],
          [[   2,    1], [   2,    0], [   3,    1]],
          ...,
          [[ 997, 1000], [ 997,  999], [ 998, 1000]],
          [[ 998, 1000], [ 998,  999], [ 999, 1000]],
          [[ 999, 1000], [ 999,  999], [1000, 1000]]], device=rc.device)
      
    • ์ง€๊ธˆ์˜ ์ƒํ™ฉ์„ ๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ ค๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด vertices_uv coordinates๋ฅผ ๋งŒ๋“ค์—ˆ๊ณ , ๊ฐ ์‚ผ๊ฐํ˜•์˜ vertices 3๊ฐœ์”ฉ์„ vertices_uv coordinates์— ํ• ๋‹นํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. (์•„์ง์€ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” UV map์ฒ˜๋Ÿผ 0~1 ๊ฐ’์œผ๋กœ normalize๊นŒ์ง€ ํ•œ ์ƒํƒœ๋Š” ์•„๋‹™๋‹ˆ๋‹ค.) image

4. UV ์ขŒํ‘œ ์Šค์ผ€์ผ๋ง ๋ฐ ์ •๊ทœํ™”

  • vertices_uv๋ฅผ ํ…์Šค์ฒ˜ ์‚ฌ์ด์ฆˆ์— ๋งž๊ฒŒ ์Šค์ผ€์ผ๋งํ•˜๊ณ , ์ •๊ทœํ™”ํ•ฉ๋‹ˆ๋‹ค.
verts_uv = torch.cat([bottom_verts_uv, top_verts_uv], dim=0)
verts_uv = verts_uv * square_size
verts_uv = verts_uv.reshape(-1, 2) / texture_size
  • square_size = 10, texture_size = 10000์ผ ๋•Œ:
    • ์ •๊ทœํ™”๋œ UV ์ขŒํ‘œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜ต๋‹ˆ๋‹ค:
      tensor([[0.0010, 0.0000],
          [0.0000, 0.0000],
          [0.0010, 0.0010],
          [0.0020, 0.0000],
          [0.0010, 0.0000],
          [0.0020, 0.0010],
          [0.0030, 0.0000],
          [0.0020, 0.0000],
          [0.0030, 0.0010],
          ...,
          [0.9990, 0.9990],
          [0.9980, 0.9990],
          [0.9990, 1.0000],
          [1.0000, 0.9990],
          [0.9990, 0.9990],
          [1.0000, 1.0000]], device=rc.device)
      
    • ์ด ์ •๊ทœํ™”๋œ UV ์ขŒํ‘œ์— ๊ฐ ์‚ผ๊ฐํ˜•์œผ๋กœ๋ถ€ํ„ฐ ๊ณ„์‚ฐํ•œ ํ”ฝ์…€ ์ธ๋ฑ์Šค๋ฅผ ํ• ๋‹นํ•˜๊ณ , ๊ทธ ํ›„์— ๊ฐ ํ”ฝ์…€์ด ํ•ด๋‹น ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์—์„œ ๊ฐ€์ง€๋Š” Barycentric Coordinates(๋ฌด๊ฒŒ์ค‘์‹ฌ ์ขŒํ‘œ)๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด local gaussian ๋ถˆํˆฌ๋ช…๋„์˜ ํ•ฉ์œผ๋กœ ๋ฐ€๋„ ํ•„๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ๋ Œ๋”๋ง๋œ ํ”ฝ์…€ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…์Šค์ฒ˜ ํ”ฝ์…€์˜ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ํ†ตํ•ด UV ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
    • uv texture image (garden scene) image

5. Texture image์„ ์ƒ์„ฑ์‹œ, ๊ธฐ๋ณธ์ ์œผ๋กœ, --square_size๋Š” texture image์—์„œ mesh์˜ triangle์„ ๋งคํ•‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ”ฝ์…€ ์ˆ˜์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ทธ๋ž˜์„œ --square_size๊ฐ€ ํด์ˆ˜๋ก, texture์˜ resolution์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

Basically, โ€“square_size is related to the number of pixels used to map a triangle of the mesh in the texture image. So the higher square_size, the higher the resolution (and the size) of the texture. Because we optimize the texture on GPU, you can have OOM issues if square_size is too high.

image

Question about coarse and refined mesh #89

  • ์ฆ‰, ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, square_size์— ๋งž๊ฒŒ bottom_triangle_pixel_bary_coords๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • sqaure_size๊ฐ€ default์ธ 10์ด๋ผ๋ฉด, triangle์˜ barycentric coordinates๋„ 10x10์˜ pixels์—์„œ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
  • square_size๋ฅผ 5๋กœ ์ค„์ด๋ฉด, triangle์˜ barycentric coordinates๋„ 5x5์˜ pixels ๋‚ด์—์„œ ๊ฒฐ์ •๋˜๋ฏ€๋กœ texture image์˜ ํ•ด์ƒ๋„๊ฐ€ ๋‚ฎ์•„์ง‘๋‹ˆ๋‹ค.
  • ํ…์Šค์ฒ˜ ํ•ด์ƒ๋„๋Š” ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ์— ์˜ํ•ด ๊ฒฐ์ •๋˜๋ฉฐ, Barycentric coordinates๋Š” ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ๊ฐ ํ”ฝ์…€์— ๋Œ€ํ•ด ์ •ํ™•ํ•œ ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    # ---Build texture image
    # Start by computing pixel indices for each triangle
    texture_img = torch.zeros(texture_size, texture_size, n_features, device=rc.device)    
    pixel_idx_inside_bottom_triangle = torch.zeros(0, 2, dtype=torch.int32, device=rc.device)
    pixel_idx_inside_top_triangle = torch.zeros(0, 2, dtype=torch.int32, device=rc.device)
    for tri_i in range(0, square_size-1):
        for tri_j in range(0, tri_i+1):
            pixel_idx_inside_bottom_triangle = torch.cat(
                [pixel_idx_inside_bottom_triangle, torch.tensor([[tri_i, tri_j]], dtype=torch.int32, device=rc.device)], dim=0)
    for tri_i in range(0, square_size):
        for tri_j in range(tri_i+1, square_size):
            pixel_idx_inside_top_triangle = torch.cat(
                [pixel_idx_inside_top_triangle, torch.tensor([[tri_i, tri_j]], dtype=torch.int32, device=rc.device)], dim=0)
    
    bottom_triangle_pixel_idx = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))[:, None] * square_size + pixel_idx_inside_bottom_triangle[None]
    top_triangle_pixel_idx = torch.cartesian_prod(
        torch.arange(n_square_per_axis, device=rc.device), 
        torch.arange(n_square_per_axis, device=rc.device))[:, None] * square_size + pixel_idx_inside_top_triangle[None]
    triangle_pixel_idx = torch.cat(
        [bottom_triangle_pixel_idx[:, None], 
        top_triangle_pixel_idx[:, None]],
        dim=1).view(-1, bottom_triangle_pixel_idx.shape[-2], 2)[:n_triangles]
    
    # Then we compute the barycentric coordinates of each pixel inside its corresponding triangle
    bottom_triangle_pixel_bary_coords = pixel_idx_inside_bottom_triangle.clone().float()
    bottom_triangle_pixel_bary_coords[..., 0] = -(bottom_triangle_pixel_bary_coords[..., 0] - (square_size - 2))
    bottom_triangle_pixel_bary_coords[..., 1] = (bottom_triangle_pixel_bary_coords[..., 1] - 1)
    bottom_triangle_pixel_bary_coords = (bottom_triangle_pixel_bary_coords + 0.) / (square_size - 3)
    bottom_triangle_pixel_bary_coords = torch.cat(
        [1. - bottom_triangle_pixel_bary_coords.sum(dim=-1, keepdim=True), bottom_triangle_pixel_bary_coords],
        dim=-1)
    top_triangle_pixel_bary_coords = pixel_idx_inside_top_triangle.clone().float()
    top_triangle_pixel_bary_coords[..., 0] = (top_triangle_pixel_bary_coords[..., 0] - 1)
    top_triangle_pixel_bary_coords[..., 1] = -(top_triangle_pixel_bary_coords[..., 1] - (square_size - 1))
    top_triangle_pixel_bary_coords = (top_triangle_pixel_bary_coords + 0.) / (square_size - 3)
    top_triangle_pixel_bary_coords = torch.cat(
        [1. - top_triangle_pixel_bary_coords.sum(dim=-1, keepdim=True), top_triangle_pixel_bary_coords],
        dim=-1)
    triangle_pixel_bary_coords = torch.cat(
        [bottom_triangle_pixel_bary_coords[None],
        top_triangle_pixel_bary_coords[None]],
        dim=0)  # 2, n_pixels_per_triangle, 3

์œ„ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ ์‚ผ๊ฐํ˜•(top_triangle, bottom_triangle)์˜ ๋‚ด๋ถ€ ํ”ฝ์…€์— ๋Œ€ํ•ด Barycentric coordinates๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ‰์ƒ์ด๋‚˜ ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๋ณด๊ฐ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์•„๋ž˜ ์˜์ƒ์„ ํ†ตํ•ด barycentric coordinate๊ฐ€ ์ •ํ™•ํžˆ ๋ญ”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Introduction to Computer Graphics (Lecture 10): Ray casting 2โ€“barycentric coordinates, CGS, etc.
  • barycentric coordinates๊ฐ€ ๋ฌด๊ฒŒ์ค‘์‹ฌ์œผ๋กœ ๋ฒˆ์—ญ๋˜์–ด์„œ ํ•˜๋‚˜์˜ ์  P์ธ๊ฑธ๋กœ ์˜คํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹ค์‹œ๋งํ•ด, P๋Š” 1๊ฐœ๋งŒ ์žˆ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ P๋Š” ์—ฌ๋Ÿฌ ๊ฐœ๋กœ, triangle์•ˆ์— ์žˆ์„์ˆ˜๋„ ๋ฐ–์— ์žˆ์„์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.)
  • barycentric coordinates์˜ ๊ณจ์ž๋Š” triangle์˜ 3๊ฐœ์˜ ์ •์ (vertcies)์œผ๋กœ triangle์„ ํฌํ•จํ•˜๋Š” plane์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ , ์—ฌ๊ธฐ์— ๊ฐ ์ •์  a,b,c๋กœ๋ถ€ํ„ฐ ๊ตฌํ•œ ฮฑ,ฮฒ,ฮณ์˜ ํ•ฉ์ด 1์ด๋ผ๋Š” constraint๋ฅผ ์คŒ์œผ๋กœ์จ ๊ทธ์— ํ•ด๋‹นํ•˜๋Š” ์  P์—์„œ๋Š” ์†๊ฐ€๋ฝ์œผ๋กœ ๊ทธ plane์„ ๋“ค์—ˆ์„ ๋•Œ, ๋–จ์–ด์ง€์ง€ ์•Š๊ณ  ํ‰ํ˜•์„ ์œ ์ง€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • constraint1: ฮฑ+ฮฒ+ฮณ=1
  • ์ด๋•Œ, triangle์„ ํฌํ•จํ•˜๋Š” plane ์™ธ๋ถ€์—์„œ๋„ ์  P๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” triangle ๋‚ด๋ถ€์˜ ์  P๋กœ๋งŒ ์ œํ•œํ•˜์—ฌ, ray๊ฐ€ triangle๊ณผ intersectํ•  ๋•Œ, ๊ทธ ์  P๋ฅผ triangle์˜ 3๊ฐœ์˜ ์ •์ (vertices)๋กœ ํ‘œํ˜„ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๊ทธ๋ฅผ ์œ„ํ•ด ์กฐ๊ฑด ํ•˜๋‚˜๋ฅผ ๋” ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์กฐ๊ฑด์€ ฮฑ,ฮฒ,ฮณโ‰ฅ0 ์ž…๋‹ˆ๋‹ค.
  • constraint2: ฮฑ,ฮฒ,ฮณโ‰ฅ0
  • ์ด๋ฅผ ํ†ตํ•ด triangle๊ณผ ray์˜ intersection์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

image image image image image image image

  • ์‹ค์ œ๋กœ ray์˜ ํ•จ์ˆ˜์™€ barycentric coordinates์˜ ์  P๊ฐ€ ๊ฐ™์€ ์ง€์ ์„ ์ฐพ๊ณ  linear equation์„ ํ’€๋ฉด ํ•œ๋ฒˆ์— ray๊ฐ€ source๋กœ๋ถ€ํ„ฐ ์–ผ๋งˆ๋‚˜ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ์„œ traingle๊ณผ intersectํ•˜๋Š”์ง€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” t์™€ barycentric coordiantes์˜ ฮฒ,ฮณ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ณ , ฮฑ ๋˜ํ•œ ฮฑ+ฮฒ+ฮณ=1๋กœ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Intersection of Ray and Triangle

Given the equations:

The equation for a ray is:

\[P(t) = R_0 + tR_d\]

The equation for the barycentric coordinates of a triangle is:

\[P(\beta, \gamma) = a + \beta(b - a) + \gamma(c - a)\]

Equating both expressions for ( P ):

\[P(t) = P(\beta, \gamma)\]

This indicates that the ray and the triangle intersect.

  • ์ด๊ฒƒ์ด barycentric coordinate๊ฐ€ ์œ ์šฉํ•œ ์ด์œ ์ž…๋‹ˆ๋‹ค.๐Ÿ˜ฒ
  • ๋‹จ, triangle ์™ธ๋ถ€๊ฐ€ ์•„๋‹Œ ๋‚ด๋ถ€์˜ ๊ฐ’์œผ๋กœ๋งŒ intersectํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ œํ•œํ•˜๋ ค๋ฉด, ๋ฐฉ์ •์‹์„ ํ‘ผ๋‹ค์Œ, ๋‹ค์Œ์„ ์ฒดํฌํ•ด์ค๋‹ˆ๋‹ค.
  • ฮฑ,ฮฒ,ฮณโ‰ฅ0, ฮฒ+ฮณโ‰ค1

image image image image image image image image

  • barycentric coordinates์˜ weight์ธ ฮฑ,ฮฒ,ฮณ๋ฅผ ๊ตฌํ–ˆ์œผ๋ฉด, ์šฐ๋ฆฌ๋Š” ๊ทธ weight๋ฅผ ์ด์šฉํ•ด์„œ interpolationํ•˜์—ฌ, triangle ๋‚ด๋ถ€์˜ ์–ด๋–ค ์ ์— ๋Œ€ํ•ด์„œ๋„ smoothํ•˜๊ฒŒ changingํ•˜๋Š” value๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๋ฐฉ๋ฒ•์€ triangle์˜ 3๊ฐœ์˜ ์ •์ (vertices)์ธ a,b,c์— ๋Œ€ํ•ด value๋กœ์จ v1,v2,v3๋ฅผ ์ค๋‹ˆ๋‹ค.
  • ์ด๋•Œ v1,v2,v3๋Š” Colors๊ฐ€ ๋ ์ˆ˜๋„ ์žˆ๊ณ , Normals์ด ๋ ์ˆ˜๋„ ์žˆ๊ณ , texture coordinates๊ฐ€ ๋ ์ˆ˜๋„ ์žˆ๊ณ , ๋‹ค๋ฅธ ์–ด๋–ค value๋“  ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  barycentric coordinate๋ฅผ ๊ตฌํ•  ๋•Œ ์‚ฌ์šฉ๋œ ฮฑ,ฮฒ,ฮณ๋กœ v1,v2,v3์˜ ๊ฐ’๋“ค์„ weighted sumํ•˜์—ฌ triangle ๋‚ด๋ถ€์˜ ์–ด๋–ค ์  P์— ๋Œ€ํ•ด์„œ๋„ smoothํ•˜๊ฒŒ interpolation๋œ ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฅผ barycentric interpolation์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ปดํ“จํ„ฐ ๊ทธ๋ž˜ํ”ฝ์Šค์—์„œ ๋งค์šฐ ์œ ์šฉํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

image image

  • ์  P๊ฐ€ triangle์˜ ์•ˆ์— ์œ„์น˜ํ•˜๋ ค๋ฉด ฮฒ+ฮณโ‰ค1์ด์–ด์•ผํ•˜๊ณ , ฮฒ,ฮณโ‰ฅ0์ด์–ด์•ผ ํ•œ๋‹ค๊ณ  ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.
\[P(\beta, \gamma) = a + \beta(b - a) + \gamma(c - a)\]
  • ์œ„ ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด notation์„ ๋‹ค๋ฅด๊ฒŒํ•ด์„œ ์“ธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. (triangle์˜ 3๊ฐœ์˜ ์ •์  A,B,C์™€ weights์ธ w1,w2)
\[P = A+w_1(B-A)+w_2(C-A)\]

image

Gamedev Maths: point in triangle

๋‹ค์–‘ํ•œ 3D ๊ทธ๋ž˜ํ”ฝ์Šค ์‘์šฉ ๋ถ„์•ผ์—์„œ Barycentric Coordinates์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ

Barycentric coordinates๋Š” 3D ๊ทธ๋ž˜ํ”ฝ์Šค์—์„œ ๋งค์šฐ ์œ ์šฉํ•˜๋ฉฐ ๋‹ค์–‘ํ•œ ์‘์šฉ ๋ถ„์•ผ์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๊ทธ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์‹œ๋“ค์ž…๋‹ˆ๋‹ค:

1. ์‰์ด๋”ฉ (Shading)

Barycentric coordinates๋Š” ์‚ผ๊ฐํ˜•์˜ ๋‚ด๋ถ€์—์„œ ์ƒ‰์ƒ์ด๋‚˜ ๋ฒ•์„ (normal)์„ ๋ณด๊ฐ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋งค๋„๋Ÿฌ์šด ์‰์ด๋”ฉ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ ์ •์ (vertex)์— ์ •์˜๋œ ์ƒ‰์ƒ์ด๋‚˜ ๋ฒ•์„ ์„ ์‚ผ๊ฐํ˜•์˜ ๋‚ด๋ถ€ ์ ์œผ๋กœ ๋ณด๊ฐ„ํ•˜์—ฌ ํ‘œ๋ฉด์ด ํ‰ํ‰ํ•˜์ง€๋งŒ ๋งค๋„๋Ÿฌ์šด ์ƒ‰์ƒ ๋ณ€ํ™”๋ฅผ ๊ฐ€์ง€๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ Phong shading์ด๋‚˜ Gouraud shading๊ณผ ๊ฐ™์€ ๊ธฐ์ˆ ์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2. ํ…์Šค์ฒ˜ ๋งคํ•‘ (Texture Mapping)

Barycentric coordinates๋Š” ์‚ผ๊ฐํ˜•์˜ ๊ฐ ์ •์ ์— ์ •์˜๋œ ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ์ ๋“ค๋กœ ๋ณด๊ฐ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ผ๊ฐํ˜• ํ‘œ๋ฉด์— ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ๋งค๋„๋Ÿฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์ •์ ์— ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ํ• ๋‹นํ•˜๊ณ , ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ๊ฐ ํ”ฝ์…€์—์„œ ํ•ด๋‹น ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์ •ํ™•ํ•œ ํ…์Šค์ฒ˜ ์ƒ‰์ƒ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์ถฉ๋Œ ๊ฐ์ง€ (Collision Detection)

๋ฌผ์ฒด์˜ ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ณผ์ •์—์„œ, Barycentric coordinates๋Š” ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ํŠน์ • ์ ์ด ์ถฉ๋Œ ์ง€์ ์ธ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฌผ์ฒด๊ฐ€ ์‚ผ๊ฐํ˜• ํ‘œ๋ฉด๊ณผ ์ถฉ๋Œํ•˜๋Š” ๊ฒฝ์šฐ, ์ถฉ๋Œ ์ง€์ ์„ ์‚ผ๊ฐํ˜•์˜ ์ •์  ์ขŒํ‘œ์™€ Barycentric coordinates๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. ์• ๋‹ˆ๋ฉ”์ด์…˜ (Animation)

Barycentric coordinates๋Š” ์Šคํ‚จ๋‹(skinning) ๊ธฐ๋ฒ•์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์บ๋ฆญํ„ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜์—์„œ ๋ผˆ๋Œ€(bone)์™€ ํ”ผ๋ถ€(skin)์˜ ๋ณ€ํ˜•์„ ๋งค๋„๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ์ •์ ์˜ ์˜ํ–ฅ์„ ์—ฌ๋Ÿฌ ๋ผˆ๋Œ€์— ๊ฑธ์ณ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ฐ ์ •์ ์ด ์—ฌ๋Ÿฌ ๋ผˆ๋Œ€์˜ ์˜ํ–ฅ์„ ๋ฐ›์„ ๋•Œ, Barycentric coordinates๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ๋ผˆ๋Œ€์˜ ๋ณ€ํ˜•์„ ์ •์ ์— ๋ณด๊ฐ„ํ•˜์—ฌ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

5. ์กฐ๋ช… ๊ณ„์‚ฐ (Lighting Calculation)

์กฐ๋ช… ๊ณ„์‚ฐ์—์„œ๋„ Barycentric coordinates๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ์ •์ ์—์„œ์˜ ์กฐ๋ช… ์ •๋ณด๋ฅผ ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ์ ๋“ค๋กœ ๋ณด๊ฐ„ํ•˜์—ฌ ๋งค๋„๋Ÿฌ์šด ์กฐ๋ช… ๋ณ€ํ™”๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋” ํ˜„์‹ค๊ฐ ์žˆ๋Š” ์กฐ๋ช… ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

6. ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง (Physically Based Rendering, PBR)

PBR์—์„œ Barycentric coordinates๋Š” ์ •์  ์†์„ฑ(vertex attributes)์„ ๋ณด๊ฐ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ์ •์ ์— ์ •์˜๋œ ์žฌ์งˆ ์†์„ฑ(material properties)์„ ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ์ ์œผ๋กœ ๋ณด๊ฐ„ํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ด‘์› ์กฐ๊ฑด์—์„œ๋„ ์ผ๊ด€๋œ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์˜ˆ์‹œ๋“ค์€ Barycentric coordinates๊ฐ€ 3D ๊ทธ๋ž˜ํ”ฝ์Šค์—์„œ ์–ผ๋งˆ๋‚˜ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๊ฐ ๊ธฐ์ˆ ๋“ค์€ Barycentric coordinates์˜ ๋ณด๊ฐ„ ํŠน์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๊ทธ๋ž˜ํ”ฝ์Šค๋ฅผ ๋ณด๋‹ค ํšจ์œจ์ ์ด๊ณ  ํ˜„์‹ค๊ฐ ์žˆ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

image

Barycentric Coordinates๋ฅผ ์ด์šฉํ•œ Shading๊ณผ Interpolation

Using Barycentric Coordinates

image

Figure 3: Barycentric coordinates๋Š” ๊ต์ฐจ์ ์—์„œ vertex data๋ฅผ interpolateํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” vertex color๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ P์˜ ์ƒ‰์ƒ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

Barycentric coordinates๋Š” shading์— ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‚ผ๊ฐํ˜•์€ ํ‰๋ฉด์ด๊ณ , ๊ฐ vertex์— ์ถ”๊ฐ€ ์ •๋ณด๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฐ๊ด€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ , ์ƒ‰์ƒ, ๋ฒกํ„ฐ ๋“ฑ). ์ด๋Ÿฌํ•œ ์ •๋ณด๋ฅผ vertex data๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, vertex A๋ฅผ ๋นจ๊ฐ„์ƒ‰, vertex B๋ฅผ ์ดˆ๋ก์ƒ‰, vertex C๋ฅผ ํŒŒ๋ž€์ƒ‰์œผ๋กœ ํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค.

๊ต์ฐจ์ ์ด ์‚ผ๊ฐํ˜•์˜ vertex ์ค‘ ํ•˜๋‚˜์™€ ์ผ์น˜ํ•˜๋ฉด, ๊ต์ฐจ์ ์—์„œ์˜ ๋ฌผ์ฒด ์ƒ‰์ƒ์€ ํ•ด๋‹น vertex์— ์—ฐ๊ด€๋œ ์ƒ‰์ƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜์ฃ . ๋ฌธ์ œ๋Š” ๊ด‘์„ ์ด ์‚ผ๊ฐํ˜•์˜ ๋‹ค๋ฅธ ๊ณณ(๋ชจ์„œ๋ฆฌ ๋˜๋Š” ๋‚ด๋ถ€)๊ณผ ๊ต์ฐจํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. Barycentric coordinates๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ผ๊ฐํ˜•์˜ vertex๋ฅผ ์‚ฌ์šฉํ•ด ์ ์˜ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•˜๋ฉด, ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ผ๊ฐํ˜•์˜ vertex์—์„œ ์ •์˜๋œ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ(์˜ˆ: ์ƒ‰์ƒ)๋ฅผ interpolate ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, Barycentric coordinates๋Š” vertex data๋ฅผ ์‚ผ๊ฐํ˜•์˜ ํ‘œ๋ฉด ์ „์ฒด์— ๊ฑธ์ณ interpolateํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค(์ด ๊ธฐ์ˆ ์€ float, color ๋“ฑ ์–ด๋–ค ๋ฐ์ดํ„ฐ ํƒ€์ž…์—๋„ ์ ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค). ์ด ๊ธฐ์ˆ ์€ ์˜ˆ๋ฅผ ๋“ค์–ด ๊ต์ฐจ์ ์—์„œ normal์„ interpolateํ•˜๊ธฐ ์œ„ํ•ด shading์— ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ์ฒด์˜ normal์€ face ๋˜๋Š” vertex ๊ธฐ์ค€์œผ๋กœ ์ •์˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์šฐ๋ฆฌ๋Š” face normal ๋˜๋Š” vertex normal์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค).

vertex ๊ธฐ์ค€์œผ๋กœ ์ •์˜๋œ ๊ฒฝ์šฐ, ์ด interpolate ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ผ๊ฐํ˜• ํ‘œ๋ฉด ์ „์ฒด์— ๊ฑธ์ณ ๋งค๋„๋Ÿฌ์šด shading์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ผ๊ฐํ˜•์€ โ€œ์ˆ˜ํ•™์ ์œผ๋กœโ€ ํ‰๋ฉด์ด์ง€๋งŒ, ๊ต์ฐจ์ ์—์„œ์˜ normal์€ vertex normal์˜ ์กฐํ•ฉ์ด๋ฏ€๋กœ, vertex normal์ด ์„œ๋กœ ๋‹ค๋ฅด๋ฉด ์ด interpolate์˜ ๊ฒฐ๊ณผ๋Š” ์‚ผ๊ฐํ˜• ํ‘œ๋ฉด ์ „์ฒด์—์„œ ์ผ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Barycentric coordinates๋Š” texture coordinates๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐ๋„ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ…์Šค์ฒ˜ ๋งคํ•‘(texture mapping)์—์„œ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์‚ผ๊ฐํ˜•์˜ ๊ฐ vertex์— ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ์ •์˜ํ•˜๊ณ , Barycentric coordinates๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ๋ชจ๋“  ์ ์— ๋Œ€ํ•ด ์ด ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ interpolateํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…์Šค์ฒ˜ ํ•ด์ƒ๋„๋Š” ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ์™€ ๊ด€๋ จ์ด ์žˆ์œผ๋ฉฐ, Barycentric coordinates๋ฅผ ํ†ตํ•ด ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ๊ฐ ํ”ฝ์…€์— ์ •ํ™•ํ•œ ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ Barycentric coordinates๋Š” ์‚ผ๊ฐํ˜• ๋‚ด๋ถ€์˜ ๋ชจ๋“  ์ ์—์„œ์˜ ์ƒ‰์ƒ์ด๋‚˜ ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

How to assign/calculate triangle texture coordinates

6. Texture image ์ƒ์„ฑ๊ณผ์ •

์ด ์ฝ”๋“œ๋Š” ๊ฐ ์‚ผ๊ฐํ˜•์˜ ๋‚ด๋ถ€ ํ”ฝ์…€์— ๋Œ€ํ•ด Barycentric coordinates๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅ ๋ชจ๋ธ ํŒŒ์ผ์—์„œ ์ฝ์–ด์˜จ UV ์ขŒํ‘œ์™€ ์ •์ ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ๊ณผ ํšŒ์ „ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ€๋„ ํ•„๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ๊ฐ ํ”ฝ์…€์˜ ํŠน์ง•์„ ๋ณด๊ฐ„ํ•˜์—ฌ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์— ๋ฐ˜์˜ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ฐ ํ”ฝ์…€์˜ ํŠน์ง•์€ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•„๋“œ์—์„œ ๊ณ„์‚ฐ๋œ opacities๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ€์žฅ ์˜ํ–ฅ๋ ฅ ์žˆ๋Š” ๊ฐ€์šฐ์‹œ์•ˆ์˜ ํŠน์ง•์„ ์„ ํƒํ•˜์—ฌ ๋ณด๊ฐ„๋˜๋ฉฐ, ์ตœ์ข…์ ์œผ๋กœ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์›ํ•˜๋Š” ๋ฐฉํ–ฅ๊ณผ ํ˜•์‹์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ชจ๋“  ์‚ผ๊ฐํ˜•์— ๋Œ€ํ•ด Barycentric coordinates๋ฅผ ํ™•์žฅ ๋ฐ ์žฌ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Barycentric coordinates๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ํ”ฝ์…€์˜ ๊ณต๊ฐ„ ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ ์‚ผ๊ฐํ˜•์˜ ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ๊ณผ ์Šค์ผ€์ผ ์กฐ์ • ํšŒ์ „ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ๊ณผ ํšŒ์ „ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ€๋„ ํ•„๋“œ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ€๋„ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ํ”ฝ์…€์˜ ํŠน์ง•์„ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณด๊ฐ„๋œ ํŠน์ง•์„ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • ์ตœ์ข…์ ์œผ๋กœ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์›ํ•˜๋Š” ๋ฐฉํ–ฅ๊ณผ ํ˜•์‹์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

1. ๋ชจ๋“  ์‚ผ๊ฐํ˜•์— ๋Œ€ํ•œ Barycentric coordinates ํ™•์žฅ ๋ฐ ์žฌ๊ตฌ์„ฑ

all_triangle_bary_coords = triangle_pixel_bary_coords[None].expand(n_squares, -1, -1, -1).reshape(-1, triangle_pixel_bary_coords.shape[-2], 3)
all_triangle_bary_coords = all_triangle_bary_coords[:len(faces_verts)]
  • triangle_pixel_bary_coords๋Š” ๊ฐ ์‚ผ๊ฐํ˜•์˜ ๋‚ด๋ถ€ ํ”ฝ์…€์— ๋Œ€ํ•œ Barycentric coordinates๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • expand์™€ reshape๋ฅผ ํ†ตํ•ด ๊ฐ ์‚ผ๊ฐํ˜•์— ๋Œ€ํ•ด ์ด ์ขŒํ‘œ๋“ค์„ ํ™•์žฅํ•˜๊ณ  ์žฌ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • all_triangle_bary_coords๋Š” ๋ชจ๋“  ์‚ผ๊ฐํ˜•์— ๋Œ€ํ•œ Barycentric coordinates๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

2. ํ”ฝ์…€ ๊ณต๊ฐ„ ์œ„์น˜ ๊ณ„์‚ฐ

pixels_space_positions = (all_triangle_bary_coords[..., None] * faces_verts[:, None]).sum(dim=-2)[:, :, None]
  • faces_verts๋Š” ๊ฐ ์‚ผ๊ฐํ˜•์˜ ์ •์  ์ขŒํ‘œ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • Barycentric coordinates์™€ ์ •์  ์ขŒํ‘œ๋ฅผ ๊ณฑํ•˜๊ณ  ํ•ฉ์‚ฐํ•˜์—ฌ ๊ฐ ํ”ฝ์…€์˜ ๊ณต๊ฐ„ ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • pixels_space_positions๋Š” ๋ชจ๋“  ์‚ผ๊ฐํ˜•์˜ ๋‚ด๋ถ€ ํ”ฝ์…€์— ๋Œ€ํ•œ ๊ณต๊ฐ„ ์ขŒํ‘œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

3. ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ ๋ฐ ์Šค์ผ€์ผ ์กฐ์ • ํšŒ์ „ ํ–‰๋ ฌ ๊ณ„์‚ฐ

gaussian_centers = rc.points.reshape(-1, 1, rc.n_gaussians_per_surface_triangle, 3)
gaussian_inv_scaled_rotation = rc.get_covariance(return_full_matrix=True, return_sqrt=True, inverse_scales=True).reshape(-1, 1, rc.n_gaussians_per_surface_triangle, 3, 3)
  • rc.points๋Š” ๊ฐ ํ‘œ๋ฉด ์‚ผ๊ฐํ˜•์˜ ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  • get_covariance๋ฅผ ํ†ตํ•ด ๊ฐ ๊ฐ€์šฐ์‹œ์•ˆ์˜ ์Šค์ผ€์ผ ์กฐ์ • ๋ฐ ํšŒ์ „ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • gaussian_centers์™€ gaussian_inv_scaled_rotation์€ ๊ฐ ์‚ผ๊ฐํ˜•์˜ ๊ฐ€์šฐ์‹œ์•ˆ ์ •๋ณด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

4. ๋ฐ€๋„ ํ•„๋“œ ๊ณ„์‚ฐ

shift = (pixels_space_positions - gaussian_centers)
warped_shift = gaussian_inv_scaled_rotation.transpose(-1, -2) @ shift[..., None]
neighbor_opacities = (warped_shift[..., 0] * warped_shift[..., 0]).sum(dim=-1).clamp(min=0., max=1e8)
  • pixels_space_positions์™€ gaussian_centers์˜ ์ฐจ์ด๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ shift๋ฅผ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
  • gaussian_inv_scaled_rotation์„ ์‚ฌ์šฉํ•˜์—ฌ shift๋ฅผ ์Šค์ผ€์ผ ๋ฐ ํšŒ์ „ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณ€ํ™˜๋œ shift๋ฅผ ํ†ตํ•ด ๊ฐ ๊ฐ€์šฐ์‹œ์•ˆ์˜ ๋ฐ€๋„๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ด๋ฅผ neighbor_opacities์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • warped_shift๋Š” ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ์—์„œ ํ”ฝ์…€ ๊ณต๊ฐ„ ์œ„์น˜๋กœ์˜ ๋ณ€์œ„๋ฅผ ์Šค์ผ€์ผ ๋ฐ ํšŒ์ „ ๋ณ€ํ™˜ํ•˜์—ฌ ์กฐ์ •ํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ํ”ฝ์…€์ด ๊ฐ€์šฐ์‹œ์•ˆ์— ์˜ํ•ด ์–ผ๋งˆ๋‚˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š”์ง€๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ”ฝ์…€์˜ ํŠน์ง•์„ ๋ณด๊ฐ„ํ•˜์—ฌ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.
  • warped_shift์˜ ๊ฐ ์š”์†Œ๋ฅผ ์ œ๊ณฑํ•˜์—ฌ ํ•ฉ์‚ฐํ•œ ๋‹ค์Œ, ์Œ์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด clamp๋กœ ์ตœ์†Œ๊ฐ’์„ 0์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • warped_shift์˜ ์ œ๊ณฑํ•ฉ์„ ์ด์šฉํ•ด ๊ฐ ํ”ฝ์…€์˜ ๊ฐ€์šฐ์‹œ์•ˆ ์˜ํ–ฅ์„ ํ‰๊ฐ€ํ•˜์—ฌ neighbor_opacities๋ฅผ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ™์ด ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    neighbor_opacities = torch.exp(-1. / 2 * neighbor_opacities)
    
  • torch.exp(-1. / 2 * neighbor_opacities)๋Š” ๊ฐ€์šฐ์‹œ์•ˆ ํ•จ์ˆ˜๋กœ ๋ณ€์œ„(warped_shift)๋ฅผ ๋ฐ€๋„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์€ ๊ฐ€์šฐ์‹œ์•ˆ ๋ถ„ํฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ ํ”ฝ์…€์ด ๊ฐ€์šฐ์‹œ์•ˆ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ์ •๋„๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์›๋ž˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์™€ ํ˜„์žฌ ์ฝ”๋“œ์—์„œ ๊ณ„์‚ฐํ•œ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์˜ ๋น„๊ต

์›๋ž˜์˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜(์ •๊ทœ ๋ถ„ํฌ ํ•จ์ˆ˜)๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

\[f(x) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(x - \mu)^2}{2\sigma^2}\right)\]

์—ฌ๊ธฐ์„œ:

  • $x$๋Š” ๋ณ€์ˆ˜
  • $\mu$๋Š” ํ‰๊ท  (mean)
  • $\sigma$๋Š” ํ‘œ์ค€ํŽธ์ฐจ (standard deviation)
  • $\exp$๋Š” ์ง€์ˆ˜ ํ•จ์ˆ˜ (exponential function)

ํ˜„์žฌ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•œ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹จ์ˆœํ™”๋œ ํ˜•ํƒœ๋กœ ์ ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

\[\mathrm{neighbor\_opacities} = \exp\left(-\frac{1}{2} \sum (\mathrm{warped\_shift}_{ij}^2)\right)\]

์—ฌ๊ธฐ์„œ:

  • $\mathrm{warped_shift}_{ij}$๋Š” ์Šค์ผ€์ผ ๋ฐ ํšŒ์ „ ๋ณ€ํ™˜๋œ ๋ณ€์œ„์ž…๋‹ˆ๋‹ค.

์ฐจ์ด์ 

์ •๊ทœํ™” ์ƒ์ˆ˜์˜ ๋ถ€์žฌ:

์›๋ž˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์—์„œ๋Š” ์ •๊ทœํ™” ์ƒ์ˆ˜ $\frac{1}{\sqrt{2\pi\sigma^2}}$๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€๋งŒ, ํ˜„์žฌ ์ฝ”๋“œ์—์„œ๋Š” ์ƒ๋žต๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํžˆ ์ƒ๋Œ€์ ์ธ ๋ฐ€๋„ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋žต๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

๋ถ„์‚ฐ ($\sigma^2$)์˜ ์ƒ๋žต:

์›๋ž˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์—์„œ๋Š” ๋ถ„์‚ฐ $\sigma^2$๊ฐ€ ํฌํ•จ๋˜์ง€๋งŒ, ํ˜„์žฌ ์ฝ”๋“œ์—์„œ๋Š” ๋ถ„์‚ฐ์„ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ๋ณ€์œ„์˜ ์ œ๊ณฑ ํ•ฉ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์—์„œ ๋ณ€์œ„๊ฐ€ ์ด๋ฏธ ์Šค์ผ€์ผ ์กฐ์ •๋œ ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•œ ๋‹จ์ˆœํ™”์ž…๋‹ˆ๋‹ค.

๋ณ€์œ„ ๊ณ„์‚ฐ ๋ฐฉ์‹:

์›๋ž˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์—์„œ๋Š” $(x - \mu)^2$๋กœ ๋ณ€์œ„๋ฅผ ๊ณ„์‚ฐํ•˜์ง€๋งŒ, ํ˜„์žฌ ์ฝ”๋“œ์—์„œ๋Š” $\mathrm{warped_shift}_{ij}^2$๋กœ ๋ณ€์œ„๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. $\mathrm{warped_shift}$๋Š” ์ด๋ฏธ ๊ฐ€์šฐ์‹œ์•ˆ ์ค‘์‹ฌ๊ณผ์˜ ์ฐจ์ด๋ฅผ ์Šค์ผ€์ผ ๋ฐ ํšŒ์ „ ๋ณ€ํ™˜ํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค.

์š”์•ฝ

ํ˜„์žฌ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•œ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜๋Š” ์›๋ž˜ ๊ฐ€์šฐ์‹œ์•ˆ ๋ฐ€๋„ ํ•จ์ˆ˜์—์„œ ์ •๊ทœํ™” ์ƒ์ˆ˜์™€ ๋ถ„์‚ฐ์„ ์ƒ๋žตํ•˜๊ณ  ๋ณ€์œ„์˜ ์ œ๊ณฑ ํ•ฉ๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ๋Œ€์ ์ธ ๋ฐ€๋„๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋‹จ์ˆœํ™”๋œ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ณ„์‚ฐ์˜ ๊ฐ„๊ฒฐ์„ฑ๊ณผ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ ์ ‘๊ทผ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

5. ํ”ฝ์…€ ํŠน์ง• ๋ณด๊ฐ„

pixel_features = faces_features[:, None].expand(-1, neighbor_opacities.shape[1], -1, -1).gather(
    dim=-2,
    index=neighbor_opacities[..., None].argmax(dim=-2, keepdim=True).expand(-1, -1, -1, 3)
    )[:, :, 0, :]
  • faces_features๋Š” ๊ฐ ์‚ผ๊ฐํ˜•์˜ ์ •์  ํŠน์ง•์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • neighbor_opacities์˜ ์ตœ๋Œ€๊ฐ’ ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์žฅ ์˜ํ–ฅ๋ ฅ ์žˆ๋Š” ๊ฐ€์šฐ์‹œ์•ˆ์˜ ํŠน์ง•์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  • ์„ ํƒ๋œ ํŠน์ง•์„ ํ†ตํ•ด ํ”ฝ์…€์˜ ํŠน์ง•์„ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค.

6. ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€ ์ฑ„์šฐ๊ธฐ

texture_img[(triangle_pixel_idx[..., 0], triangle_pixel_idx[..., 1])] = pixel_features
  • ๋ณด๊ฐ„๋œ ํ”ฝ์…€ ํŠน์ง•์„ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€(texture_img)์˜ ์ ์ ˆํ•œ ์œ„์น˜์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

7. ์ตœ์ข… ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€ ๋ณ€ํ™˜

texture_img = texture_img.transpose(0, 1)
texture_img = SH2RGB(texture_img.flip(0))
  • ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. transpose์™€ flip์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์›ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • SH2RGB ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ RGB ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

8. PyTorch3D์—์„œ Mesh Rasterization ๋ฐ Texture Mapping ์„ค์ •

faces_per_pixel = 1
    max_faces_per_bin = 50_000
    mesh_raster_settings = RasterizationSettings(
        image_size=(rc.image_height, rc.image_width),
        blur_radius=0.0, 
        faces_per_pixel=faces_per_pixel,
        # max_faces_per_bin=max_faces_per_bin
    )
    lights = AmbientLights(device=rc.device)
    rasterizer = MeshRasterizer(
            cameras=rc.nerfmodel.training_cameras.p3d_cameras[0], 
            raster_settings=mesh_raster_settings,
    )
    renderer = MeshRenderer(
        rasterizer=rasterizer,
        shader=SoftPhongShader(
            device=rc.device, 
            cameras=rc.nerfmodel.training_cameras.p3d_cameras[0],
            lights=lights,
            blend_params=BlendParams(background_color=(0.0, 0.0, 0.0)),
        )
    )
    texture_idx = torch.cartesian_prod(
        torch.arange(texture_size, device=rc.device), 
        torch.arange(texture_size, device=rc.device)
        ).reshape(texture_size, texture_size, 2
                    )
    texture_idx = torch.cat([texture_idx, torch.zeros_like(texture_idx[..., 0:1])], dim=-1)
    texture_counter = torch.zeros(texture_size, texture_size, 1, device=rc.device)
    idx_textures_uv = TexturesUV(
        maps=texture_idx[None].float(), #texture_img[None]),
        verts_uvs=verts_uv[None],
        faces_uvs=faces_uv[None],
        sampling_mode='nearest',
        )
    idx_mesh = Meshes(
        verts=[rc.surface_mesh.verts_list()[0]],   
        faces=[rc.surface_mesh.faces_list()[0]],
        textures=idx_textures_uv,
        )

์ฐจ์› ํ™•์žฅ

๊ธฐ๋ณธ์ ์œผ๋กœ, PyTorch์—์„œ None์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฐจ์›์„ ํ™•์žฅํ•˜๋ฉด ์ฐจ์›์„ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ…์„œ์˜ ๋ฐฐ์น˜(batch) ์ฐจ์›์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์—ฐ์‚ฐ์—์„œ ํ•„์š”ํ•œ ํ˜•ํƒœ๋กœ ํ…์„œ๋ฅผ ๋งž์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

์ฃผ์–ด์ง„ ์ฝ”๋“œ์—์„œ verts_uv, faces_uv, texture_idx์˜ ์ฐจ์›์„ ํ™•์žฅํ•˜๋Š” ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์›๋ž˜ ํ…์„œ์˜ ๋ชจ์–‘

  • verts_uv: UV ์ขŒํ‘œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ…์„œ. ๋ชจ์–‘์€ (N, 2)์ž…๋‹ˆ๋‹ค.
  • faces_uv: ๊ฐ ์–ผ๊ตด(face)์— ๋Œ€ํ•œ UV ์ธ๋ฑ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ…์„œ. ๋ชจ์–‘์€ (M, 3)์ž…๋‹ˆ๋‹ค.
  • texture_idx: ํ…์Šค์ฒ˜ ์ขŒํ‘œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ…์„œ. ๋ชจ์–‘์€ (texture_size, texture_size, 2)์ž…๋‹ˆ๋‹ค.

์ฐจ์› ํ™•์žฅ

  • verts_uv[None]:
    • ์›๋ž˜ ํ…์„œ ๋ชจ์–‘: (N, 2)
    • ํ™•์žฅ๋œ ํ…์„œ ๋ชจ์–‘: (1, N, 2)
  • faces_uv[None]:
    • ์›๋ž˜ ํ…์„œ ๋ชจ์–‘: (M, 3)
    • ํ™•์žฅ๋œ ํ…์„œ ๋ชจ์–‘: (1, M, 3)
  • texture_idx[None]:
    • ์›๋ž˜ ํ…์„œ ๋ชจ์–‘: (texture_size, texture_size, 2)
    • ํ™•์žฅ๋œ ํ…์„œ ๋ชจ์–‘: (1, texture_size, texture_size, 2)

9. PyTorch3D๋ฅผ ์‚ฌ์šฉํ•œ 3D Mesh ํ…์Šค์ฒ˜ ์—…๋ฐ์ดํŠธ: ํ‰๊ท ๊ฐ’ ๊ณ„์‚ฐ ๋ฐ ๋ Œ๋”๋ง ๊ณผ์ •

    
    texture_counter = torch.zeros(texture_size, texture_size, 1, device=rc.device)

    ...

    for cam_idx in range(len(rc.nerfmodel.training_cameras)):
        p3d_cameras = rc.nerfmodel.training_cameras.p3d_cameras[cam_idx]
        
        # Render rgb img
        rgb_img = rc.render_image_gaussian_rasterizer(
            camera_indices=cam_idx,
            sh_deg=0,  #rc.sh_levels-1,
            compute_color_in_rasterizer=True,  #compute_color_in_rasterizer,
        ).clamp(min=0, max=1)
        
        fragments = renderer.rasterizer(idx_mesh, cameras=p3d_cameras)
        idx_img = renderer.shader(fragments, idx_mesh, cameras=p3d_cameras)[0, ..., :2]
        # print("Idx img:", idx_img.shape, idx_img.min(), idx_img.max())
        update_mask = fragments.zbuf[0, ..., 0] > 0
        idx_to_update = idx_img[update_mask].round().long() 

        use_average = True
        if not use_average:
            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = rgb_img[update_mask]
        else:
            no_initialize_mask = texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])][..., 0] != 0
            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = no_initialize_mask[..., None] * texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])]

            texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] = texture_img[(idx_to_update[..., 0], idx_to_update[..., 1])] + rgb_img[update_mask]
            texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])] = texture_counter[(idx_to_update[..., 0], idx_to_update[..., 1])] + 1

    if use_average:
        texture_img = texture_img / texture_counter.clamp(min=1)        
    
    return verts_uv, faces_uv, texture_img
  • ๊ฐ ์นด๋ฉ”๋ผ์— ๋Œ€ํ•ด 3D mesh๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
  • ๋ Œ๋”๋ง๋œ RGB ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ texture ์ด๋ฏธ์ง€๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  • use_average ํ”Œ๋ž˜๊ทธ์— ๋”ฐ๋ผ texture ์ด๋ฏธ์ง€๋ฅผ ํ‰๊ท  ๊ฐ’์œผ๋กœ ๊ณ„์‚ฐํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ์ตœ์ข…์ ์œผ๋กœ UV coordinates (verts_uv), UV indices (faces_uv), ๊ทธ๋ฆฌ๊ณ  texture image (texture_img)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ๊ณผ์ •์€ ์—ฌ๋Ÿฌ ์นด๋ฉ”๋ผ ๋ทฐ์—์„œ ๋ Œ๋”๋ง๋œ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ข… texture ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ mesh์˜ UV map์— ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

์ตœ์ข…์š”์•ฝ

  • vertices_uv: ๊ฐ ์ •์‚ฌ๊ฐํ˜• ์…€์˜ ์ •์  UV ์ขŒํ‘œ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ (0,0)์—์„œ (1,1) ์‚ฌ์ด์˜ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • faces_uv: ๊ฐ ์‚ผ๊ฐํ˜• ๋ฉด์˜ UV ์ขŒํ‘œ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • UV ์ขŒํ‘œ ์Šค์ผ€์ผ๋ง ๋ฐ ์ •๊ทœํ™”: square_size์™€ texture_size์— ๋”ฐ๋ผ UV ์ขŒํ‘œ๋ฅผ ์Šค์ผ€์ผ๋งํ•˜๊ณ  ์ •๊ทœํ™”ํ•˜์—ฌ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€์˜ ๊ฐ ํ”ฝ์…€์ด ํ…์Šค์ฒ˜์˜ ์–ด๋–ค ์œ„์น˜์— ๋Œ€์‘๋˜๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€ ์ดˆ๊ธฐํ™” ๋ฐ ํ”ฝ์…€ ์ธ๋ฑ์Šค ๊ณ„์‚ฐ: ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ๊ฐ ์‚ผ๊ฐํ˜•์˜ ํ”ฝ์…€ ์ธ๋ฑ์Šค๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • barycentric coordinate ๋ฐ density field ์ƒ์„ฑ: barycentric coordinate๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด local gaussian ๋ถˆํˆฌ๋ช…๋„์˜ ํ•ฉ์œผ๋กœ ๋ฐ€๋„ ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ํ”ฝ์…€ ํŠน์ง• ๊ณ„์‚ฐ ๋ฐ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ: barycentric coordinate๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ํ”ฝ์…€์˜ ํŠน์ง•์„ ๊ณ„์‚ฐํ•˜๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ…์Šค์ฒ˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

Leave a comment