Chuyển đến nội dung chính

第 18 課:服裝渲染 — 使用多重視野在 Avatar 上渲染衣服

布料模擬與渲染:將設計懸垂到 3D 身體、織物物理、皺紋生成。多視圖輸出:前視圖、側視圖、後視圖。照明和材質系統。

🧠 人工智慧與機器學習 — 第 17 課 第 18 課:服裝渲染 — 渲染襯衫 多元視角頭像

人工智慧在行動:建構時尚和按需印刷的人工智慧平台

第 5 部分:虛擬試戴與電腦視覺

亞洲開發網

簡介

頭像在那裡,設計也在那裡——現在我們需要將襯衫渲染到頭像上。這不僅是將紋理貼平,還必須有布料模擬:布料必須自然垂墜,褶皺處有皺紋,設計必須根據襯衫的表面變形。


1. 服裝渲染管線

Design (PNG)     +     Avatar (3D mesh)     +     Garment Template
     │                       │                         │
     └───────────┬───────────┘                         │
                 │                                     │
        ┌────────▼────────┐              ┌─────────────▼──────────────┐
        │ UV Mapping       │              │ Cloth Simulation            │
        │ Design → texture │              │ Draping, wrinkles, gravity │
        └────────┬────────┘              └─────────────┬──────────────┘
                 │                                     │
                 └───────────────┬─────────────────────┘
                                 │
                        ┌────────▼────────┐
                        │ Final Rendering  │
                        │ Lighting, shadow │
                        │ Multi-view output│
                        └─────────────────┘
                                 │
                    ┌────────────┼────────────┐
                    ▼            ▼            ▼
               Front View   Side View   Back View

2.服裝模板系統

class GarmentTemplate:
    """Template 3D mesh cho các loại áo"""

    TEMPLATES = {
        "tshirt_regular": {
            "mesh": "garments/tshirt_regular.obj",
            "uv_map": "garments/tshirt_regular_uv.png",
            "print_zones": {
                "front_chest": {"uv_range": (0.2, 0.15, 0.8, 0.65)},
                "back_print": {"uv_range": (0.2, 0.15, 0.8, 0.70)},
                "left_chest": {"uv_range": (0.55, 0.20, 0.75, 0.40)},
            },
        },
        "tshirt_oversize": {
            "mesh": "garments/tshirt_oversize.obj",
            "uv_map": "garments/tshirt_oversize_uv.png",
            "print_zones": {
                "front_chest": {"uv_range": (0.15, 0.12, 0.85, 0.68)},
                "back_print": {"uv_range": (0.15, 0.12, 0.85, 0.72)},
            },
        },
    }

    def get_template(
        self, garment_type: str, size: str
    ) -> trimesh.Trimesh:
        template = self.TEMPLATES[garment_type]
        mesh = trimesh.load(template["mesh"])

        # Scale mesh theo size
        scale_factor = self._get_scale(size)
        mesh.apply_scale(scale_factor)

        return mesh

3.設計→服裝紋理

class DesignToTexture:
    """Map design lên UV texture của áo"""

    def apply_design(
        self,
        garment_template: dict,
        design: Image.Image,
        print_zone: str = "front_chest",
        shirt_color: str = "#FFFFFF",
    ) -> Image.Image:
        """
        Tạo texture image cho garment mesh

        1. Start với shirt_color background
        2. Map design vào đúng UV zone
        """
        # Create base texture (shirt color)
        texture_size = 2048
        texture = Image.new(
            "RGBA", (texture_size, texture_size),
            self._hex_to_rgba(shirt_color),
        )

        # Get UV range for print zone
        zone = garment_template["print_zones"][print_zone]
        uv = zone["uv_range"]  # (u_min, v_min, u_max, v_max)

        # Map design to UV coordinates
        target_x = int(uv[0] * texture_size)
        target_y = int(uv[1] * texture_size)
        target_w = int((uv[2] - uv[0]) * texture_size)
        target_h = int((uv[3] - uv[1]) * texture_size)

        # Resize design to fit zone
        design_resized = design.resize(
            (target_w, target_h),
            Image.Resampling.LANCZOS,
        )

        # Paste design onto texture
        texture.paste(design_resized, (target_x, target_y), design_resized)

        return texture

4.布料模擬(簡化)

class ClothSimulator:
    """Simulate cloth draping trên body"""

    def drape_garment(
        self,
        garment_mesh: trimesh.Trimesh,
        body_mesh: trimesh.Trimesh,
    ) -> trimesh.Trimesh:
        """
        Basic cloth simulation:
        1. Fit garment roughly to body
        2. Push garment vertices outward from body surface
        3. Add wrinkle displacement
        """
        # 1. Align garment to body
        garment_aligned = self._align_to_body(
            garment_mesh, body_mesh
        )

        # 2. Collision detection & resolution
        garment_fitted = self._resolve_collisions(
            garment_aligned, body_mesh,
            offset=0.5,  # 0.5cm clearance
        )

        # 3. Add wrinkles
        garment_wrinkled = self._add_wrinkles(
            garment_fitted, body_mesh
        )

        return garment_wrinkled

    def _resolve_collisions(
        self,
        garment: trimesh.Trimesh,
        body: trimesh.Trimesh,
        offset: float,
    ) -> trimesh.Trimesh:
        """Push garment vertices ở bên trong body ra ngoài"""
        import numpy as np

        vertices = garment.vertices.copy()

        # For each garment vertex, check if inside body
        for i, v in enumerate(vertices):
            # Find closest point on body surface
            closest, distance, face_id = \
                body.nearest.on_surface([v])

            if distance[0] < offset:
                # Push outward along body surface normal
                normal = body.face_normals[face_id[0]]
                vertices[i] = closest[0] + normal * offset

        garment.vertices = vertices
        return garment

    def _add_wrinkles(
        self,
        garment: trimesh.Trimesh,
        body: trimesh.Trimesh,
    ) -> trimesh.Trimesh:
        """Add realistic wrinkle displacement"""
        import numpy as np

        vertices = garment.vertices.copy()
        normals = garment.vertex_normals

        # Simple noise-based wrinkles
        noise = np.random.normal(0, 0.1, vertices.shape)

        # More wrinkles at joints (armpits, waist)
        for i, v in enumerate(vertices):
            # Armpit zone: more wrinkle amplitude
            if -0.15 < v[0] < 0.15 and 0.1 < v[1] < 0.3:
                noise[i] *= 2.5
            # Waist zone
            if v[1] < -0.2:
                noise[i] *= 1.5

        vertices += normals * noise * 0.003
        garment.vertices = vertices

        return garment

5. 多視圖渲染

class MultiViewRenderer:
    """Render avatar + garment từ nhiều góc"""

    def render_views(
        self,
        avatar: AvatarMesh,
        garment: trimesh.Trimesh,
        views: list[str] = ["front", "side", "back"],
    ) -> dict[str, Image.Image]:
        scene = self._create_scene(avatar, garment)
        results = {}

        camera_angles = {
            "front": (0, 0, 3),         # z-axis front
            "side": (3, 0, 0),          # x-axis side
            "back": (0, 0, -3),         # -z-axis back
            "three_quarter": (2, 0.5, 2), # 3/4 view
        }

        for view_name in views:
            camera_pos = camera_angles[view_name]
            image = self._render_from_angle(
                scene, camera_pos
            )
            results[view_name] = image

        return results

    def _create_scene(
        self, avatar: AvatarMesh, garment: trimesh.Trimesh
    ) -> trimesh.Scene:
        scene = trimesh.Scene()
        scene.add_geometry(avatar.mesh, node_name="body")
        scene.add_geometry(garment, node_name="garment")
        return scene

    def _render_from_angle(
        self,
        scene: trimesh.Scene,
        camera_position: tuple,
        resolution: tuple = (800, 1200),
    ) -> Image.Image:
        """Render scene from specific camera angle"""
        import pyrender

        # Convert trimesh scene to pyrender
        pr_scene = pyrender.Scene()

        for name, mesh in scene.geometry.items():
            pr_mesh = pyrender.Mesh.from_trimesh(mesh)
            pr_scene.add(pr_mesh)

        # Camera
        camera = pyrender.PerspectiveCamera(yfov=np.pi / 4)
        camera_pose = self._look_at(
            eye=camera_position,
            target=(0, 0, 0),
            up=(0, 1, 0),
        )
        pr_scene.add(camera, pose=camera_pose)

        # Lights
        light = pyrender.DirectionalLight(
            color=[1.0, 1.0, 1.0], intensity=3.0
        )
        pr_scene.add(light, pose=camera_pose)

        # Render
        renderer = pyrender.OffscreenRenderer(*resolution)
        color, depth = renderer.render(pr_scene)
        renderer.delete()

        return Image.fromarray(color)

總結

服裝效果圖:

  1. 服裝模板 — 用於襯衫的預先建造 3D 網格
  2. UV 映射 — 將設計映射到紋理上的正確列印區域
  3. 布料模擬——碰撞解決、皺紋生成
  4. 多視圖 — 渲染前視圖、側視圖、後視圖、四分之三視圖
  5. Pyrender — 用於伺服器端影像產生的離屏渲染

下一篇文章:即時虛擬試穿 — 在瀏覽器中進行 360° 旋轉和動畫。