[3D CV ์ฐ๊ตฌ] 3DGS SuGaR sugar_model.py extract_texture_image_and_uv_from_gaussians
UV map์ texture 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์ ๋๋ค.
-
์ด texture image๋ ๋จผ์ ์ ์ํ uv coordinates์์ ์ขํ์์ ์ ์๋์์ ๊ฒ์ ๋๋ค.
-
๊ทธ๋ฆฌ๊ณ ์ด texture๋ gaussian render๋ฅผ ์ฌ์ฉํ์ฌ ๋ง๋ค์ด์ก์ต๋๋ค.
gaussians renderers๋ก ๋ง๋ค์ด์ง texture๊ฐ ์ด๋ป๊ฒ ๋ง๋ค์ด์ก๋์ง ๋ ์์ธํ ์ดํด๋ด ์๋ค.
- ๋จผ์ blender์์ OBJ ํ์ผ์ ์ด์ด์ฃผ๊ณ , Material Preview ๋ชจ๋๋ก ๋ฐ๊ฟ์ค๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ์ฐ์ธกํ๋จ์ ์ฐํด๋ฆญ์ ํ์ฌ Scene Statistics๋ฅผ ์ผ์ค์, Vertices, Faces์ ๊ฐ์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
- ์ฐ์ธกํ๋จ์์ Verts: 1,023,699๊ฐ / Faces: 1,997,996๊ฐ์์ ํ์ตํ ์ ์์ต๋๋ค.
- ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด, 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๊น์ง ํ ์ํ๋ ์๋๋๋ค.)
- Bottom ์ผ๊ฐํ:
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)
- ์ ๊ทํ๋ UV ์ขํ๋ ๋ค์๊ณผ ๊ฐ์ด ๋์ต๋๋ค:
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.
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์ ๊ตฌํ ์ ์๊ฒ ๋ฉ๋๋ค.
- ์ค์ ๋ก 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
- 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์ด๋ผ๊ณ ํ๋ฉฐ, ์ปดํจํฐ ๊ทธ๋ํฝ์ค์์ ๋งค์ฐ ์ ์ฉํ ๋ฐฉ๋ฒ์ ๋๋ค.
- ์ P๊ฐ triangle์ ์์ ์์นํ๋ ค๋ฉด ฮฒ+ฮณโค1์ด์ด์ผํ๊ณ , ฮฒ,ฮณโฅ0์ด์ด์ผ ํ๋ค๊ณ ๋ฐฐ์ ์ต๋๋ค.
- ์ ์์ ๋ค์๊ณผ ๊ฐ์ด notation์ ๋ค๋ฅด๊ฒํด์ ์ธ ์๋ ์์ต๋๋ค. (triangle์ 3๊ฐ์ ์ ์ A,B,C์ weights์ธ w1,w2)
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์ ๋ณด๊ฐ ํน์ฑ์ ํ์ฉํ์ฌ ๋ณต์กํ ๊ทธ๋ํฝ์ค๋ฅผ ๋ณด๋ค ํจ์จ์ ์ด๊ณ ํ์ค๊ฐ ์๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
Barycentric Coordinates๋ฅผ ์ด์ฉํ Shading๊ณผ Interpolation
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