Community이미지github.com

TraX22/blender-low-poly

Blender 연결(공식 BlenderLab 서버)을 통해 스타일화된 로우폴리 모델과 씬을 빌드합니다. 사용자가 로우폴리, 패싯 스타일, 카툰 또는 게임 스타일 모델을 요청할 때 사용합니다.

blender-low-poly란 무엇인가요?

이 스킬은 BlenderLab 서버를 통해 Blender에서 로우폴리 모델링을 수행하는 AI 에이전트 가이드입니다. 로우폴리 기법은 단순히 폴리곤 수를 낮추는 것이 아니라, 큰 형태, 평평한 쉐이딩, 제한된 색상 팔레트, 과장된 비율을 특징으로 하는 스타일을 구현합니다. 에이전트는 BlenderLab 도구(예: `execute_blender_code`)를 사용하여 Blender와 상호작용하며, Python 스크립트를 실행하고 뷰포트 스크린샷을 캡처합니다. 이 스킬은 9가지 규칙(플랫 쉐이딩, 적은 세그먼트, 베벨/서브디비전 금지, 단색 재질, 의도적인 팔레트, 과장된 비율, 수동 불규칙성, 세부 모듈 분리, 부품 간 교차 허용)을 제공하며, 최종 검사 함수(`final_check`)를 통해 렌더링 품질을 측정합니다. 주로 게임 엔진용 에셋을 대상으로 하며, EEVEE 렌더러와 한 방향성 조명을 사용합니다. 이 스킬은 `blender-modeling` 스킬과 함께 사용되며, 레시피는 `references/low-poly-recipes.md` 파일에 저장되어 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/TraX22/HydraOps-Skills/tree/HEAD/blender-low-poly

Installed? Explore more 이미지 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Blender에서 로우폴리 모델링

로우폴리는 스타일입니다. 단순히 낮은 삼각형 개수가 아니라, 크고 읽기 쉬운 형태, 눈에 띄는 평평한 면, 몇 가지 단색, 그리고 현실보다 약간 과장된 비율입니다. 폴리곤 수가 적은 사실적인 모델은 로우폴리가 아닙니다. 그것은 거친 사실적 모델일 뿐입니다.

일반적인 작업 루틴(씬을 보고, 부분별로 계획하고, 호출당 한 부분씩 작업하고, 각 단계 후 스크린샷을 찍고, 자신이 만들지 않은 것은 절대 삭제하지 않음)은 blender-modeling 스킬에 있습니다. 그것을 따르세요. 이 스킬은 로우폴리에서 무엇이 다른지 설명합니다.

Blender 연결

BlenderLab을 통해 작업합니다. 공식 Blender Lab 서버이며, 해당 도구는 blenderlab_…입니다:

  • 씬에 있는 것: get_objects_summary; 하나의 오브젝트를 자세히: get_object_detail_summary (name으로). 파일(저장 여부 및 경로): get_blendfile_summary_path_info.
  • Python 실행: execute_blender_code. 호출 사이에 아무것도 유지되지 않습니다(각 스크립트에 도우미 함수를 포함시키세요). 스크립트가 출력하는 내용이 반환되고, result에 할당된 딕셔너리가 데이터로 반환됩니다. 가드는 일부 연산자(Blender 종료, 공장 초기화)를 거부합니다.
  • 3D 뷰포트 보기: get_screenshot_of_area_as_image (with area_ui_type: "VIEW_3D"); 전체 창: get_screenshot_of_window_as_image); 오브젝트를 가리키기: jump_to_view3d_object_by_name.
  • Python API 확인: get_python_api_docs (bpy.types.BevelModifier와 같은 식별자), search_api_docs; 매뉴얼: search_manual_docs.
  • 빠른 렌더링: render_thumbnail_to_path, render_viewport_to_path; 이미지는 Blender의 임시 폴더로 이동하고 결과가 실제 경로를 제공합니다.
  • 내보내기: 코드 (bpy.ops.export_scene.gltf, bpy.ops.export_scene.fbx).
  • 에셋 라이브러리 및 3D 생성기: 없음. 오브젝트를 직접 만들거나, 사용자에게 가져올 파일을 요청하세요.

이러한 도구를 사용할 수 없으면 중단하고 그렇게 말하세요. 사용자는 BlenderLab 연결(도구 → 연결)을 설치하고, 이 에이전트에 제공하고, 애드온의 서버가 시작된 상태로 Blender를 열어야 합니다. 할 수 없는 작업을 완료된 것으로 설명하지 마세요.

스타일, 규칙으로

  1. 모든 곳에서 플랫 쉐이딩. 모든 면은 하나의 평평한 색상입니다. 부드러운 쉐이딩이나 자동 스무스가 없습니다. 이것이 면이 보이게 만드는 것입니다.
  2. 의도적으로 적은 세그먼트. 실린더는 6-8면, 원뿔은 5-6면, 구는 1-2단계 세분화된 아이코스피어로 만듭니다. 32면 실린더는 무엇보다도 룩을 망칩니다.
  3. 베벨, 서브디비전 서페이스 없음. 면을 둥글게 만듭니다. 더 부드러운 가장자리가 필요하면 최대 단일 세그먼트 모따기를 사용하세요.
  4. 단색, 텍스처 없음. 색상당 하나의 재질, Base Color가 있는 Principled BSDF, Roughness 0.8-1.0, Metallic 0, 스페큘러 광택 없음. 재질을 재사용하세요. 전체 씬에는 보통 6-12개가 필요합니다.
  5. 의도적인 팔레트. 모델링 전에 선택하세요: 1-2개의 지배적인 색상, 2-3개의 보조 색상, 1개의 악센트 색상. 약간 채도가 낮은 중간톤, 따뜻함과 차가움의 균형, 그리고 이웃하는 부분 간의 다양성을 위해 각 주요 색상의 두 가지 톤(밝음과 어두움).
  6. 통통하고 과장된 비율. 실제보다 두꺼운 벽, 기둥, 줄기; 과도하게 큰 지붕, 문, 창문; 테이퍼진 형태(밑부분이 더 넓거나, 카툰 느낌을 위해 윗부분이 더 넓음). 이 수준의 디테일에서는 얇은 부분이 사라집니다.
  7. 손으로, 소량으로 불완전함 만들기. 완벽하게 정렬된 것은 없습니다: 굴뚝을 2-4° 기울이고, 기와의 크기를 몇 퍼센트, 기울기를 2-3° 변형하고, 바위와 지형의 정점을 무작위로 이동시킵니다. 구조는 여전히 읽을 수 있어야 합니다: 기와 줄은 줄로 유지되고, 어떤 조각도 옆으로 돌리거나 표면에서 들어 올리지 않습니다. 고정된 무작위 시드를 사용하여 재실행 시 동일한 결과를 얻습니다.
  8. 세분화가 아닌 별도의 작은 조각을 통한 디테일. 창문은 네 개의 상자로 만든 프레임에 유리 한 장; 기와는 작은 슬래브의 줄; 돌은 벽에 박힌 몇 개의 불규칙한 블록입니다. 각 조각은 단순합니다. 배열이 풍부함을 제공합니다.
  9. 조각은 교차할 수 있음. 로우폴리 부품은 용접되는 대로 서로 밀어 넣습니다: 빔이 벽에 파고들고, 바위가 땅에 파고듭니다. 불리언이나 깔끔한 접합에 노력을 들이지 마세요.

워크플로우

  1. 참조가 있으면 읽으세요: 부품을 나열하고, 반복되는 것을 세고(기와 줄, 빔 개수, 벽당 창문 수), 부품 간의 비율을 비율로 기록하고(벽 높이에 대한 지붕 높이, 너비에 대한 높이), 팔레트 색상의 이름을 지정하세요. 그 목록을 기반으로 만드세요. 이미지가 결정합니다: 하나의 크기를 기준으로 삼고 비율에서 나머지를 도출하고, 그림과 모순되는 미터 단위의 크기를 발명하지 말고, 요청하지 않는 한 그림에 표시되지 않은 것(베이스, 소품)을 추가하지 마세요.
  2. 블록아웃 큰 덩어리를 먼저 올바른 비율의 상자와 쐐기로 만듭니다. 스크린샷을 찍고 실루엣을 비교하세요: 실루엣이 잘못되면 디테일이 구해내지 못합니다.
  3. 팔레트: 색상별로 이름을 지정하여(LP_Wall_Light, LP_Roof_Red, LP_Wood_Dark) 재질을 한 번에 만듭니다.
  4. 구조, 그 다음 2차 부품(빔, 프레임, 굴뚝, 계단), 그 다음 작은 디테일(기와, 돌, 판자, 균열), 그 다음 주변(지면 슬래브, 바위 몇 개, 잔디 덤불).
  5. 거칠게 하기: 규칙 7의 무작위 오프셋 및 기울기.
  6. 조명 및 프레이밍(아래), 그 다음 뷰를 참조의 각도에 맞추고, 캡처하여 부분별로 비교: 실루엣, 비율, 각 부분의 위치, 색상. 수정.
  7. final_check로 마무리, 작업의 마지막 호출: 함수는 아래 "최종 검사"에 있습니다. 렌더링 결과를 직접 보지 않습니다. 렌더링(배경과 분리된 모델, 참조 이미지 파일 이름이 있으면 그것과 비교)을 측정하고 잘못된 부분에 대해 PROBLEM: 줄을 출력합니다. 수정하고 다시 실행하고(최대 두 번) 그 출력을 보고서에 넣으세요. 직접 측정한 값은 이를 대체하지 않습니다: 모델을 만들거나 변경하는 작업은 이 함수가 실행될 때까지 완료되지 않습니다.

참조에 대해 두세 번의 수정 라운드를 예상하세요. 여전히 다른 점을 말하세요.

작업당 제한된 도구 라운드가 있습니다(blender-modeling의 "단계 예산" 참조): 호출당 두세 개의 관련 부품을 만들고, 마일스톤(블록아웃, 디테일, 최종)에서 캡처하고, 가장 눈에 띄는 부분을 먼저 배치하세요.

모든 레시피의 코드는 references/low-poly-recipes.md에 있습니다: 팔레트, 플랫 쉐이딩, 테이퍼 상자, 박공 지붕, 기와 줄, 창문과 문, 목재 프레임, 바위, 나무, 지형, 분산 및 조명 설정. 첫 번째 스크립트 전에 열어보세요.

하나의 오브젝트에 여러 블록

돌 층, 흉벽, 포장, 판자, 벽돌은 하나의 오브젝트에 속하는 많은 작은 상자입니다. lp_blocks로 만드세요: 각 상자를 자체 bmesh에서 완성한 다음에야 오브젝트에 추가합니다. 정점 및 면 목록을 수동으로 추가하여 상자를 결합하지 말고, 개수를 변경하는 작업(모따기) 후에 "마지막 N개 정점"을 이동하지 마세요: 면이 잘못된 정점에 배치되어 모델이 늘어난 스파이크처럼 나옵니다. final_check는 그렇게 깨진 오브젝트의 이름을 지정합니다. 다시 만들고, 보고하지 마세요.

import bpy, bmesh
from mathutils import Matrix, Vector

def lp_blocks(name, col, mat, blocks, chamfer=0.03):
    """하나의 플랫 쉐이딩된 오브젝트에 여러 상자를 만듭니다. blocks는 (center, size) 또는
    (center, size, z_rotation) 목록입니다: center와 size는 미터 단위의 (x, y, z)이고,
    회전은 라디안입니다. 각 상자는 자체 bmesh에서 완성된 후에만 오브젝트에 추가됩니다."""
    whole = bmesh.new()
    for center, size, *rot in blocks:
        bm = bmesh.new()
        bmesh.ops.create_cube(bm, size=1.0)
        bmesh.ops.scale(bm, vec=Vector(size), verts=bm.verts)
        if chamfer > 0:
            bmesh.ops.bevel(bm, geom=bm.edges[:], offset=min(chamfer, min(size) * 0.25), segments=1, affect='EDGES')
        bmesh.ops.transform(bm, matrix=Matrix.Translation(Vector(center)) @ Matrix.Rotation(rot[0] if rot else 0.0, 4, 'Z'), verts=bm.verts)
        piece = bpy.data.meshes.new("_piece")
        bm.to_mesh(piece)
        bm.free()
        whole.from_mesh(piece)                                   # 완성된 상자를 오브젝트의 메시에 추가
        bpy.data.meshes.remove(piece)
    mesh = bpy.data.meshes.new(name)
    whole.to_mesh(mesh)
    whole.free()
    for poly in mesh.polygons:
        poly.use_smooth = False
    obj = bpy.data.objects.get(name)
    if obj is None:
        obj = bpy.data.objects.new(name, mesh)
        col.objects.link(obj)
    else:
        obj.data = mesh
    obj.data.materials.append(mat)
    return obj

# 하나의 오브젝트에 두 가지 크기를 번갈아 가며 배열한 돌 벽 코스 한 줄.
col = bpy.data.collections["LP_Model"]
stone = bpy.data.materials["LP_Stone_Light"]
lp_blocks("Wall_Course_01", col, stone,
          [((x * 0.42 - 1.26, -1.0, 0.35), (0.40 if x % 2 else 0.36, 0.20, 0.30)) for x in range(7)])

형태와 그 생성 방법

원하는 형태만드는 방법
벽, 빔, 판자, 계단상자, 크기 조절; 위나 아래를 가늘게 하여 개성 부여
돌 블록, 흉벽, 포장, 벽돌lp_blocks (위): 하나의 오브젝트에 여러 모따기된 상자
박공 지붕프리즘(삼각형 프로파일을 돌출), 모든 면에서 벽 위로 돌출
지붕 기와지붕 경사면에 얇은 슬래브 줄, 각 줄이 아래 줄과 겹치고 작은 무작위 크기와 기울기
굴뚝, 탑, 기둥테이퍼 상자; 뚜껑 슬래브는 약간 더 넓게
나무줄기: 5-6면 테이퍼 실린더. 잎: 2-3개의 쌓인 원뿔(소나무) 또는 1-3개의 변위된 아이코스피어(활엽수)
바위1단계 세분화된 아이코스피어, 정점을 무작위로 이동, Z축으로 압축, 땅에 파묻음
지형, 섬절단이 적은 그리드, 노이즈로 정점 높이기, 플랫 쉐이딩; 또는 디오라마 베이스용 두꺼운 불규칙 슬래브
물평평한 평면 또는 슬래브, 하나의 파란색, 약간 광택(Roughness 0.2)
잔디, 덤불작은 원뿔 또는 압축된 아이코스피어를 클러스터로
둥근 것(통, 바퀴, 우물)6-8면 실린더

예산

대략적인 삼각형 개수, 과도하게 제작되었는지 판단하기 위한 것:

에셋삼각형 개수
작은 소품(바위, 상자, 덤불)20-150
나무60-300
집 또는 건물500-3,000
캐릭터300-1,500
전체 디오라마3,000-15,000

완료 시 개수를 출력하세요. 예산을 크게 초과하면 일반적으로 너무 많은 세그먼트 또는 세분화하는 모디파이어가 있다는 의미입니다.

조명 및 프레젠테이션

스타일은 조명에 따라 달라집니다: 인접한 면이 다른 밝기를 가질 때만 면이 읽힙니다.

  • 하나의 태양을 비스듬히(약 45° 위, 측면으로 30-45°), 강도 3-4, 부드러운 그림자와 함께; 그리고 밝은 하늘색의 월드를 강도 0.6-1.0으로 추가하여 그림자가 완전히 검지 않도록 합니다. 3점 스튜디오 설정은 사용하지 마세요. 흰색 배경을 얻기 위해 빛을 올리지 말고, 색상을 입히기 위해 월드에 틴트를 주지 마세요: 배경판의 자체 재질이 색상을 제공합니다.
  • 약간 따뜻한 태양과 약간 차가운 월드는 고전적인 룩을 제공합니다.
  • 뷰 변환을 Standard로 설정하여 팔레트 색상이 선택한 대로 유지되도록 하세요(기본 변환은 색상을 회색으로 만듭니다).
  • 카메라: 위에서 3/4 뷰(아래로 30-35°), 긴 렌즈(50-85 mm) 또는 디오라마용 직교 투영.
  • 팔레트 색상의 평범한 배경, 또는 깨끗한 가장자리로 끝나는 지면 슬래브. 배경 평면은 모델보다 훨씬 커야 하거나(20배 이상), 월드가 같은 색상이어야 합니다: 프레임에 가장자리나 지평선이 보여서는 안 됩니다.
  • EEVEE로 충분하고 빠릅니다. 부드러운 그림자와 앰비언트 오클루전이 많은 도움이 됩니다.

재질, 파일로 렌더링, 문제 해결은 blender-materials-render에 있습니다.

게임 엔진용

로우폴리 에셋은 일반적으로 엔진으로 이동합니다. 내보낼 때 플랫 쉐이딩을 유지하고(면은 분할 법선으로 저장되어 정점 개수는 증가하지만 삼각형 개수는 증가하지 않음), 하나의 에셋 조각을 몇 개의 재질 슬롯이 있는 하나의 메시로 병합하고, 원점을 하단 중앙에 배치하세요. 나머지는 blender-game-export를 따르세요.

최종 검사 (항상 마지막 호출)

렌더 도구는 파일을 쓰고 그 경로를 제공합니다: 이미지를 직접 보지 않습니다. 따라서 작업의 마지막 execute_blender_code 호출은 final_check이며, 그대로 복사되고, 출력하는 내용이 그대로 보고서에 들어갑니다. 장면의 카메라에서 작은 렌더를 만들고 잘못된 것을 지적합니다: 재질이 없는 오브젝트, 깨진 지오메트리, 프레임 밖의 모델, 타거나 어두운 렌더, 그리고 참조 파일 이름이 있으면 프레이밍, 밝기, 색상 및 실루엣의 단단함 차이.

import bpy, os, re
import numpy as np
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view

BACKDROP_WORDS = ("ground", "backdrop", "floor", "sky", "shadow")

def subject_stats(path, size=128):
    """이미지를 배경(네 모서리의 색상)과 피사체(나머지)로 분할:
    이미지에서 피사체가 차지하는 비율, 경계 상자, 밝기, 채도 및 색상."""
    img = bpy.data.images.load(path, check_existing=False)
    w, h = img.size
    sw, sh = (size, max(8, round(size * h / w))) if w >= h else (max(8, round(size * w / h)), size)
    img.scale(sw, sh)
    px = np.empty(len(img.pixels), dtype=np.float32)
    img.pixels.foreach_get(px)
    bpy.data.images.remove(img)
    rgb = px.reshape(sh, sw, 4)[:, :, :3]                       # 행 0은 이미지의 맨 아래
    cy, cx = max(2, sh // 8), max(2, sw // 8)
    corners = np.concatenate([rgb[:cy, :cx].reshape(-1, 3), rgb[:cy, -cx:].reshape(-1, 3),
                              rgb[-cy:, :cx].reshape(-1, 3), rgb[-cy:, -cx:].reshape(-1, 3)])
    # 배경 = 네 모서리 색상 중 하나에 가까움(그라데이션이나 부드러운 그림자도 포함).
    q = np.unique(np.round(corners * 8).astype(int), axis=0)
    flat = rgb.reshape(-1, 3)
    dist = np.min(np.abs(flat[:, None, :] * 8 - q[None, :, :]).max(axis=2), axis=1)
    subject = (dist > 1.0).reshape(sh, sw)
    share = float(subject.mean())
    whole = flat @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
    out = {"size": (w, h), "image_brightness": round(float(whole.mean()), 3), "subject_pct": round(100 * share, 1)}
    if share < 0.005:
        return 

…

(번역을 위해 잘렸습니다)

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

관련 스킬