Blender 애니메이션 — Python 기반 3D 작업
bpy를 통해 Blender를 백그라운드 모드로 구동하며, 재실행 가능한 스크립트로 작업합니다. .blend 파일은 스크립트의 빌드 산출물로 취급합니다. 스크립트가 진실 공급원(source of truth)이고, 파일이 전달물입니다.
아키텍처 및 판단 기준은 references/BLENDER-PLAYBOOK.md를 참조하고, 바로 사용 가능한 코드 패턴은 references/BLENDER-RECIPES.md를 참조하세요. 모든 API 세부 사항은 설치된 Blender 버전의 문서를 기준으로 확인하십시오. Python API는 릴리스 간에 변경됩니다.
1. 환경 확인 먼저
blender --version
blender --background --factory-startup --python-expr \
"import bpy, sys; print('BLENDER', bpy.app.version_string); print('PY', sys.version.split()[0]); print('ENGINES', [e.bl_idname for e in bpy.types.RenderEngine.__subclasses__()][:5])"
규칙:
bpy는 Blender에 번들된 Python에 속합니다. 절대pip install bpy하거나 시스템 Python으로 씬 스크립트를 실행하지 마세요.--python-exit-code 1을 전달하여 Python 예외가 조용한 성공 대신 셸 명령을 실패로 처리하게 합니다.- 클린 빌드를 위해
--factory-startup을 사용하세요. 사용자의 열린 프로젝트를 날려버리는 데 사용하지 마세요. - 모든 렌더링 시작 시 Blender 버전, 엔진, 프레임 범위, 해상도, FPS, 출력 경로를 로그에 기록하세요.
2. 표준 실행 방식
# 처음부터 씬을 빌드하고 .blend 저장
blender --background --factory-startup --python-exit-code 1 --python blender/scripts/build_scene.py
# 기존 프로젝트 수정
blender --background project.blend --python-exit-code 1 --python blender/scripts/update_scene.py
# 저장된 애니메이션 렌더링
blender --background project.blend --render-anim
# 한 프레임 렌더링
blender --background project.blend --render-frame 120
# 안전하게 범위 렌더링 (이미지 시퀀스)
blender --background project.blend --python-exit-code 1 --python blender/scripts/render_frames.py
긴 작업은 출력을 logs/로 리디렉션하여 실행하고, 재개가 필요할 수 있는 작업에는 항상 이미지 시퀀스를 선호하세요.
3. 스크립트 아키텍처
작은 씬의 경우 한 파일로 시작하세요. 씬이 약 300줄을 초과하거나 하나 이상의 샷이 있을 때 분할하세요:
blender/
config.py # fps, 프레임 범위, 해상도, 색상 팔레트, 경로, 시드
build.py # 진입점: 리셋 → 설정 → 샷 빌드 → 저장
scene_setup.py # 월드, 단위, 컬렉션, 렌더 설정
geometry.py # 프리미티브, 커브, 파라메트릭 메시
materials.py # 노드 빌더, 재사용 가능한 재질 팩토리
animation.py # 키프레임, 드라이버, 절차적 모션
camera.py # 리그, 궤도/돌리, 심도, 프레이밍 검사
lighting.py # 키/필/림, HDRI, 방사형 악센트
simulation.py # 파티클, 강체, 천 — 캐시 설정
render.py # 엔진, 샘플, 출력, 연락처 시트, 인코딩
shots/ # 샷별 모듈: shot_01.py, shot_02.py
절대 타협할 수 없는 사항:
- 멱등성. 빌드를 다시 실행하면 이름이 지정된 컬렉션/오브젝트만 업데이트됩니다. 지오메트리를 복제하거나 관련 없는 사용자 콘텐츠를 지우지 않습니다.
- 이름 지정.
Main_Logo,Cam_Orbit,Light_Key,Emitter_Particles. 절대Cube.003을 배송하지 마세요. - 설정 가능. 모든 튜너블 값은
config.py또는 데이터클래스에 있어 리비전이 한 줄 변경으로 가능합니다. - 단언적. 에셋, 카메라, 폰트 또는 출력 디렉터리가 없으면 큰 소리로 실패하세요.
- 범위 지정. 정리 작업은 소유한 이름(예:
MS_접두사)의 컬렉션만 삭제합니다.
4. 씬 구성
- 선택, 모드, 영역에 의존하는 컨텍스트에 민감한
bpy.ops대신 데이터 API(bpy.data.objects.new,bpy.data.meshes.new,collection.objects.link)를 선호하세요. - 연산자가 정말로 필요한 경우, 해당 버전에 맞는 적절한 컨텍스트 재정의를 제공하고 이유를 문서화하세요.
- 노드와 작은 팩토리 함수를 통해 재질을 구축하세요. 오브젝트마다 20개의 노드 트리를 복사하여 붙여넣지 마세요.
- 실제 세계 스케일을 사용하고 씬 단위 시스템을 설정하세요. 잘못된 스케일은 심도, 물리, 조명을 망가뜨립니다.
- 월드 노드 트리를 명시적으로 유지(색상, 강도, HDRI)하여 렌더링이 다른 머신에서도 재현 가능하도록 하세요.
- 에셋을 패킹하거나 상대 경로로 만드세요.
//assets/...경로와 패키지 폴더가 누군가의 다운로드 폴더에 있는 절대 경로보다 낫습니다.
5. 애니메이션
의도적으로 메커니즘을 선택하세요:
| 필요성 | 사용 |
|---|---|
| 작성된 위치/회전/스케일/재질 변경 | 명시적 보간이 있는 키프레임 |
| 다른 속성이나 표현식을 따르는 속성 | 드라이버 (간단하고 문서화된 표현식) |
| 반복적/파라메트릭 모션 (궤도, 파도, 그리드, 나선) | 프레임 종속 수학, 상태 비저장 평가 |
| 수천 개의 인스턴스, 패턴, 산포 | 지오메트리 노드 |
| 물리적으로 나타나는 모션 | 시뮬레이션 (캐시, 전체 범위 확인) |
| 공간을 통한 카메라 경로 | 커브 + Follow Path 제약, 또는 키프레임 리그 |
키프레임 위생:
obj.location = (0, 0, 0)
obj.keyframe_insert("location", frame=1)
obj.location = (4, 0, 0)
obj.keyframe_insert("location", frame=48)
for fc in obj.animation_data.action.fcurves:
for kp in fc.keyframe_points:
kp.interpolation = 'BEZIER'
kp.easing = 'EASE_IN_OUT'
kp.handle_left_type = kp.handle_right_type = 'AUTO_CLAMPED'
- 의도적으로 보간을 설정하세요. 기본 베지어는 유기적인 움직임에 적합합니다. 일정한 속도와 기계적 움직임에는
LINEAR를 사용하고, 계단식/픽셀룩에는CONSTANT를 사용하세요. - 루핑 주기에 의도하지 않은 이징(easing)을 남기지 마세요. 루프 지점에서 움찔거림이 발생합니다.
- 루프의 경우: 프레임 1과 프레임 N+1을 동일하게 만든 후
[1, N]을 렌더링하세요. - 모든 절차적 요소는 프레임의 순수 함수여야 합니다:
f(frame). 상태를 누적하지 말고, 시드되지 않은random을 사용하지 마세요.
6. 지오메트리 노드와 인스턴싱
스케일, 변형 또는 비파괴적 제어가 필요할 때 지오메트리 노드를 사용하세요:
- 산포/인스턴스 (나무, 잔해, 별, 군중 블록).
- 파라메트릭 배열, 그리드, 방사형 패턴, 이슬람 기하학적 타일링.
- 애니메이션 변위, 필드, 인스턴스별 회전/스케일.
- 무거운 10,000개 오브젝트 Python 루프를 하나의 노드 트리로 변환.
nodes.new(...) 및 links.new(...)로 이름을 사용하여 노드를 빌드하고, 깨지기 쉬운 인덱스는 사용하지 마세요. 사용자가 요청할 노브(개수, 반경, 시드, 속도)를 수정자 입력으로 노출하여 리비전에 코드 변경이 필요 없게 하세요.
7. 재질, 조명, 카메라
- 재질: Principled BSDF 베이스; 명시적인 거칠기/메탈릭; 악센트를 위한 방사(절제하세요 — 블룸이 평평한 샷을 고칠 수 없습니다). 실제 투명도는 필요할 때만 사용하고, 최종 엔진에서 확인하세요.
- 조명: 키 → 필 → 림 → 환경. 형태와 깊이가 장식보다 우선합니다. 노출과 하이라이트 클리핑을 확인하세요. 뷰포트만 확인하지 마세요.
- 카메라: 피사체에 맞는 초점 거리와 프레이밍을 선택하세요. 궤도, 돌리, 크레인, 고정 — 각각 이유가 있어야 합니다. 극단적인 프레임에서도 프레이밍을 확인하세요. 프레임 1만 확인하지 마세요. 부드러운 베지어 핸들을 선호하고; 급정지보다는 미세한 정착을 추가하세요.
- 심도: 이야기의 포인트에 초점을 맞추세요. 작은 텍스트를 흐리게 하지 마세요.
8. 엔진 및 렌더 설정
| 엔진 | 용도 | 비용 |
|---|---|---|
| EEVEE | 모션 그래픽, 스타일라이즈드 작업, 빠른 반복, 미리보기 | 낮음 |
| Cycles | 포토리얼리스틱 빛, 반사, GI, 유리, 최종 뷰티 | 높음 |
| Workbench | 블로킹, 실루엣, 애니메틱 | 매우 낮음 |
확실하지 않을 때는 둘 다 테스트하세요. 엔진 속도는 씬에 따라 달라지며 마케팅에 의존하지 않습니다. GPU는 빌드, 드라이버, OS에 따라 다릅니다 — 항상 CPU 경로를 유지하세요.
명시적으로 설정하고 로그에 기록:
scene.render.engine = 'BLENDER_EEVEE_NEXT' # 4.2+; 이전 빌드는 'BLENDER_EEVEE'를 사용할 수 있음
scene.render.resolution_x, scene.render.resolution_y = 1920, 1080
scene.render.resolution_percentage = 100
scene.render.fps, scene.render.fps_base = 30, 1.0
scene.frame_start, scene.frame_end = 1, 300
scene.render.image_settings.file_format = 'PNG'
scene.render.image_settings.color_mode = 'RGBA'
scene.render.filepath = "//renders/shot_01/frame_"
- 미리보기: 25–50% 해상도, 낮은 샘플, EEVEE 또는 Workbench.
- 최종: 전체 해상도, 룩에 맞게 선택된 엔진, 도움이 된다면 디노이즈, 올바른 뷰 트랜스폼.
- 색상: 뷰 트랜스폼(AgX/Filmic/Standard)을 한 번 결정하고 모든 패스에 적용한 후, 다운스트림의 Remotion/FFmpeg에서 일치시킵니다. 일치하지 않는 트랜스폼은 "합성이 탁하게 보이는" 가장 흔한 원인입니다.
- 긴 렌더링 → PNG(또는 HDR이 필요할 때는 EXR) 시퀀스, 그 후 인코딩. 직접 비디오 출력은 빠른 리뷰용입니다.
9. 미리보기 사다리
- 대표 프레임 하나의 스틸.
- 키 프레임: 첫 번째, 드러남, 정점, 각 전환, 마지막.
- 가장 어려운 모션을 포함하는 저샘플, 저해상도 세그먼트.
- 축소된 스케일, 실제 FPS로 전체 미리보기.
- 1–4가 올바르게 보인 후에만 최종.
연락처 시트와 대상 프레임으로 검사:
ffmpeg -i preview.mp4 -vf "fps=2,scale=320:-1,tile=4x4" -frames:v 1 previews/contact.jpg
깜빡임, z-파이팅, 팝핑, 교차, 텍스처 스위밍, 카메라 클리핑, 노출 변화, 읽을 수 없는 텍스트를 찾으세요.
10. 텍스트, 다국어 및 로고 작업
- 복사본과 폰트 라이선스를 확인한 후에만 텍스트를 메시로 변환하세요. 소스에 편집 가능한 텍스트 오브젝트를 유지하세요.
- 애니메이션 전에 글리프 커버리지를 확인하세요. 페르시아어/아랍어의 경우, 렌더링된 프레임에서 모양 형성, 연결, RTL 순서, 발음 구별 기호, 혼합 스크립트 구두점을 확인하세요. 올바른 문자열도 텍스트 경로가 모양을 형성하지 않으면 잘못 렌더링될 수 있습니다.
- 브랜드/로고 작업의 경우 가능하면 벡터 소스를 얻으세요. 래스터인 경우 의도적으로 트레이싱하고 4K에서 가장자리를 확인하세요.
- 베벨/가중치를 적용하여 타입이 빛을 견디게 하세요. 순수 검정-검정 또는 순수 흰색-흰색 가장자리는 피하세요.
11. 성능 및 안정성
- 복제하지 말고 인스턴스하세요. 샘플을 낮추기 전에 세분화 수준을 낮추세요.
- 투명 레이어와 볼류메트릭을 제한하세요. 이들은 일반적인 렌더링 시간 킬러입니다.
- 시뮬레이션을 캐싱하고, 캐시가 프로젝트 내부에 있거나 문서화되었는지 확인하세요.
- 깨지기 쉬운 작업의 경우 프레임 배치로 렌더링하세요. 프레임 수를 확인하고 인코딩 전에 누락된 프레임을 잡으세요.
- RAM/VRAM/디스크를 모니터링하세요. 4K EXR 시퀀스는 큽니다. 긴 렌더링 전에 여유 공간을 확인하세요.
- 더 새로운 Blender 주요 버전으로 열기/저장하기 전에
.blend파일을 백업하세요. 최신 파일은 이전 빌드에서 열리지 않을 수 있습니다.
12. 최소 작업 예제
# build.py — 실행 방법:
# blender --background --factory-startup --python-exit-code 1 --python build.py
import bpy, bmesh, math, os
scene = bpy.context.scene
scene.frame_start, scene.frame_end = 1, 120
scene.render.fps = 30
scene.render.resolution_x, scene.render.resolution_y = 1920, 1080
scene.render.engine = 'BLENDER_EEVEE_NEXT'
scene.render.filepath = "//renders/frame_"
# 소유한 컬렉션 정리
col = bpy.data.collections.get("MS_Main") or bpy.data.collections.new("MS_Main")
if col.name not in scene.collection.children:
scene.collection.children.link(col)
def add_sphere(name, loc, radius, mat):
"""데이터-API 구: 연산자 없음, 컨텍스트 의존성 없음."""
mesh = bpy.data.meshes.new(name)
bm = bmesh.new()
bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=radius)
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new(name, mesh)
obj.location = loc
col.objects.link(obj)
obj.data.materials.append(mat)
return obj
mat = bpy.data.materials.new("MS_Emission")
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]
bsdf.inputs["Base Color"].default_value = (0.05, 0.6, 0.55, 1)
bsdf.inputs["Emission Color"].default_value = (0.1, 0.9, 0.8, 1)
bsdf.inputs["Emission Strength"].default_value = 3.0
bsdf.inputs["Roughness"].default_value = 0.3
core = add_sphere("MS_Core", (0, 0, 1), 0.8, mat)
core.keyframe_insert("scale", frame=1)
core.scale = (1.15, 1.15, 1.15)
core.keyframe_insert("scale", frame=40)
core.scale = (1.0, 1.0, 1.0)
core.keyframe_insert("scale", frame=90)
# 카메라
cam_data = bpy.data.cameras.new("MS_Cam")
cam = bpy.data.objects.new("MS_Cam", cam_data)
col.objects.link(cam)
scene.camera = cam
cam.location = (6, -6, 3)
cam.rotation_euler = (math.radians(75), 0, math.radians(45))
# 저장
os.makedirs(bpy.path.abspath("//renders"), exist_ok=True)
bpy.ops.wm.save_as_mainfile(filepath=bpy.path.abspath("//scene.blend"))
print("BUILD OK", len(col.objects), "objects")
더 자세한 내용은 references/BLENDER-RECIPES.md를 참조하세요: 궤도 리그, 이징, 지오메트리 노드 산포, 파티클, 텍스트, 알파 패스, 렌더 팜 스크립트, 인코딩 명령어.
13. Blender MCP (선택적 편의)
백그라운드 모드의 스크립트가 프로덕션 경로입니다: 재현 가능, 차이 비교 가능, 디버깅 가능, CI 친화적. MCP는 대화형 씬 검사와 이미 열린 세션에서의 실시간 조정에 유용합니다. 이는 Blender 구성 요소가 아닌 서드파티 통합입니다.
사용자가 원하는 경우: 리포지토리와 라이선스를 검토하고, 설치된 Blender와의 호환성을 확인하며, Blender 내부에서 임의의 Python 실행을 허용한다는 점을 이해하고, 루프백에 바인딩하며 신뢰할 수 없는 네트워크에 노출하지 마세요. 현재 패키지 이름은 mcp-for-blender입니다. 오래된 blender-mcp 스니펫 대신 리포지토리의 현재 설정 지침을 따르세요. 전달된 프로젝트의 종속성으로 만들지 마세요.
14. 전달물
Blender 작업이 완료되었을 때 갖추어야 할 것:
output/final.mp4(또는 요청된 형식),ffprobe로 검증됨.blender/scripts/*.py— 빌드 소스.scene.blend— 편집 가능한 프로젝트, 에셋 패킹 또는 상대 경로.renders/이미지 시퀀스 (또는 문서화된 마스터), 유용할 때 유지.README.md— 정확한 Blender 버전, 설치, 빌드, 미리보기, 최종 렌더링, 리비전 명령어.- 짧은 보고서: 도구, 해상도, FPS, 지속 시간, 프레임 수, 시각적으로 확인된 사항.
사용자가 변경하고 싶어할 수 있는 애니메이션을 요청했는데 MP4만 전달하지 마세요.