파일을 복사하지 않고 도시를 조립하는 법
오늘은 OpenUSD Composition의 핵심인 Reference, Payload, Variant Set을 작은 스마트시티 Stage에 직접 적용한다. 목표는 API를 외우는 것이 아니라, 데이터 성격에 따라 어떤 방식을 선택해야 하는지 설명할 수 있는 상태가 되는 것이다.
건물 자산 재사용, 무거운 Geometry의 선택적 로딩, 신호등 Variant 전환을 구현하고 세 Composition Arc의 차이를 자신의 말로 설명한다.
1. 30초 핵심 정리
| 기능 | 한 문장 정의 | 스마트시티 예시 |
|---|---|---|
| Reference | 외부 자산의 Prim 트리를 특정 위치에 재사용 | 동일한 신호등·건물을 여러 곳에 배치 |
| Payload | 필요할 때만 불러오는 선택적 Reference | 고해상도 건물 Geometry 지연 로딩 |
| Variant Set | 미리 정의한 대안 중 하나를 비파괴적으로 선택 | 신호 상태 또는 LOD 선택 |
OpenUSD의 Composition은 여러 Layer와 자산에 기록된 의견(opinion)을 합성해 최종 Stage를 만드는 과정이다. 작은 데모에서는 모든 것을 한 파일에 넣을 수 있지만, 산업용 디지털 트윈에서는 재사용·협업·성능·버전 관리를 위해 자산을 분리해야 한다.
2. 오늘 만들 파일 구조
smart-city-twin/
├── city.usda
├── assets/
│ ├── building.usda
│ └── traffic_light.usda
├── payloads/
│ └── building_geometry.usda
└── scripts/
├── create_assets.py
├── build_city.py
└── inspect_composition.py
city.usda는 Geometry를 직접 소유하지 않는다. 어떤 자산을 어디에 배치할지, 어떤 Variant를 선택할지, 도시에서 어떤 더 강한 의견을 적용할지만 책임진다.
3. Reference: 자산을 복사하지 않고 재사용하기
Reference는 다른 Layer의 Prim 트리를 현재 Stage의 특정 Prim 아래로 합성한다. 외부 자산과의 연결은 유지하면서, 배치된 Prim에 위치나 속성 같은 더 강한 의견을 추가할 수 있다.
# USD Stage와 Xform Schema, 경로 표현에 필요한 모듈을 불러온다.
from pxr import Usd, UsdGeom, Sdf
# 도시 전체 Composition 결과를 저장할 새 Root Layer를 만든다.
stage = Usd.Stage.CreateNew("city.usda")
# 모든 도시 자산이 들어갈 최상위 Prim을 정의한다.
world = UsdGeom.Xform.Define(stage, "/World")
# Prim path를 생략한 외부 Reference가 찾을 수 있도록 기본 진입점을 지정한다.
stage.SetDefaultPrim(world.GetPrim())
light = UsdGeom.Xform.Define(
stage, "/World/TrafficLights/TrafficLight_01"
)
# 원본을 복사하지 않고 외부 신호등 자산을 이 배치 Prim 아래에 합성한다.
light.GetPrim().GetReferences().AddReference(
"./assets/traffic_light.usda",
Sdf.Path("/TrafficLight")
)
stage.GetRootLayer().Save()
defaultPrim이 올바르면 Prim path를 생략할 수 있다. 둘 다 없다면 Arc는 기록되어도 원하는 콘텐츠가 합성되지 않을 수 있다.
Reference와 Sublayer는 무엇이 다른가?
Sublayer는 Layer 전체를 동일한 namespace에 겹쳐 합성하고, Reference는 독립 자산을 특정 Prim 위치에 가져온다. 환경·운영·Physics 의견을 겹칠 때는 Sublayer, 신호등이나 차량을 반복 배치할 때는 Reference가 자연스럽다.
4. Payload: 무거운 Geometry를 선택적으로 로드하기
Payload는 선택적으로 로드하거나 언로드할 수 있는 Composition Arc다. 건물의 ID·분류·Variant 같은 가벼운 인터페이스는 유지하고, 실제 고해상도 Mesh만 Payload 뒤에 둘 수 있다.
건물 진입점: assets/building.usda
# USDA 텍스트 형식의 버전 선언이다.
#usda 1.0
(
defaultPrim = "Building"
upAxis = "Y"
metersPerUnit = 1
)
def Xform "Building" (
# 고용량 Geometry는 인터페이스 파일과 분리하고 필요할 때만 로드한다.
prepend payload = @../payloads/building_geometry.usda@
)
{
custom string assetType = "building"
custom string assetId = "BLDG-TEMPLATE-001"
}
로드 상태 제어
from pxr import Usd
# 처음에는 Payload 내용을 제외해 가벼운 작업 집합으로 Stage를 연다.
stage = Usd.Stage.Open(
"city.usda",
load=Usd.Stage.LoadNone
)
# 로딩 상태를 제어할 건물의 배치 Prim을 경로로 찾는다.
building = stage.GetPrimAtPath(
"/World/Buildings/Building_01"
)
print("로드 전:", building.IsLoaded())
# 이 건물의 Payload와 하위 콘텐츠만 작업 집합에 포함한다.
building.Load()
print("로드 후:", building.IsLoaded())
# 작업이 끝나면 Payload를 내려 메모리 사용량을 줄일 수 있다.
building.Unload()
LoadNone으로 열어도 Payload Arc와 배치 Prim 자체가 사라지는 것은 아니다. Payload를 통해 들어오는 무거운 자식 콘텐츠가 작업 집합에서 제외된다.
5. Variant Set: 구성 상태를 비파괴적으로 바꾸기
Variant Set은 하나의 Prim에 여러 대안 상태를 정의하고, 그중 하나를 선택한다. LOD, Material, 장비 구성, 주간·야간 표현처럼 제한된 선택지에 적합하다.
from pxr import Usd, UsdGeom, Gf
stage = Usd.Stage.CreateNew(
"assets/traffic_light.usda"
)
# Variant Set을 소유할 신호등 자산의 루트 Prim을 정의한다.
light = UsdGeom.Xform.Define(stage, "/TrafficLight")
stage.SetDefaultPrim(light.GetPrim())
lamp = UsdGeom.Sphere.Define(
stage, "/TrafficLight/Lamp"
)
# 루트 Prim의 Variant Set 컬렉션을 가져온다.
variants = light.GetPrim().GetVariantSets()
# red, yellow, green 선택지를 담을 signalState 세트를 만든다.
signal = variants.AddVariantSet("signalState")
colors = {
"red": Gf.Vec3f(1.0, 0.05, 0.05),
"yellow": Gf.Vec3f(1.0, 0.75, 0.05),
"green": Gf.Vec3f(0.05, 1.0, 0.15),
}
# 각 상태를 Variant로 등록한 뒤 해당 Variant 내부에 색상 의견을 작성한다.
for state, color in colors.items():
signal.AddVariant(state)
signal.SetVariantSelection(state)
# Edit Target을 현재 Variant로 잠시 전환해야 변경값이 Variant 안에 저장된다.
with signal.GetVariantEditContext():
lamp.GetDisplayColorAttr().Set([color])
# 자산을 처음 열었을 때 사용할 기본 선택 상태를 명시한다.
signal.SetVariantSelection("red")
stage.GetRootLayer().Save()
GetVariantEditContext() 안에서 작성한 의견은 일반 Layer 위치가 아니라 현재 선택한 Variant 내부에 저장된다. 도시 Stage에서는 같은 원본 자산을 Reference한 뒤 각 배치에 다른 선택을 기록할 수 있다.
light_01.GetVariantSet(
"signalState"
).SetVariantSelection("red")
light_02.GetVariantSet(
"signalState"
).SetVariantSelection("green")
6. 가장 중요한 설계 판단: Variant인가, Attribute인가?
온도·습도·차량 위치처럼 계속 바뀌는 값은 Attribute나 time sample로 기록한다. LOD·Material·정비 모드처럼 미리 정해진 대안은 Variant로 관리한다.
- Variant: normal / flashing / maintenance, low / high LOD
- Attribute: 현재 신호, 센서 측정값, 객체 위치, 조명 밝기
이번 학습에서는 Composition을 확인하기 위해 신호 색상을 Variant로 구현했다. 그러나 실제 시뮬레이션에서 매 프레임 상태가 바뀐다면 Variant를 런타임 데이터 저장소처럼 남용하지 않는 편이 좋다.
7. 완성된 Stage 구조
/World
├── Buildings
│ ├── Building_01
│ │ └── Reference → building.usda
│ │ └── Payload → building_geometry.usda
│ └── Building_02
│ └── Reference → building.usda
└── TrafficLights
├── TrafficLight_01
│ ├── Reference → traffic_light.usda
│ └── signalState = red
└── TrafficLight_02
├── Reference → traffic_light.usda
└── signalState = green
8. 저장보다 중요한 검증
Composition Arc가 파일에 기록되었다고 해서 콘텐츠가 정상 합성됐다는 뜻은 아니다. 다음 항목을 반드시 확인한다.
defaultPrim과 대상 Prim path가 올바른가?- 상대 asset path가 작성 Layer 기준으로 해석되는가?
- Payload의 Load·Unload 결과가 계층에 반영되는가?
- Variant 이름과 선택값이 실제 자산에 존재하는가?
upAxis와metersPerUnit이 일치하는가?- Omniverse Console에 unresolved asset 또는 composition error가 없는가?
9. 면접 답변으로 압축하기
Reference는 외부 자산을 현재 namespace에 합성해 재사용합니다. Payload는 유사하지만 선택적으로 로드·언로드할 수 있어 대규모 Stage의 작업 집합 관리에 적합합니다.
LOD, Material, 장비 구성처럼 미리 정의된 제한된 대안을 비파괴적으로 선택할 때 사용합니다. 계속 변하는 센서 값이나 위치에는 Attribute가 더 적합합니다.
Prim 유효성, asset path, defaultPrim, Edit Target, Payload 로딩, Variant 선택, 의견 강도를 순서대로 확인합니다. Layer 패널, Console, usdview와 Python API를 함께 사용합니다.
10. DAY 3 체크리스트
□ 건물 Geometry를 Payload로 분리했다.
□ LoadNone, Load(), Unload()를 확인했다.
□ signalState Variant Set을 만들었다.
□ 두 신호등에 서로 다른 Variant를 선택했다.
□ 실시간 Attribute와 Variant의 목적을 구분했다.
□ asset path와 opinion strength 문제를 점검했다.
마무리
오늘의 핵심은 세 가지 API를 외우는 것이 아니라 데이터의 성격에 따라 Composition 방식을 선택하는 것이다.
Payload — 무겁고 선택적으로 로드할 콘텐츠
Variant Set — 제한된 구성 대안
Attribute — 지속해서 변하는 운영 데이터
DAY 4에서는 이 구조를 바탕으로 Geometry와 운영 데이터를 별도 Layer로 분리한다. 원본 3D 자산을 건드리지 않고 센서 상태, 시뮬레이션 값, Physics 설정을 독립적으로 관리하는 단계다.
참고 자료
OpenUSD Introduction ·
UsdReferences API ·
UsdVariantSet API ·
NVIDIA OpenUSD Asset Structure Principles
파일을 복사하지 않고 도시를 조립하는 법
오늘은 OpenUSD Composition의 핵심인 Reference, Payload, Variant Set을 작은 스마트시티 Stage에 직접 적용한다. 목표는 API를 외우는 것이 아니라, 데이터 성격에 따라 어떤 방식을 선택해야 하는지 설명할 수 있는 상태가 되는 것이다.
건물 자산 재사용, 무거운 Geometry의 선택적 로딩, 신호등 Variant 전환을 구현하고 세 Composition Arc의 차이를 자신의 말로 설명한다.
1. 30초 핵심 정리
| 기능 | 한 문장 정의 | 스마트시티 예시 |
|---|---|---|
| Reference | 외부 자산의 Prim 트리를 특정 위치에 재사용 | 동일한 신호등·건물을 여러 곳에 배치 |
| Payload | 필요할 때만 불러오는 선택적 Reference | 고해상도 건물 Geometry 지연 로딩 |
| Variant Set | 미리 정의한 대안 중 하나를 비파괴적으로 선택 | 신호 상태 또는 LOD 선택 |
OpenUSD의 Composition은 여러 Layer와 자산에 기록된 의견(opinion)을 합성해 최종 Stage를 만드는 과정이다. 작은 데모에서는 모든 것을 한 파일에 넣을 수 있지만, 산업용 디지털 트윈에서는 재사용·협업·성능·버전 관리를 위해 자산을 분리해야 한다.
2. 오늘 만들 파일 구조
smart-city-twin/
├── city.usda
├── assets/
│ ├── building.usda
│ └── traffic_light.usda
├── payloads/
│ └── building_geometry.usda
└── scripts/
├── create_assets.py
├── build_city.py
└── inspect_composition.py
city.usda는 Geometry를 직접 소유하지 않는다. 어떤 자산을 어디에 배치할지, 어떤 Variant를 선택할지, 도시에서 어떤 더 강한 의견을 적용할지만 책임진다.
3. Reference: 자산을 복사하지 않고 재사용하기
Reference는 다른 Layer의 Prim 트리를 현재 Stage의 특정 Prim 아래로 합성한다. 외부 자산과의 연결은 유지하면서, 배치된 Prim에 위치나 속성 같은 더 강한 의견을 추가할 수 있다.
# USD Stage와 Xform Schema, 경로 표현에 필요한 모듈을 불러온다.
from pxr import Usd, UsdGeom, Sdf
# 도시 전체 Composition 결과를 저장할 새 Root Layer를 만든다.
stage = Usd.Stage.CreateNew("city.usda")
# 모든 도시 자산이 들어갈 최상위 Prim을 정의한다.
world = UsdGeom.Xform.Define(stage, "/World")
# Prim path를 생략한 외부 Reference가 찾을 수 있도록 기본 진입점을 지정한다.
stage.SetDefaultPrim(world.GetPrim())
light = UsdGeom.Xform.Define(
stage, "/World/TrafficLights/TrafficLight_01"
)
# 원본을 복사하지 않고 외부 신호등 자산을 이 배치 Prim 아래에 합성한다.
light.GetPrim().GetReferences().AddReference(
"./assets/traffic_light.usda",
Sdf.Path("/TrafficLight")
)
stage.GetRootLayer().Save()
defaultPrim이 올바르면 Prim path를 생략할 수 있다. 둘 다 없다면 Arc는 기록되어도 원하는 콘텐츠가 합성되지 않을 수 있다.
Reference와 Sublayer는 무엇이 다른가?
Sublayer는 Layer 전체를 동일한 namespace에 겹쳐 합성하고, Reference는 독립 자산을 특정 Prim 위치에 가져온다. 환경·운영·Physics 의견을 겹칠 때는 Sublayer, 신호등이나 차량을 반복 배치할 때는 Reference가 자연스럽다.
4. Payload: 무거운 Geometry를 선택적으로 로드하기
Payload는 선택적으로 로드하거나 언로드할 수 있는 Composition Arc다. 건물의 ID·분류·Variant 같은 가벼운 인터페이스는 유지하고, 실제 고해상도 Mesh만 Payload 뒤에 둘 수 있다.
건물 진입점: assets/building.usda
# USDA 텍스트 형식의 버전 선언이다.
#usda 1.0
(
defaultPrim = "Building"
upAxis = "Y"
metersPerUnit = 1
)
def Xform "Building" (
# 고용량 Geometry는 인터페이스 파일과 분리하고 필요할 때만 로드한다.
prepend payload = @../payloads/building_geometry.usda@
)
{
custom string assetType = "building"
custom string assetId = "BLDG-TEMPLATE-001"
}
로드 상태 제어
from pxr import Usd
# 처음에는 Payload 내용을 제외해 가벼운 작업 집합으로 Stage를 연다.
stage = Usd.Stage.Open(
"city.usda",
load=Usd.Stage.LoadNone
)
# 로딩 상태를 제어할 건물의 배치 Prim을 경로로 찾는다.
building = stage.GetPrimAtPath(
"/World/Buildings/Building_01"
)
print("로드 전:", building.IsLoaded())
# 이 건물의 Payload와 하위 콘텐츠만 작업 집합에 포함한다.
building.Load()
print("로드 후:", building.IsLoaded())
# 작업이 끝나면 Payload를 내려 메모리 사용량을 줄일 수 있다.
building.Unload()
LoadNone으로 열어도 Payload Arc와 배치 Prim 자체가 사라지는 것은 아니다. Payload를 통해 들어오는 무거운 자식 콘텐츠가 작업 집합에서 제외된다.
5. Variant Set: 구성 상태를 비파괴적으로 바꾸기
Variant Set은 하나의 Prim에 여러 대안 상태를 정의하고, 그중 하나를 선택한다. LOD, Material, 장비 구성, 주간·야간 표현처럼 제한된 선택지에 적합하다.
from pxr import Usd, UsdGeom, Gf
stage = Usd.Stage.CreateNew(
"assets/traffic_light.usda"
)
# Variant Set을 소유할 신호등 자산의 루트 Prim을 정의한다.
light = UsdGeom.Xform.Define(stage, "/TrafficLight")
stage.SetDefaultPrim(light.GetPrim())
lamp = UsdGeom.Sphere.Define(
stage, "/TrafficLight/Lamp"
)
# 루트 Prim의 Variant Set 컬렉션을 가져온다.
variants = light.GetPrim().GetVariantSets()
# red, yellow, green 선택지를 담을 signalState 세트를 만든다.
signal = variants.AddVariantSet("signalState")
colors = {
"red": Gf.Vec3f(1.0, 0.05, 0.05),
"yellow": Gf.Vec3f(1.0, 0.75, 0.05),
"green": Gf.Vec3f(0.05, 1.0, 0.15),
}
# 각 상태를 Variant로 등록한 뒤 해당 Variant 내부에 색상 의견을 작성한다.
for state, color in colors.items():
signal.AddVariant(state)
signal.SetVariantSelection(state)
# Edit Target을 현재 Variant로 잠시 전환해야 변경값이 Variant 안에 저장된다.
with signal.GetVariantEditContext():
lamp.GetDisplayColorAttr().Set([color])
# 자산을 처음 열었을 때 사용할 기본 선택 상태를 명시한다.
signal.SetVariantSelection("red")
stage.GetRootLayer().Save()
GetVariantEditContext() 안에서 작성한 의견은 일반 Layer 위치가 아니라 현재 선택한 Variant 내부에 저장된다. 도시 Stage에서는 같은 원본 자산을 Reference한 뒤 각 배치에 다른 선택을 기록할 수 있다.
light_01.GetVariantSet(
"signalState"
).SetVariantSelection("red")
light_02.GetVariantSet(
"signalState"
).SetVariantSelection("green")
6. 가장 중요한 설계 판단: Variant인가, Attribute인가?
온도·습도·차량 위치처럼 계속 바뀌는 값은 Attribute나 time sample로 기록한다. LOD·Material·정비 모드처럼 미리 정해진 대안은 Variant로 관리한다.
- Variant: normal / flashing / maintenance, low / high LOD
- Attribute: 현재 신호, 센서 측정값, 객체 위치, 조명 밝기
이번 학습에서는 Composition을 확인하기 위해 신호 색상을 Variant로 구현했다. 그러나 실제 시뮬레이션에서 매 프레임 상태가 바뀐다면 Variant를 런타임 데이터 저장소처럼 남용하지 않는 편이 좋다.
7. 완성된 Stage 구조
/World
├── Buildings
│ ├── Building_01
│ │ └── Reference → building.usda
│ │ └── Payload → building_geometry.usda
│ └── Building_02
│ └── Reference → building.usda
└── TrafficLights
├── TrafficLight_01
│ ├── Reference → traffic_light.usda
│ └── signalState = red
└── TrafficLight_02
├── Reference → traffic_light.usda
└── signalState = green
8. 저장보다 중요한 검증
Composition Arc가 파일에 기록되었다고 해서 콘텐츠가 정상 합성됐다는 뜻은 아니다. 다음 항목을 반드시 확인한다.
defaultPrim과 대상 Prim path가 올바른가?- 상대 asset path가 작성 Layer 기준으로 해석되는가?
- Payload의 Load·Unload 결과가 계층에 반영되는가?
- Variant 이름과 선택값이 실제 자산에 존재하는가?
upAxis와metersPerUnit이 일치하는가?- Omniverse Console에 unresolved asset 또는 composition error가 없는가?
9. 면접 답변으로 압축하기
Reference는 외부 자산을 현재 namespace에 합성해 재사용합니다. Payload는 유사하지만 선택적으로 로드·언로드할 수 있어 대규모 Stage의 작업 집합 관리에 적합합니다.
LOD, Material, 장비 구성처럼 미리 정의된 제한된 대안을 비파괴적으로 선택할 때 사용합니다. 계속 변하는 센서 값이나 위치에는 Attribute가 더 적합합니다.
Prim 유효성, asset path, defaultPrim, Edit Target, Payload 로딩, Variant 선택, 의견 강도를 순서대로 확인합니다. Layer 패널, Console, usdview와 Python API를 함께 사용합니다.
10. DAY 3 체크리스트
□ 건물 Geometry를 Payload로 분리했다.
□ LoadNone, Load(), Unload()를 확인했다.
□ signalState Variant Set을 만들었다.
□ 두 신호등에 서로 다른 Variant를 선택했다.
□ 실시간 Attribute와 Variant의 목적을 구분했다.
□ asset path와 opinion strength 문제를 점검했다.
마무리
오늘의 핵심은 세 가지 API를 외우는 것이 아니라 데이터의 성격에 따라 Composition 방식을 선택하는 것이다.
Payload — 무겁고 선택적으로 로드할 콘텐츠
Variant Set — 제한된 구성 대안
Attribute — 지속해서 변하는 운영 데이터
DAY 4에서는 이 구조를 바탕으로 Geometry와 운영 데이터를 별도 Layer로 분리한다. 원본 3D 자산을 건드리지 않고 센서 상태, 시뮬레이션 값, Physics 설정을 독립적으로 관리하는 단계다.
참고 자료
OpenUSD Introduction ·
UsdReferences API ·
UsdVariantSet API ·
NVIDIA OpenUSD Asset Structure Principles
0 댓글