본문 바로가기

Houdini/Houdini1_Volumes

06_SOLVER와 함께 VOLUME 다루기


Volume과 Solver를 함께 사용할 때 신경써야할 것

  • Solver가 안정적으로 반복이 될 수 있도록 만드는 것이 중요하다.
  • 불필요한 정보는 깔끔하게 정리해주는 것이 좋다.
  • 시작과 끝의 포멧을 맞춰주는 것이 엄청나게 중요하다.

 

solver에 넣기 전에 solver 밖에서 한번의 사이클이 어떻게 진행될지 잘 짜주는 것이 중요하다.

 

sphere 형태의 SDF가 팽창하듯이 점점 커지려면 어떤 정보가 필요할까?

  • VDB Advect가 필요하다.
  • polygon으로 SDF를 만들기 위한 VDB from Polygon이 필요하다.
  • 외부로 팽창하는 방향을 위해 gradient를 얻기 위한 VDB Analysis가 필요하다.

기본적인 사이클
Timestep을 올린 결과

표면이 자글자글하게 퀄리티가 낮아보이는 이유는, 현재 세팅에서는 처음에 분석한 gradient 정보만을 가지고 팽창하기 때문이다.

 

Solver를 꺼내보자.

Hello Solver

 

For Loop with Feedback을 사용해서도 비슷하게 묘사할수도 있지만, Solver를 사용해보는 이유는, 한 프레임이 변할 때 지난 프레임 대비 얼만큼의 변화가 있을지 묘사해보기 위해서이다.

 

위 노드들 중 얼만큼이 solver 안으로 들어가야할까?

이만큼?

위 이미지처럼 VDB from Polygons - VDB analysis - VDB Advect 를 다 solver에 넣어줄수도 있지만, 논리적으로 안정적인 방법은 아니다.

  • 실제로 solver에 넣어주면 작동은 한다.
  • 하지만 안정적인 방법은 아니다.
  • 11frame에서 solver의 prev_frame에 들어가는 정보는? polygon 정보이다.(현재 solver의 시작프레임이 11frame)
  • 11frame에서 solver를 통해 얻게 되는 정보는? VDB Advect의 결과로 SDF이다.
  • 12frame에서 solver의 prev_frame에 들어가는 정보는? 11frame의 결과로 얻은 SDF이다.
  • 처음 폴리곤의 정보를 받아서 VDB from Polygons 노드를 활용해 SDF로 변환해주는 것은 옳지만, SDF의 정보를 VDB from Polygons 노드로 입력하는 것은 약간 문제가 있다.

solver 안의 내용을 살짝만 바꿔주면 된다.

VDB from Polygons 노드를 solver 밖으로 빼줬을 때 얻는 이익은?

solver 시작 프레임의 첫 시작부터 SDF를 이용하고, VDB Advect의 결과도 SDF이다. 다음 프레임의 prev_frame으로 들어갈 정보도 SDF이고 결과는 SDF로, solver가 작동하는 모든 frame에서 시작과 끝의 포멧이 SDF로 동일하다.

 

solver를 사용함으로 얻는 이익은?

solver 없이 timestep만 올라갔다면, 처음 SDF를 분석한 gradient만을 활용해서 timestep이 적용되므로 퀄리티의 차이가 난다.

  • solver 없이 timestep을 0.1만큼 올려준 결과와, solver를 사용해서 0.02씩 올라가서 timestep이 0.1일 때의 결과는 차이가 난다.
    • SDF의 최신 정보를 몇번이나 분석하느냐가 차이가 난다. 
    • solver 없이 timestep만 올린 경우에는 gradient는 처음에 분석한 한번이다.
    • solver가 사용될 경우, 한 프레임에 얼만큼 이류가 일어날지 timestep으로 정하는 부분은 동일하지만, 다음 이류가 일어날 때, 앞서 얻었던 gradient를 그대로 가져다 사용하는 것이 아니라, 이전 결과를 바탕으로 또 한번 분석을 진행해서 얻은 새로운 gradient를 사용한다.(모양이 바뀐만큼 새롭게 gradient를 갱신해서 사용한다.)

solver를 사용하지 않은, timestep 0.5의 결과
solver를 사용한 결과


템플릿을 만들어보자.

 

닫혀있는 폴리곤이 주어진다면, solver에 의해 덩치가 커지는 시스템을 만들 것이다. 그 결과는 폴리곤이 될 수도, fog volume이 될 수도, SDF volume이 될 수도 있다.

 

당장에 solver를 꺼내서 작업하는 것이 아니다.

한 프레임에 어떤 작업을 거치기를 원하는지 사이클을 잘 구축하는 것이 중요하다.

우리가 결과로 얻기를 원하는 포멧이 폴리곤이라면, 폴리곤으로 바꿔주는 노드를 어디에 달아줘야할지 생각해서 정해야한다.

만약 결과로 얻기 원하는 포멧이 fog volume이라면, 어디에서 처리해줘야할지 잘 정할 수 있어야한다.

 

기본적으로 normal방향으로 팽창하는것 처럼 묘사하기 위해서 SDF는 반드시 필요하다.

SDF 정보를 바탕으로 gradient 정보도 필요하다.

 

꼭 명심해야할 것은 solver의 시작과 끝의 포멧이 같아야 안정적이다.

 

이론상으로는 다른 방식으로도 묶는 것이 가능하다.

  • 시작과 끝의 정보 포멧만 맞춰주면 된다.

만약 solver를 다룸에 있어 꼭 convert VDB를 사용해서 Polygon으로 끝을 내고 싶다면, 다음 frame의 prev_frame에 들어가게 될 정보는 polygon이 된다. 이 때 solver에 들어가서 반복하게 될 내용은 다음과 같을 것이다.

  • VDB from Polygons가 solver 안에 없을 경우, 다음 frame의 prev_frame에 들어간 폴리곤 정보를 가지고는 VDB Analysis나 VDB Advect를 진행할 수 없다.

 

만일, convert VDB를 사용해서 fog로 끝을 내고 싶다면? 무언가 새로운 노드가 필요할 것이다.

시작과 끝의 포멧이 Fog일 때의 개념도

 

지금 시작과 끝의 포멧을 맞추는 것을 강조하는 이유

  • 간단한 세팅일 때, 시작과 끝의 포멧이 같아야 한다는 뉘앙스를 꼭 가지고 있을 필요가 있다.
    • 그렇게 해야 나중에 작업이 복잡해질 때, 검토의 기준이 명확하게 생긴다.
    • 볼륨의 이름에 대해서도 마찬가지이다.
      • 여러 볼륨이 동시에 쓰이다보면, 한 사이클의 마지막 결과로 나오는 볼륨의 이름이 처음 시작에 필요한 볼륨의 이름과 다르게 끝날수도 있다.
      • 이런 경우 solver를 돌렸을 때, 원하는 작업이 두번째 프레임부터는 진행되지 않는 경우가 발생한다. 스타트 프레임에서는 되는 것 같다가 그 뒤로 작업이 안되는 경우이다.
    • 스스로 검토하고 정리하기 위해서는 기준이 필요하다. 이 기준이 바로 시작과 끝의 정보가 동일해야 한다는 것이다. 형태(포멧)도 같아야하고 기왕이면 이름도 같아야한다.
  • 작업의 합리성과 효율성 때문이다.(계산의 효율성 측면)
    • VDB를 다루다보면, 정보의 손실이 꽤 많이 일어난다. 
      • 그 중에서도 convert VDB가 자주 쓰이다보면 당연하게도 정보가 손실될 확률이 높아진다.
    • 불필요한 계산이 늘어나는 것은 속도에도 좋지 않은 영향을 미친다.
      • 면으로 바꿨다가 다시 SDF로 바꾸는 과정, 생각보다 오래 걸린다.
      • SDF > Fog / Fog > SDF로 변환하는 과정 또한 매 프레임마다 변환을 하게 될 경우, 시간낭비가 발생한다. 정보의 손실확률 또한 높다.
      • 그래서 볼륨을 다루면서 solver를 사용할 때는 가장 핵심이 되는 작업만 넣어주는 것이 효과적이다.

 

Start - End 구간을 solver에 넣어줬다.

 

이번에는 굳이 convert VDB를 활용해 solver의 결과로 폴리곤을 얻는 경우이다.

 

이번에는 convert VDB로 Fog를 solver의 결과로 받을 수 있도록 해보자.

근데 결과가 이상하다. 팽창이 아닌 수축을 한다.

  • 현재 논리적으로 틀린 부분은 없다.
  • 검토를 해보자
    • 주어지는 정보는 fog VDB, solver가 끝났을 때의 결과도 fog VDB이다.
    • 과정 상에서 정보의 형태가 틀린 것은 없다.
    • Parameter에 문제가 있나?
      • 현재 solver 내부의 이류는 계속해서 커지는 방향으로 팽창하게 될 것이다.
      • 맨 처음에 제공해주는 for volume이 solver 내부에서 SDF로 바뀔 때 부피가 줄어든다.
        • 이 과정이 문제이다.
        • convert VDB의 Fog Isovalue를 조절해 주는 것으로 해결할 수 있다.
          • Fog Isovalue값이 높을수록 Fog > SDF일 때 부피가 많이 줄어든다.
          • Fog Isovalue값이 0이면 문제가 발생한다.
          • 0보다 미세하게 큰 값(0.0001)을 넣어주면 될 듯 하다.
      • Fog Isovalue 값을 0.0001로 바꿔줌으로써 축소되는 것은 막았지만, 커지고는 있으나 우리가 원하던 모양과는 다르다.
    • 해상도에 문제가 있는지 확인이 필요하다.
      • VDB from Polygons 노드의 Voxel size를 조절해서 해상도를 높여줄 수 있다.
      • 하지만 답은 아니다.
    • 다시 기준을 살펴볼 필요가 있다.
      • 시작 / 끝의 정보가 동일한가?
      • 이것이 계산 효율상 최선인가?
        • 앞의 두 경우보다 이 경우는 봐야할 파라미터도 많다.
        • 기왕이면 이 방법은 좋은 방법은 아니구나 하고 다른 방법을 사용하기를 권장한다.
        • convert VDB는 최대한 마지막에 한번 사용한다. 이런 느낌으로 쓰는 것이 좋다.

시간이 지날수록 튀어나온 곳은 더 튀어나오도록, 들어간 곳은 더 들어가도록 만들어보자.

 

필요한 정보

  • SDF가 필요하고, gradient, curvature가 필요하다.
  • 전 프레임의 결과를 바탕으로 작동하는 것이기 때문에 solver 또한 사용된다.

 

무시무시한 러버토이

이제 solver를 사용하기 위해 확인을 해보자.

각 노드에서 노드로 흐르는 정보의 포멧을 나타낸 것이다.

흐르는 정보의 포멧으로 보았을 때, solver에는 시작과 끝이 SDF 포멧이 되는 것이 좋을 듯 하다.


이제 좀 더 손을 보자.

  • VDB Smooth(blur)를 활용해서 너무 들쭉날쭉한 curvature 값을 완화시킨다.
  • Advect 되는 속도를 조절한다.
  • Vector Field 내에 다른 값들을 추가해본다.(ex - wind, noise)

VDB Smooth를 좀 더 손보자.

Filter Voxel Radius - 수치만큼의 voxel 칸을 기준으로 smooth가 발생한다는 의미

Iterations - 몇번이나 smooth가 발생하느냐에 대한 항목(smooth의 강도)

Filter Voxel Radius 2 / Iterations 4 / 30F
Filter Voxel Radius 1 / Iterations 1 / 15F

Use World Space Radius Units - 한칸, 두칸등으로 기준을 잡는 것이 아니고, 실제 거리값을 기준으로 smooth를 발생시킨다.

Use World Space Radius Units On / Filter Radius 0.1 / Iterations 1 / 30F
Use World Space Radius Units On / Filter Radius 0.03 / Iterations 1 / 15F
Use World Space Radius Units On / Filter Radius 0.03 / Iterations 3 / 15F

 

Advect의 속도를 바꿔보자.

  • Timestep의 수치를 조절해준다.
  • Timestep의 수치가 줄어들면, 한번의 사이클에서 이류되는 양이 줄어들기 때문에 효과가 느리게 발생하는 것 처럼 보일 것이다.

Use World Space Radius Units On / Filter Radius 0.03 / Iterations 3 / Timestep 0.001 / 15F

속도가 느려짐과 동시에 더욱 많은 자연스러운 결과들을 얻을 수 있다.

만약 Timestep이 0.005라면?

Use World Space Radius Units On / Filter Radius 0.03 / Iterations 3 / Timestep 0.005 / 15F

Timestep 수치를 높이게 되면서, 한 프레임에 변하는 양이 굉장히 늘어나고 있다.

  • 프레임과 프레임 사이에 어떤 과정으로 인해 커졌을지에 대한 정보는 부족할지도 모른다.

위와 같은 효과를 내줄 때는, Timestep이 작으면 작을수록 더욱 디테일한 결과들을 얻을 수 있구나 라고 생각해도 좋을 듯 하다.

 

Use World Space Radius Units On / Filter Radius 0.03 / Iterations 3 / Timestep 0.0005 / 30F
Use World Space Radius Units On / Filter Radius 0.05 / Iterations 5 / Timestep 0.0005 / 90F
Use World Space Radius Units On / Filter Radius 0.05 / Iterations 3 / Timestep 0.0005 / 60F
Use World Space Radius Units On / Filter Radius 0.05 / Iterations 2 / Timestep 0.0005 / 60F

 

안으로 파고드는 부분이 아쉽다면, curvature 값의 음수에 해당하는 값을 삭제해줄 수 있을 것이다.

Use World Space Radius Units On / Filter Radius 0.05 / Iterations 2 / Timestep 0.0005 / 60F

 

Vector Field에 위로 올라가는 힘(ex - wind)을 추가해보자.

둥실둥실 떠오르고 있다.

gradient와 curvature의 곱에 대한 결과에 더해준 vector 값 {0, 10, 0}은 크게 느껴질 수도 있다. 하지만, 현재 Advect의 Timestep이 0.0005이기 때문에, 그렇게 크게 작용하지는 않는다.

 

여기에 노이즈를 추가해보자.


정리

  • 직접 tweak 해보면서 체크해볼 사항
    • solver 세팅을 잘 잡을 수 있는가
      • 기준을 가지고 있는가
    • 효과의 속도를 조절할 수 있는가
      • timestep의 값을 이해했는가
    • Vector Field를 다룸에 있어서 겁먹지 않고 정보들을 쓸 수 있는가

Fog volume과 solver를 사용해보자.

SDF를 사용할때와의 다른 점은 다뤄줘야할 정보가 더 많다는 것이다.

Fog volume을 사용할 때 신경써야할 부분이 좀 더 많다.

 

일단 solver 밖에서 한사이클이 어떻게 돌아갈지 계획을 잘 세워주는 것이 좋다.

볼륨작업을 할 예정이기 때문에 지오메트리 때와 같이 어트리뷰트를 달고 다니는 느낌으로 접근하면 꼬일 수 있다.

명확한 이름 구분으로 더할 것은 더하고, 버릴 것은 버려야한다.

 

우리가 만들 것은 이전 프레임의 fog 볼륨에 현재 프레임의 fog 볼륨이 더해지는 것이다.

이전 프레임의 fog 볼륨은 밀도가 낮아진 상태로 더해질 준비가 되어야하며, 새로 더해지는 fog 볼륨은 밀도의 감소 없이 멀쩡한 상태로 더해져야한다.

 

time shift 노드를 활용한다.

Time Shift Node

어느 순간의 정보를 보고싶은지 사용자가 직접 정해줄 수 있는 노드

Time Shift parameter

Frame : 어느 순간의 정보를 볼지에 대해 프레임 넘버를 적어준다.

식도 대입이 가능하기 때문에 $F - 1 / $F - 2 등으로 기입이 가능하다.

이 부분만 solver에 들어가면 된다.


이번에는 container를 만들어주고, prev_frame의 볼륨 이름을 temp로 만들어서, container에 temp를 더하고, 그 결과로 나온 container에 fog 볼륨을 더해보자.

prev_frame의 볼륨 바운딩 박스와 first_input의 볼륨 바운딩 박스를 모두 포함할 수 있도록 merge로 묶은 것에 대해 다시 bound 노드를 활용해서 바운딩박스를 만들어주고, 그것을 container로 활용하였다.

그리고 container 볼륨에 temp 볼륨이 더해지고, 마찬가지로 그 결과에 fog 볼륨이 더해졌다.

이때까지의 결과로 나오는 볼륨의 이름은 container 이고, 현재 prev_frame으로 들어가는 볼륨의 이름은 fog 이기 때문에 name 노드를 활용해서 container 볼륨의 이름을 fog로 바꿔줬다.

 

solver 내부

시작과 끝이 일치해야한다는 뉘앙스만 잘 가지고 있어도 해볼만한 예제이다.


source가 바뀌어도 잘 작동하는지 확인해보자.

 

crag를 fog로 바꿔서 source로 넣어보자.

 

volume visualization까지 적용한 모습

 


solver 안에 Advect 세팅을 추가해보자.

vector field를 생성해줘서 약간의 바람같은 역할을 하도록 할 것이다.

 

prev_frame으로 들어오고 있는 기존의 fog 볼륨(temp)이 VDB Advect에 의해 이류가 될 수 있을 것이다.

vector field가 위를 향하는 것이라면, Advect의 결과로 수증기가 증발하는듯한 느낌을 낼 수도 있다.

 

vector field(wind)를 만들기 위해서 바운딩박스를 이용한다.

기존의 바운딩박스는 조금 타이트한 면이 없지않아 있기 때문에, 약간 더 크게 만들어주도록 한다.

Bound parameter

Lower / Upper Padding : Bounding Box의 크기를 각각 x, y, z 크기만큼 늘린다.

 

wind 라는 이름의 vector field를 만들고, prev_frame으로 들어오는 temp volume이 이류시켰다.

 

위를 향하는 효과를 키우려 한다면, solver 내의 volume vop의 constant의 값을 올리는 방법과 VDB Advect의 timestep의 값을 키워주는 방법이 있다.


crag로도 동일 내용 적용해보자.

 


이번에는 vector field에 변화를 줘보자

(wind 볼륨이 바뀐다는 이야기이다.)

 

wind 볼륨에 노이즈를 추가해줄 예정이다.

 

 

roughness를 0.75로 올린 결과

 

voxel size를 0.03으로 적용해서 해상도를 높인 결과

노이즈의 frequency와 roughness를 조절하면서 비교해보자.

frequency 1,1,1 / roughness 0.5 / 37F
frequency 2,2,2 / roughness 0.5 / 37F
frequency 2,2,2 / roughness 0.8 / 37F

 


노이즈의 패턴이 단조로운 관계로, 시간에 따라 노이즈의 패턴이 바뀌도록 적용해보자.

AA flow Noise를 사용한다.

AA flow noise를 사용할 경우, noise의 패턴이 그때그때마다 바뀌게 되어서 좀 더 다채로운 모양의 결과가 나오게 된다.

 

짧은 팁!
나중에 불을 묘사하는 것이 폭발을 묘사하는 것보다 어려운 순간들이 있다. 생각보다 불의 일렁임은 정말 빠르다.
이런 빠르게 움직이는 묘사가 없으면, 불보다는 연기처럼 보일 확률이 높다. 이 부분에 대한 뉘앙스는 꼭 알아두고 가자. 

crag의 망치에 불을 질러보자.

VDB의 바운딩 박스를 보고싶지 않다면, volume visualization 노드 앞쪽에 convert VDB를 사용해서 일반 볼륨으로 바꿔주면 된다.

불이 나오는 망치 소스가 너무 일률적으로 이쁜 모양이라 polygon을 remesh 적용 후 attribute noise로 모양을 구기고 애니메이션을 추가해줬다.


불이 길어지는 잔상의 조절을 위해 VDB combine의 multplier 값을 조절하는 것은 적용은 해보았으나 컴퓨터가 뻗는 관계로 캡쳐는 진행하지 못했다... 아하하

 

컴퓨터를 빨리 바꾸라는 뜻인가보다 :)

 

열심히 일해야겠네 아주...

 

어제 오늘 복습도 겸하면서 작업해본 volume과 solver의 활용, 점점 무언가를 만들어볼 생각에 두근두근해지는 후디니이다.

 

solver에 VDB 적용해보는 것을 강의 듣기 전에 해본 것은 임의의 container를 사이즈를 주고 거기에 combine으로 더해줘서 표현해본 것이었는데, container 크기를 초기에 크게 잡아놓고 하는 것인지라, 생각해보면 데이터 낭비가 발생할 수도 있는 부분이지 않았을까... 싶은데, 바운딩 박스로 volume의 크기만큼 container를 만들어주는 것을 보니까 아.. 이런 방법도 있구나 하는 것을 다시 한번 생각해본다.

 

열심히 하자 후디니!

건강하게 하자 후디니!