Houdini Copernicus Ultimate Guide 튜토리얼 정리 · 12편

프로그래밍과 고급 기능

VEX Wrangle과 OpenCL로 COP 안에서 직접 코드를 쓰는 방법, ONNX Inference로 ML 모델을 실행하는 법, Invoke Block 반복 루프와 Subnet 캡슐화까지 고급 기능을 다룸.

생성 · 수정 · 그윈, 코리언메시

[SOURCE] SideFX · The Ultimate Copernicus Guide | Every COP Node Explained

카테고리 houdini

#houdini#copernicus#cop#vex#opencl#onnx

← 목차로 돌아가기

노드만으로 부족할 때 COP 안에서 직접 코드를 쓰는 두 가지 길(VEX Wrangle, OpenCL)과, ML 모델 실행(ONNX Inference), 반복 루프(Invoke Block), 네트워크 캡슐화(Subnet)를 정리함. COP은 기본적으로 모든 것을 GPU에서 돌리는 컨텍스트이므로, 성능이 중요하면 OpenCL이 정석이고 VEX Wrangle은 CPU 실행이라는 점이 선택 기준임. 여기 내용은 핵심 개념의 시그니처(mono/UV/RGB/RGBA)와 좌표 공간(-1~1)을 전제로 함.

이 문서는 SideFX 공식 튜토리얼 The Ultimate Copernicus Guide | Every COP Node Explained를 학습하며 정리한 기록임 — 오리지널 창작물이 아니라 원본 강의 내용을 주제별로 재구성한 자료임.

요약

  • VEX Wrangle: 전 픽셀을 순회하며 VEX 실행. CPU 실행 — 간단한 코드엔 편하지만 최적화 상황엔 비추천. @C 등 바인딩 참조, v@P = 텍스처 포지션(-1~1), @ix = 픽셀 정수 인덱스. 바인딩 추가로 마스크 입력 확장 가능.
  • OpenCL: GPU 커널 — 성능이 중요하면 정석. 입출력 분리 정의(src/dst), #bind layer / parm / ramp 선언, 출력은 @dst.set(). 글로벌: @ix/@iy/@xres/@yres(int — (float) 캐스팅 필요), float 리터럴은 0.0f 접미사 필수, @time은 Include Time 옵션 필요.
  • bufferSample: 레이어 위치 샘플링(픽셀 간 보간) — 중첩 for 루프와 조합해 블러 등 커널 자작.
  • Writeback: 출력을 입력으로 되먹이는 피드백 루프. @WRITEBACK 블록 + Use Writeback Kernel + Iterations.
  • ONNX Inference: .onnx 모델 실행(예: 스타일 트랜스퍼). RGB 입력 요구, Resample Size를 모델 해상도(예: 224×224)로, Brightness Multiplier 1/255. 고해상도·비정사각형 이미지엔 한계.
  • Invoke Block: Block Begin/End를 반복 호출하는 노드 레벨 루프. Iterations에 $F를 넣으면 프레임 누적 피드백 애니메이션. 피드백 시 입출력 구성이 일치해야 함.
  • Subnet: 파라미터에서 입출력 정의 + 내부 Input/Output 노드로 연결 — HDA 제작의 기초. Switch by Type·Layer Properties와 함께 씀.

VEX 랭글 (VEX Wrangle)

COP 안에서 VEX 코드로 픽셀 단위 연산을 작성하는 노드임. 쉬운 대신 CPU에서 돌아감.

주요 파라미터

  • Bindings: 파라미터 하단에 기본으로 C 바인딩이 있음. 지우면 입출력이 사라지고, 다시 추가해 원하는 이름을 붙이면 그 이름의 입력+출력이 생김.
  • 코드에서 @C처럼 @ 문법으로 바인딩을 참조함(일반 wrangle의 attribute처럼).

실행 모델

  • 지오메트리 wrangle이 point/primitive 위를 도는 것과 달리, 모든 픽셀 위를 순회함. "각 픽셀에서 @C 값이 무엇인가"를 평가/설정하는 것임.
  • 코드가 없으면 빈(투명) 사각형이 출력됨 — C의 모든 값이 0이라 alpha도 0이기 때문임(RGBA는 투명, mono면 검정).

문법 예제

@C = 1;                       // 흰 블록
@C = set(1, 0, 0, 1);         // vector4 (빨강, alpha 1) — VEX 문법 전부 그대로 동작
@C.x -= @Time;                // 재생하면 빨강이 점점 빠짐
@C.x = clamp(@C.x, 0, 1);     // 0 밑으로 안 떨어지는 페이드 효과
  • 입력 연결: Fractal Noise(RGBA)를 첫 입력에 연결하면 그 값 위에 코드가 적용됨 — 빨강 채널이 시간에 따라 사라지는 식.
  • 마스크 입력 추가: mask 바인딩을 추가하고 Concentric 램프를 연결 → @Time * @mask로 마스크가 효과 강도를 제어함(바깥쪽에서만 발생). x 말고 다른 채널에도 자유롭게 적용 가능함.

바인딩 이름 충돌 / 글로벌 값

  • P 같은 글로벌 값과 이름이 겹치면 에러 남. v@P텍스처 포지션을 나타냄 — 좌하단 (-1,-1) ~ 우상단 (1,1), R/G 채널의 vector2로 표현됨. P라는 바인딩을 만들면 "P already exists" 에러 → pos 같은 다른 이름으로 바꾸면 해결됨.
  • 픽셀 번호 글로벌: @ix — 좌→우 픽셀 정수 인덱스(0 ~ 해상도, 예: 0~1024).
@C = 1.0 * @ix / 1024;   // 좌 0 → 우 1 정규화 그라디언트
                         // 앞에 1.0 * 을 곱해 float 계산을 강제해야 함

팁 / 워크플로

  • 실전 판단 기준: Wrangle은 CPU 실행임. COP에서는 모든 것을 GPU에서 돌리고 싶으므로 보통 OpenCL을 씀. 다만 성능이 중요하지 않은 간단한 코드는 VEX가 익숙하니 wrangle로 빠르게 쓰는 경우가 많음. 최적화가 필요한 상황에서는 비추천.

오픈CL (OpenCL)

GPU에서 실행되는 커널 코드를 작성하는 노드임. Wrangle보다 복잡하지만 훨씬 빠름. 노드를 놓으면 초기 상태로 검은 사각형(알파 없는 순수 검정)이 나옴.

주요 파라미터

  • Signature 탭: Wrangle과 달리 입력과 출력을 분리해 정의할 수 있음(입출력이 서로 안 맞아도 됨). 기본으로 src(입력)와 dst(출력)가 있음. src는 Optional 플래그가 켜져 있어 입력 없이도 에러가 안 남 — 끄면 입력이 필수가 됨.
  • Bindings 탭: 노드 파라미터(정수, float, ramp 등)를 바인딩으로 추가함.
  • Options 탭: Include Time, Iterations, Use Writeback Kernel 등.

바인딩과 커널 문법

  • Signature나 Bindings에 무언가를 만들면 커널 코드에서 layer 또는 parameter로 선언해줘야 함.
    • layer = 노드로 들어오는 이미지 레이어.
    • parameter = 노드 파라미터(정수, float, ramp 등).
#bind layer src? val=0    // layer 바인딩. ? = optional, val=0 = 입력 없을 때 기본값
#bind layer !&dst         // & = 입력이자 출력(read/write), ! = 입력에서 제외
                          // 합쳐서 "출력 전용"이 됨
#bind parm radius val=2   // 파라미터 바인딩 (노드 UI에 같은 이름 바인딩 필요)
  • 데이터 타입을 안 쓰면 타입 불특정이라는 뜻임. 보통은 float, float2, float3 같은 타입을 명시함 — vector가 아니라 float/float2/float3 표기 사용.
  • 값 출력은 @dst = ...가 아니라 @dst.set(...) set 함수를 사용함.
  • 기본 예제: 커널에 float value = 0.5;를 쓰고 @dst.set(value); 하면 0.5가 출력됨. src의 기본값(val)을 0.5로 바꾸면 입력이 없을 때 0.5, 입력이 연결되면 입력이 우선됨 — optional의 의미가 이것임.

글로벌 바인딩과 캐스팅 (문서의 "OpenCL for VEX users" 섹션 참고)

  • @ix, @iy: 픽셀 정수 좌표. @xres, @yres: 해상도. @ixy, @res: int2 형태.
  • 이것들은 int이므로 픽셀/해상도 나눗셈에서 캐스팅이 필요함:
    • 방법 1: 1.0f *를 곱해 float 연산 강제.
    • 방법 2: (float) 캐스팅 — (float)@ix / @xres처럼 흔히 보게 되는 문법임.
float xgrad = (float)@ix / @xres;          // 좌→우 그라디언트
float ygrad = (float)@iy / @yres;          // 아래→위 그라디언트
// max(xgrad, ygrad), min(...), 합산 등으로 조합
float value = sin((xgrad + ygrad) * 45);   // 사선 스트라이프 패턴. -ygrad로 방향 뒤집기
  • @time 주의: @time을 쓰면 "time is unavailable, turn on Include Time" 에러가 남 — 이런 글로벌은 자동 포함되지 않으므로 Options에서 Include Time을 켜야 함. 시간을 더하면 오프셋, @time * 0.1처럼 줄여 곱하면 패닝 효과가 됨.
  • float 리터럴 주의: clamp(sum, 0, 1)은 에러 남 — clamp는 float을 기대하므로 0.0f, 1.0f처럼 f 접미사를 붙여야 함: sum = clamp(sum, 0.0f, 1.0f);

예제 1 — bufferSample로 블러 커널 직접 만들기

  • Fractal Noise(amplitude 올림)를 입력으로 연결. src의 optional을 해제(?val 제거, Signature의 optional 체크 해제) — 기본값은 optional일 때만 필요하기 때문임.
  • 블러 원리: 각 픽셀이 주변(좌우상하) 픽셀들을 조사해 평균을 냄. (max를 취하는 등 다른 연산으로 변형 가능함.)
#bind layer src float
#bind layer !&dst float
#bind parm radius val=2

@KERNEL {
    float sum = 0;        // 픽셀 값 누적
    int count = 0;        // 이웃 수 카운트
    int radius = @radius; // Bindings의 integer 바인딩 → UI 슬라이더로 반경 조절

    // OpenCL에서 매우 흔한 중첩 for 루프 패턴:
    // 픽셀 기준 상하좌우 radius 픽셀만큼의 정사각형 영역 순회
    for (int x = -radius; x <= radius; x++) {
        for (int y = -radius; y <= radius; y++) {
            float2 pos = (float2)(@ix + x, @iy + y);  // bufferSample이 float2를 기대함
            float value = @src.bufferSample(pos);
            sum += value;
            count++;
        }
    }
    sum /= count;
    @dst.set(sum);
}
  • bufferSample: 들어오는 레이어의 위치를 샘플링함. float 위치를 받아 픽셀 사이 값을 보간해줌. 문서 "OpenCL for VEX users" 맨 아래의 레이어 바인딩 메서드 목록에서 찾을 수 있음.

예제 2 — Writeback (피드백 루프)

  • 커널을 실행 → 출력을 다시 입력으로 되먹여 재실행하는 루프 구조임.
  • 셋업:
    1. Signature에서 입력과 출력을 동일하게 맞춤 — 입력에 src, dst 둘 다, 출력에도 src, dst 둘 다.
    2. 커널 바인딩에서 둘 다 &(read/write)로 지정함(!& = 출력 전용, & = 읽기/쓰기). dst도 float으로 지정.
    3. dst를 입력에도 연결해야 에러가 사라짐(dst가 더 이상 출력 전용도, optional도 아니기 때문임).
    4. Options → Use Writeback Kernel 활성화.
@WRITEBACK {
    @src.set(@dst);   // 커널이 dst를 계산한 뒤, dst를 src로 되먹여 다시 실행
}
  • Iterations 0이면 아무 일 없고, 1, 2, 3...으로 반복 실행됨. Iterations에 @Frame을 넣으면 시간에 따라 블러가 누적됨(radius를 줄여 조절 권장).
  • 보케 유사 효과: 임계값 이상이면 밝기를 키우는 조건 추가 — if (sum > 0.8) sum *= 1.05; → 밝은 영역이 블러되며 점점 빛나는 보케 느낌이 됨(필터의 Bokeh 노드와 비교해 볼 것).

예제 3 — Histogram 연계 채널별 커브 색 보정 커널

  • 배경: Copernicus에 아직 채널별 커브 노드가 없어서 직접 제작하는 예제임(추후 공식 추가 예상). Histogram 노드로 R/G/B 분포를 확인하면서 보정함 — Histogram 자체의 읽는 법은 색·채널 참조.
  • 진단: Mono to RGB로 붉은 색조를 만들어 원본에 Overlay → 히스토그램에서 G/B는 저역에 몰리고 R만 고역까지 분포하는 것 확인 → "red 채널을 보정해야 한다"는 판단 근거가 됨.
  • 셋업:
    1. source 입력을 RGB로 강제(optional 해제). 앞단에서 RGBA to RGB 캐스팅. dst 출력도 RGB(float3).
    2. Bindings에 r_ramp, g_ramp, b_ramp 세 개를 ramp 타입으로 생성.
    3. 커널 상단에 #bind ramp 구문으로 세 램프를 바인딩.
#bind layer src float3
#bind layer !&dst float3
#bind ramp r_ramp
#bind ramp g_ramp
#bind ramp b_ramp

@KERNEL {
    float3 image = @src;
    image.x = @r_ramp(image.x);
    image.y = @g_ramp(image.y);
    image.z = @b_ramp(image.z);
    @dst.set(image);
}
  • 사용: 히스토그램을 띄운 채 OpenCL 노드를 템플릿하고 Composite View로 나란히 보면서, r_ramp의 고역을 낮춰 red를 눌러 균형 잡힌 이미지로 보정함.

팁 / 추가 학습 자료

  • OpenCL 문서가 부족한 편인데, Junichiro Horikawa의 자료가 가장 좋다고 추천됨 — 최근 Copernicus의 OpenCL 노드를 다룬 라이브 영상들이 있음. 공식 문서("OpenCL for VEX users")도 큰 도움이 됨.

ONNX 인퍼런스 (ONNX Inference)

ONNX 머신러닝 모델을 COP 안에서 실행하는 노드임. 예제는 스타일 트랜스퍼임.

주요 파라미터

  • Model File: 다운로드한 .onnx 파일 지정.
  • Set Up Shapes from Model: 실행하면 입출력이 자동 구성됨.
  • Input/Output의 Resample Size(기본 -1,-1): 모델이 기대하는 해상도로 리샘플.
  • Output 섹션의 Brightness Multiplier.

팁 / 워크플로 (스타일 트랜스퍼 예제)

  • 모델 준비: ONNX GitHub("onnx models" 검색, 첫 GitHub 페이지) → validated 폴더 → vision → style transfer → fast neural style → models 폴더. Houdini 문서 예제가 쓰는 Mosaic 9 모델(mosaic-9.onnx)을 raw로 다운로드. 저장 위치는 hip 폴더 권장(문서 권장사항).
  • 셋업 절차:
    1. Model File에 .onnx 지정 → "Set Up Shapes from Model" 실행.
    2. 입력(File 노드의 나비 이미지) 연결 — 바로는 동작 안 함:
      • RGBA가 아니라 RGB를 기대함 → RGB로 전환해 입력 에러 해결.
      • 해상도 문제: 구성된 매트릭스 값 1, 3, 224, 224(입력/출력)는 "이미지 1장, 3채널(RGB), 해상도 224×224"라는 뜻임.
    3. 입력 이미지가 224×224가 아니므로 Input/Output의 Resample Size를 224, 224로 설정함.
    4. 결과가 지나치게 밝게 나옴 — 처리 방식이 값을 더해가는 방식이기 때문임 → Output의 Brightness Multiplier를 1/255로 설정하면 해결됨.
    • 결과: 나비가 모자이크 스타일로 변환됨.
  • 한계/미해결 사항:
    • 다른 해상도로는 동작하지 않는 것으로 보임 — ONNX 모델이 주어진 입력 크기보다 큰 해상도로는 안 도는 듯함(타일 단위 실행 가능성은 추측 수준).
    • 정사각형이 아닌 이미지는 정사각형으로 찌그러뜨려 처리되므로 실용성이 떨어짐.
    • 현재로선 "동작시키는 법" 수준의 기본 개요임.

인보크 블록 (Invoke Block + Block Begin / Block End)

별도로 정의한 블록(서브그래프)을 호출해 실행하며, 반복(iteration) 실행으로 루프를 구현하는 노드임.

주요 파라미터

  • 블록 지정: Invoke Block에서 Block End 노드를 드래그해 지정.
  • Set Inputs and Outputs from Selected Block: 블록의 입출력을 Invoke Block에 반영함.
  • Iterations: 블록 반복 실행 횟수.

셋업 방법

  • Invoke Block 단독으로는 아무것도 안 함. 반드시 블록과 함께 써야 함.
    • 방법 1: Block Begin + Block End를 각각 놓고, Block Begin의 Block Path에 Block End를 드래그해 참조 연결.
    • 방법 2: Block 노드를 한 번에 드롭.
  • 블록은 기본적으로 입출력이 없음 — Block Begin에 Input을, Block End에 Output을 직접 추가해야 함.
  • Invoke Block에서 Block End를 지정한 뒤 "Set Inputs and Outputs from Selected Block"을 실행해야 입출력이 생김(블록이 선택 안 된 상태에서 실행하면 안 됨).
  • 이후 "input 1 is missing" 에러가 나면 입력(예: File 노드)을 연결하면 됨.

팁 / 워크플로

  • 동작 예제: 블록 내부에 Transform 2D(회전 5도)를 넣으면, Invoke Block을 거친 나비 이미지가 회전됨. Iterations를 올리면 블록이 반복 실행되며 회전이 누적됨. 블록 안에 Blur(streak 방향: angle/length 지정)를 추가하면 iteration마다 회전+블러가 누적 적용됨.
  • $F 피드백 애니메이션: Iterations에 $F 표현식을 넣으면 프레임마다 반복 횟수가 증가 — 매 프레임 회전·블러가 누적되는 피드백 애니메이션이 됨.
  • 즉 이것은 루프의 등가물임: 블록에 값을 넣고 한 번 실행 → 결과를 다시 입력으로 피드백 → iteration 수만큼 반복. OpenCL의 Writeback과 개념적으로 같은 패턴을 노드 레벨에서 구현하는 것임.
  • 주의: 입력과 출력은 여러 개 가능함. 단, 반복(피드백) 효과를 쓰려면 입력 개수/구성과 출력이 일치해야 함 — 어떤 값을 블록에 되먹임할지 알아야 하기 때문임.
  • 참고: 같은 Block Begin/End 구조가 뷰포트 슬랩 컴프 등록(Slap Comp Block)과 블록의 디스크 저장(Block to Geometry)에도 쓰임 — 렌더·입출력 참조.

서브넷 (Subnet / Input / Output 노드)

HDA 제작의 기초가 되는 서브네트워크 입출력 구성임.

주요 파라미터

  • Subnet 파라미터에서 입력/출력을 정의함(이름과 타입).
  • 내부의 Input / Output 노드가 정의된 입출력과 실제 네트워크를 연결함.

팁 / 워크플로

  • 예제 셋업: File(기본값 butterfly) → 이미지를 "값(value)"과 "색(color)"으로 분리하는 네트워크.
    • RGB to Mono(시그니처를 RGBA로 전환 — Switch로 처리할 수도 있지만 여기선 직접 전환)로 그레이스케일 생성.
    • 원본을 이 mono로 Divide → 색 성분과 값 성분이 분리됨.
  • Output 연결 규칙:
    • 네트워크를 Subnet으로 묶으면 기본적으로 출력이 없음. Subnet 파라미터에서 출력(예: value, color)을 추가해도, 그것만으로는 내부와 연결되지 않음.
    • 내부에 Output 노드를 드롭하면 Subnet에 정의된 출력들이 자동으로 등록됨 → 내부 노드들을 여기에 꽂으면 실제 출력이 됨. 출력 타입도 맞춰줘야 함(예: value는 mono 타입으로).
  • Input 연결 규칙:
    • 마찬가지로 Subnet에 입력을 정의한 뒤, 내부에 Input 노드를 드롭하면 사용 가능한 입력들이 자동 등록됨 → 내부 네트워크에 연결하면 동작함.
  • HDA를 만들 때는 들어오는 데이터 타입에 따라 분기하는 Switch by Type(ID·세그먼트 또는 유틸리티 노드 참조)과, 데이터를 처리 전에 올바른 상태로 준비하는 Layer Properties(렌더·입출력 참조)가 함께 쓰임.

Houdini Copernicus Ultimate Guide 튜토리얼 정리 · 전체 12편

  1. 00 Houdini Copernicus Ultimate Guide 튜토리얼 정리 index
  2. 01 핵심 개념
  3. 02 생성·패턴 노드
  4. 03 값 조정과 리매핑
  5. 04 블렌딩과 합성
  6. 05 색 보정
  7. 06 필터와 이펙트
  8. 07 SDF와 거리장
  9. 08 UV·왜곡·좌표
  10. 09 지오메트리·3D 연동
  11. 10 ID·세그먼트·변환 유틸리티
  12. 11 파일 입출력과 렌더 파이프라인
  13. 12 프로그래밍과 고급 기능