VFX

2022. 7. 31. 22:09Public/Unity

[VFX Graph 란?]

1)컴퓨트 셰이더 기반의 GPU 파티클 도구

(컴퓨트 셰이더란?) 
https://docs.unity3d.com/kr/2018.4/Manual/class-ComputeShader.html

2)GPU의 강력 병렬 파워를 사용하여 수백만 개의 파티클을 뿌릴 수 있음

3)뿌릴 수는 있는데 이것도 많으면 느려짐.

4)모바일에서도 사용 가능 (OpenGL ES3.1이상)

한줄로 요약하자면 
계산을 넣어 -> 파티클을 더 많이 사용할 수 있는 툴
 
 
[컴퓨트 셰이더와 VFX Graph]

1)GPU는 주로 화면에 그리는 것을 담당.

2) GPU의 엄청난 병렬 계산 능력을 활용하기 위해서는 셰이더언어로 접근. 이것이 Compute(계산)셰이더

3) 고정 파이프라인대로 화면에 그리는 버텍스/픽셀 셰이더를 이용하지 않고, 오로지 GPU의 계산능력만을 이용하는 것

4) 컴퓨터 셰이더를 이용하려면 커널,스레드,그룹 이라는 곳에 데이터를 적절하게 집어넣는 작업이 필요. 아티스트에게는 난이도가 높다.

5) 하지만 이 모든걸 다 알아서 해주고 아티스트는 노드만 짜면 되는것이 바로 VFX Graph
 
 
 

[Shuriken과 비교하면 어떤 부분이 다를까요?]

1) 컴퓨트 셰이더로 계산하기 때문에 다소 무거운 계산도 빠르게 처리 가능

2) 슈리켄에서는 다소 무거웠던 Noise,Light,Collider 모듈 등도 빠르게! 사용가능

3) 수학 공식을 이용하여 제네레이티브한 움직임을 만들어 내는 것이 가능
   (가장 쉬운 예로는 포지션값에 sin()등을 사용할 수 있다. 그 외에도 내적 외적 등 다양한 계산 가능)

4) 다양한 어트리뷰트 (속성) 값을 이용하여 그것을 이용한 계산을 할 수 있음

5) 벡터 필드, 포인트 캐시 이용 가능

6) 랜덤 함수가 추가됨

7) 각종 공간변환(행렬)을 사용 할 수 있음

8) 변수 제어가 용이해짐
 
 
 
[VFX Graph의 전체 그림]

1) 4가지의 상 - 하 블록 에서 파티클에 값을 집어넣음

2) 좌 - 우 노드를 추가하여 특정 값이 아닌 계산에 의한 값을 집어 넣을 수 있음

3) 변수를 추가하여 외부에서 제어가능
 
 
 
[잠시 원론적인 이야기]

1) 생성하는 녀석을 일반적으로 Emitter 라고 하고

2) 생성된 녀석을 일반적으로 Particle 이라고 합니다.

--- 일종의 클래스 - 인스턴스 관계라고 생각해도 될듯 합니다.
 
 
 
[이미터는 어떤 정보가 필요할까요?]

녹색 : 이미터가 파티클에게 알려주어야 할 정보 
파랑 : 알려주지 않아도 되는 정보

1) 몇 초 동안 Emission을 해야 할지 (Duration) 파랑

2) 몇번 반복해야 할지(Loop) 파랑

3) 얼마나 자주 생성 시킬지 (Spawn Rate - Emission) 파랑

4) 최대 파티클 수는 얼마일지(Capacity - Max Particles) 파랑

5) 몇 초 동안 살아있어야 할지(Lifetime) 녹색

6) 어떤 곳에서 파티클을 생성시켜야 할지(Position) 녹색

7) 어떤 방향으로 보낼지. 어떤 힘으로 보낼지(Velocity - Speed) 녹색

8) 생성될때 크기는 어떨지 (Scale - Size)회전값은 어떨지 (Angle - Rotate) 녹색

9) 처음 컬러는 어떨지(Color) 녹색
 
 
 
[이미터는 어떤 정보가 필요할까요?]

1) 기본적으로 이미터가 넘겨준 값(어트리뷰트)은 일단 다 들어와 있는 상태

2) 이 어트리뷰트들을 어떻게 변형할지

3) 총 라이프 타임 대비 얼마나 살아왔는지 (Age - OverLifeTime)

4) 어떤 힘을 받아 움직일지 (Force)

5) 어떤 충돌을 적용할지 (Collision)

6) 어디를 향하고 있어야 할지 (Orient)

7) 어떻게 렌더링 되어야 할지 (Output - Renderer)
 
 
 
[이미터는 어떤 정보가 필요할까요?]

1) 기본적으로 이미터가 넘겨준 값(어트리뷰트)은 일단 다 들어와 있는 상태

2) 이 어트리뷰트들을 어떻게 변형할지

3) 총 라이프 타임 대비 얼마나 살아왔는지 (Age - OverLifeTime)

4) 어떤 힘을 받아 움직일지 (Force)

5) 어떤 충돌을 적용할지 (Collision)

6) 어디를 향하고 있어야 할지 (Orient)

7) 어떻게 렌더링 되어야 할지 (Output - Renderer)
 
 
 
[블록에 대해서 알아봅시다]

1) VFX Graph는 크게 4가지의 블록으로 구성

2) SpawnInitialize Update Output

3) Particle이 하는일은 Update, Output

----이니셜라이즈 끝부터 Particle이라고 써져있는 부분은 다 이유가 있음
 
 
 
 
 
[Emitter - Spawn과 initialize의 차이]

Spawn은 이미터 '만'알면 되는 값 - 생성
(Spawn)과 관련된 값

Initialize는 파티클 '도' 알아야 하는 값 - 여러 속성 (Attribute)값
 
 
[1.Spawn 블록]
- 4개 블록중 가장 간단!
- Loop와 Delay를 넣을 수 있다.
- Event로 시작과 끝을 넣을 수 있다.
- Spawn Rate에 변수를 만들어 넣을 수 있다.
- Periodic(주기적) Burst 노드를 사용할 수 있다.
- 이게 끝..?
 
 
[2. Initialize 블록]

  • 여러 Attribute를 붙일 수 있음
  • Lifetime, Velocity , Angle, Color,Scale . . .
  •  Bound (Sphere)를 붙여봅시다.
  • 인스펙터 옵션으로 노드속성 변경 가능

-> 인스펙터에서 노드속성 변경

  • Set Attribute
  • 노드를 클릭한 채로 인스펙터를 봅시다.
  • Overwrite(=Set)
  • Add,Multiply,Blend
  • 사실 Set 하나로 다 끝낼 수 있는것을 늘려놓은 상태
  • Get과 Set에 대해서 조금 더 자세한 설명

[어트리뷰트 종류]

  • 주로 사용하는 중요한 것들 위주로, 너무 직관적인건 빼고(lifetime,size(uniform),scale)
  • OldPosition - 한 프레임 전 위치
  • Position - 현재 위치
  • Velocity - 방출하는 힘(방향과 힘이 모두 있는 상태).Vector3(Velocity)가 벡터임을 아는것이 매우 중요
  • Direction  - 방출되는 방향 (노멀라이즈되어 방향만 남은 값)
  • Age - 라이프타임 대비 얼마나 진행되었는지 (0~1값)
  • TexIndex - SubUV를 제어할 수 있는 Index 값
  • Particle ID - 생성된 순서대로 인덱스(ID)값이 주어짐. 매우 중요하니 뒤에 다시 다룸
  • axisX,Y,Z - 로컬 축
  • Alive - 살아있는지 소멸했는지(BOOL)
  • TargetPosition - Line을 렌더링할때만 사용.Line의 목표지점
  • Angle - Rotation 회전각
  • Angular Velocity - 회전하려는 힘

 
 
[Velocity가 Vector3 임이 중요한 이유]

  • Vector3는 Unity에서만 사용하는 자료형.
  • Float3 처럼 float 세 개를 가지고 있으나 이것이 벡터임을 명시
  • 각 3축으로 향하는 값을 알고 있으면 방출하는 방향과 힘을 알 수 있다.
  • Velocity를 알고 있으면 이전 위치, 다음 위치를 얻을 수 있다.
  • 이전 위치 = Position - (Velocity / FrameRate)
  • 다음 위치 = Position + (Velocity / FrameRate)
  • 운동 방향을 계산할 수 있다는 것은 아티스트의 '의도'대로 움직이게 할 수 있다는 것

 
 
 
 
 
 
[3. Update 블록]

  • 매 프레임 Update 되는 블록
  • 여기서 Set을 한다면 initialize값이 의미가 없어진다 (굳이 중복 계산을 할 필요는 없다. 상황에 맞는 블록에서 계산하면 됨)
  • Initialize된 어트리뷰트를 Get을 통해 불러올 수 있다.
  • 업데이트는 상-하 블록만으로는 뭔가 하기 애매한 경우가 많다.(overLifeTime은 아웃풋에 있기 때문에...)
  • Update에서는 본격적으로 상-하 블록이 아닌 좌-우 노드를 사용

 
 
[Update 1]

  • 어트리뷰트를 Get하여 사용해보자!
  • Fractional(Frac)은 정수를 버리고 소수를 취하는 함수
  • 파티클의 위치값에 따라서 색이 다르게 나오게 할 수 있다.

 
 
[Update2 - 안쪽으로 모이는 힘(Force) 만들기]

현재 포지션을 Normalize하여 중심점으로부터의 벡터를 얻고 음수를 곱해주면 강하게 안쪽으로 모이는 힘을 얻을 수 있다.
 
 
 
 
[Update3 - 커브 샘플링하기]

  • 계산으로 만들기 빡센 움직임은 그냥 커브를 만들어 넣을 수도 있다.
  • Age Over LifeTime을 Time에 연결. 가로축이 Age가 된다.
  • Output 블록에서 overLifetime을 손쉽게 넣을 수도 있다. 생각대로 움직이지 않는다면 중복인지 체크!

 
[Update4 - 터뷸런스(Turbulence) 사용해보기]

  • Field Transform도 변형해보면 재밌는 효과가 나온다.
  • Relative - 파티클의 원래 힘에 합해서 계산
  • Absolute - 파티클의 힘을 단순히 덮어씌워 계산
  • Intensity : 난류의 강도
  • Drag : 난류의 저항
  • Frequency : 난류의 파장
  • Roughness: 거칠게 흩어지는 정도
  • Lacunarity : 프랙탈이 공간을 채우는 간격

 
[Update 5 - 노이즈 노드 사용]

  • 보통 Position 어트리뷰트 노드를 Coordinate에 연결
  • Value,Perlin,Cellular
  • Curl Noise는 아티팩트를 줄일 때 사용 (Value와 Perlin만 있음) 패턴화된 모양을 해소시킬 수 있음
  • Random Number는 범위 내에서 무질서한 값을 출력하지만 Noise는 '연속성'이 이쓴 랜덤값을 출력

 
 
 
[Output 블록]

  • 기본으로 Quad Output이지만 변경가능
  • Lit이 들어간 아웃풋은 빛을 받을 수 있다.
  • Blend가 기본으로 Alpha기 때문에 Lit으로 변경하려면 Opaque으로 변경한 후 해야함!
  • Shader Graph 연동도 여기서 할 수 있다.
  • Orient는 매우 중요하다
  • 슈리켄에서 하던 OverLife를 여기서 사용할 수 있다!
  • Line Output은 조금 특이하다
  • 플립북(Sub UV) 설정도 여기서 할 수 있다.

 
[좌 - 우 노드 몇가지 응용사례 소개]
슈리켄에서 할 수 없었던 것들을 계산을 통하여 해 봅시다.
 
[응용1 - 원운동 , 위글러] //?

  • cos(x) 와 sin(y)를 더하면 원운동
  • 이미터가 원궤적을 그리며 돌게 해보자
  • X,Y,Z 값을 각각 다르게 움직이게 하면 위글러

[응용2 - HSV to RGB] // Update

  • 계산으로 구한 특정 값을 HSV 중 H에 넣어서 Hue 배리에이션
  • S와 V는 주로 1 근처 값을 사용
  • Hue는 Fraction과 함께 사용하면 자연스럽게 루핑이 되어 좋다
  • 다채로운 색을 표현하기에 좋다

[응용3 - Particle ID]

  • Particle ID를 Position.X 에 연결
  • 특정 변수를 넘어가면 Y값에 1을 더하고 다시 반복 (Modulo & Divide)
  • 특정 변수의 제곱을 넘어가면 Z값에 1을 더하고 반복 

 
[응용4 - 포인트 캐시]

  • Set Position from Map 노드
  • 포인트 캐시 추출 유틸리티
  • Mesh로 만들면 3D 포인트 캐시
  • Color,Normal,UV등의 정보도 출력가능

 
 
[응용5 - 볼륨 렌더링]

  • 미리 벡터 필드를 구해와야 한다.
  • SDF (Signed Distance Field)와
  • DGF(Distance Gradient Field) 필요
  • 후디니에서 쉽게 생성할 수 있다
  • 편의상 두 줄로 만들어 두자

 
[SDF의 원리]

  • SDF에서 Signed는 음스와 양수를 모두 표현할 수 있다는 뜻이다 
    (Unsigned는 양수만. 음수 / 양수를 구분할 수 있는 Sign이 Type구조안에 있는가 없는가에 따라 다르다)
  • 즉 특정 볼륨의 SDF는 정확하게 동일할때 0
  • 바깥으로 갈수록 양수증가
  • 안쪽으로 갈수록 음수증가
  • 특정 볼륨으로부터의 거리를 VectorField화 한것
  • Distance field는 여러 곳에 등장하나, 특정 모양으로 확장 / 축소를 할 수 있다는 공통적인 성질을 가지고 있다.

 
 
[Distance Gradient Field 가 뭐지?]

  • SDF는 어디서 들어본적이라도 있는데 이건 너무 생소하다!
  • 하지만 어렵게 생각 할 것은 없다. 움직이는 파티클을 보면 금방 알 수 있다
  • SDF는 모양! DGF는 방향!
  • 방향을 얻는다면 또 할 수 있는 것들이 많다 (내적,외적 등) - 업벡터와의 외적을 이용하여 (마치 탄젠트처럼) 표면을 따라 움직이게 한다든지

[DGF의 역할]

  • Update 에서 볼륨의 형태부분에 특정 힘을 가하는 방식
  • 볼륨모양으로 방출하게 만드는 벡터필드
  • Negative가 있으면 안쪽, 없으면 바깥쪽

 
 
 
[응용6 - Sphere Collider 적용]
이것을 이용해서 충돌처리 가능

  • VFX Property Binder
  • 외부에서 VFX Graph Property에 직접 값을 넣어주는 편의 스크립트
  • 외부 데이터를 VFX에서 시각화 하는 데에도 사용 가능

 
[VFX 프로퍼티 데이터 타입]

  • 일단 굉장히 많다.
  • Transform을 통으로 받아올수도 있음
  • 벡터필드는 Texture3D
  • Uint(unsigned Int)는 쓸곳이 많다.
  • OrientedBox - Box인데 오리엔트(어딘가를 바라보는)Box
  • Camera - 카메라.FOV,Near/Far Plane,aspectRatio,Buffer 등을 제어
  • FlipBook - 가로 x, 세로 y 몇번째 인덱스인지
  • 도형들은 뭔가 계산에 사용할 수 있을것 같긴한데 그냥 없어도 될거같다

 
[응용7 - Line Output]

  • Line Output만은 다른녀석들과 조금 다르게 TargetPosition이라는 어트리뷰트에 값을 넣어주어야 한다.
  • 현재 Position과 Target Position 사이를 잇는것이 Line

 
[응용7 - 더 쉽게 하는법!]

  • Line용 'Position : Sequential' 노드가 있음
  • 익스페리멘탈에 체크 해야 보이는 숨겨져 있는 노드

 
[응용 8 - Shader Graph 연동]

  • Shader Graph에서 VFX Shader Graph를 제작
  • Shader Graph에서 프로퍼티를 만들면 알아서 Vfx Graph Output노드에 프로퍼티가 추가된다
  • Shader Graph의 아웃풋은 무조건 들고 오기 때문에 조금 불편한 면도 있다 (무조건 컬러를 덮어씌우기 때문에 그래디언트가 있었다면 VFX 프로퍼티로 그래디언트등을 추가해서 연결해 주어야 한다)

 
 
 
 
 
 
 
참고자료 : https://www.youtube.com/watch?v=POnVRD6_QaU