TWA Houdini1/Volumes

TWA 후디니 1 Volume_09 : 충돌 세팅 2가지, VDB from Particles

yiss09 2023. 3. 1. 06:17

https://www.twahoudini.com/course/volumes1

 

HOUDINI1_ VOLUMES

2️⃣ 볼륨의 기초이론과 응용 그리고 SIMULATION까지 경험해보세요.

www.twahoudini.com

 

Volume09 강의에서는 Volume에서의 충돌 세팅과, VDB from Particles를 통해 물체의 표면에서만 발생하는 Smoke를 배우게 된다.

 

1. 충돌, 중력에 관한 내용

2. 다른 방식의 Source Setting

3. VDB from Particles

4. 예제

 


1. 충돌, 중력에 관한 내용

 

먼저 Smoke를 다루기 위해 필요한 Source의 기본 셋팅을 잡아준다.

Polygons 타입의 Sphere를 생성해 Fog VDB로 전환해준다. Name node를 이용해 density 정보의 이름을 변경해 temperature와 vel의 정보를 만들어준다. 이때 vel의 정보는 vel.x, vel.y, vel.z로 따로 만들어졌다가 VDB Vector from Scalar를 통해 하나의 vector 정보로 뭉쳐준다.

Dop Network를 생성해 Smoke Solver, Smoke Object, Volume Source, Gas Resize Fluid Dynamic을 연결해주었다.

vel의 정보에 Volume Vop을 달아 정보를 통제할 수 있도록 해주었다. Volume Vop 내부에서 constant와 Bind Export를 연결해 vel이 {0,0,0}의 위치정보를 가지도록 해주었다.

temperature에 Volume Vop을 달아 Noise를 주었다. 이때 -1~1 사이의 범위를 다루는 Anti-Aliased Flow Noise를 사용하였다. 그 결과 생성되는 Smoke가 양수와 음수 방향으로 동시에 발생하며 나아가는 것을 볼 수 있다.

 

이를 해결하기 위해  Anti-Aliased Flow Noise에 Clamp를 달아 그 값을 통제해주어도 되지만, 이번엔 0~1 사이의 값을 다루는 Turbulent Noise를 사용해준다.

Multiply를 달아 Turbulent Noise의 Amplitude를 조절할 수 있도록 해주었다.

 

연기가 지속적으로 살아있는 것이 아쉽다. Smoke Solver의 Input3(Velocity Update)에 Merge와 함께 Gas Dissipate를 연결해주겠다.

density에 대해 이용해줄 예정이며, Diffusion은 blur에 대한 내용이니 사용하지 않겠다. 만약 Smoke의 blur에 대한 내용을 더 자세히 다루고 싶다면 Gas Blur node를 이용해주도록 하자.

Evaporation rate의 값을 높게 잡아주어 연기가 금방 사라지도록 해주었다.

그리고 Gas Turbulence를 달아 Velocity에 Noise를 준다. 적당히 Scale값만을 건드려준다.

 

이제 충돌과 중력에 관한 내용을 다루어보겠다.

이 말은 기억하는가? "충돌의 조건은 앞으로 Volume이 될 것이다." Particle과 SDF에서 중요하게 언급되었다. Volume의 충돌도 마찬가지이다. SDF에 의한 충돌 조건을 만들어줄 수 있다.

만약에 Smoke 위로 Box가 존재한다면, Smoke가 위로 올라가다가 충돌해서 Box 표면에 쓸려 올라가게 될 것이다.

Smoke 위로 Torus가 존재한다면, 어떠한 연기들은 Torus의 중간 구멍으로 빠져나가고, 어떠한 연기는 표면 옆면을 쓸어가면 올라갈 것이다.

그러면 Torus가 Smoke를 지나간다면 어떻게 될까? 아마 연기가 중간에 한번 끊기는 구간이 생길 것이다. 그림으로는 표현의 한계가 있으니, Houdini 안에서 한번 이러한 현상을 다루어보겠다.

 

Dop Import Fields를 통해 Geometry 단계에서도 볼 수 있게 해주었다. 색을 입힌 Light를 생성함으로써 Smoke에 입체감을 더해주었다.

이제 충돌의 정보가 될 Object를 생성해주겠다.

Polygons 타입의 Box를 생성해 Size와 Position, Rotation을 지정해준다. Null node를 달아 해당 Object 정보를 col_obj로 지정해준다.

Tranform node에 VDB from Polygons를 달아 SDF VDB를 생성해준다. SDF 정보를 col_sdf로 지정해주었다.

Scene view를 보면 아직 Dop Network 안에서 아무런 작업도 해주지 않았기 때문에 Smoke가 Box를 지나치는 것을 확인할 수 있다.

 

Dop Network 안에서 Static Object node를 이용하여 충돌을 설정해주었다.

SOP Path에는 col_object를 불러들이고, Proxy Volume에는 col_sdf의 정보를 불러들였다.

이때 Mode를 Volume Sample로 변경해주어야 Proxy Volume으로부터 불러들인 SDF의 정보를 이용할 수 있다.

Geometry 단계의 SDF로부터 정보를 가져오기 때문에 Guide의 해상도를 높여주려면, VDB from Polygons의 voxel size를 조절하면 된다.

 

이제 Rotate 값에 $FF를 넣어 회전하는 Torus를 Collision 정보로서 이용해보겠다. 

만약 Torus의 정보를 연결해주었는데도 불구하고 Dop Network 안에서 Torus가 회전하지 않는다면, Use Deforming Geometry를 체크해주어야 Static Object가 움직인다.

Torus가 회전하면서 Smoke를 통과하고 있다. Volume이 col_sdf에 의해 영향을 받는 것을 볼 수 있다.

 

Gravity를 달아 Smoke가 중력을 받도록 해주었다.

이때 Gravity에 의해 Smoke가 한계없이 아래로 떨어지는 것을 막기 위해서 Groundplane을 달아주었다.

Gravity에 의해 바닥에 고인 Smoke를 확인할 수 있다.

 

Volume Visualization에서 Density Scale과 Shadow Scale을 높여 Smoke가 좀 더 입체적으로 보이게 해주었다.

해당 결과에서 아쉬운 부분을 찾아보자면, Torus로 만들어둔 충돌 조건에 의해 Smoke에 구멍이 뻥 뚫리게 된 것이다.

 

 


2. 다른 방식의 Source Setting

 

이제 Volume 충돌에 대한 다른 세팅을 배워볼 예정이다.

지금까지의 우리가 Smoke Solver에서 이용했던 정보는 대부분 Fog 정보였다. Name node로 Fog 정보를 변환해서 다른  정보로 만들어주었었다.

Volume에서 Collision에 관해 공부함으로써 처음으로 SDF가 의미있어졌다.

Volume의 충돌을 묘사하기위해 Static Object의  Proxy Volume에 SDF가 쓰였다. 이제 이와 같은 내용을 Static Object node 없이 Volume Source만을 이용하여 표현해주려한다.

앞으로의 작업을 하기 위해선 Static Object를 썼을 때 Volume에 어떠한 변화가 생겼었는지 볼 필요가 있다.

 

Volume에서의 Velocity를 주목해보자.

Velocity Field가 움직이는 것을 보면, 단순히 Object에 의해 Volume이 충돌하게되는 뉘앙스와는 다르다. 

Volume이 움직이는 이유는 Velocity Advect이다. Static Object가 움직이면서 Velocity Field를 제공하고 있기에 Smoke가 움직일 수 있게 된다.

Scene view에서 빨강색으로 표현된 부분은 Static Object가 발생시키고 있는 Velocity Field이다.

Gravity와 Statict Object에 의해 만들어진 Velocity에 의해 Smoke Solver가 영향을 받고 있다.

 

우리가 Static Object를 활용했을 때에는 SDF를 직접 제공해줬다. 그리고 Static Object가 자동으로 Velocity를 만들어주었다. 이때 만들어준 Velocity가 Smoke Solver에 적용이 되는 것이다.

Static Object를 만들지 않고 Volume Source로서 동일한 역할을 수행해주기 위해서는, SDF와 Velocity 두 가지 모두 직접 제공해주어야 한다. Static Object에서는 Velocity가 자동으로 구해졌지만, 이 방법에서는 직접 Velocity를 제공해주어야 한다.

이때 SDF는 col이라고 이름을 붙여줄 것이고, Velocity는 colvel의 이름을 붙여준 Vector Volume을 만들어줄 예정이다.

이러한 과정을 VDB from Polygons의 Surface Attributes에서 해주겠다.

이론 설명을 위해 간단한 구성으로 진행하겠다.

 

이제 col의 정보를 만들어주었기 때문에, colvel이라는 vector Velocity 정보를 만들어주려한다.

Torus가 회전하는 움직임의 Velocity 정보를 얻기 위해서 Trail node를 활용해준다.

Result Type을 Compute Velocity로 해줌으로써 움직임의 속도를 구해줄 수 있다.

이때 만들어진 v라는 정보를 VDB from Polygons에서 이용할 예정이다.

 

Surface Attributes에서 +를 해준다는 것은 VDB from Polygons 위로 들어오고 있는 정보 중 어떠한 정보를 Volume으로써 만들어주겠다는 것을 뜻한다.

Attributes에서 point.P와 point.V 중 하나를 선택할 수 있다. Node info를 보면 이때 선택한 정보가 vector Volume으로 저장된 것을 알 수 있다.

이때 생성된 정보의 이름을 VDB Name에서 변경해줄 수 있다.

 

Volume Trail을 이용해 colvel의 정보를 시각화해보겠다.

Torus가 움직이는 방향에 따라서 Velocity에 대한 Volume 정보가 나온 것을 볼 수 있다.

Volume Slice에서 Offset 값을 변경해 Torus의 다른 부분에서의 colvel의 정보도 확인할 수 있다.

 

이제 해당 내용을 가지고 다시 작업을 시작해보겠다.

우리는 Torus가 Smoke에 충돌하는 것처럼 묘사하고 싶다. 그런데 Static Object로서 충돌조건을 사용하는게 아닌, Volume Source의 어떤 일종으로써 충돌조건을 제공하고 싶다. 이를 위해 SDF 정보와 Velocity의 정보가 필요하다.

Trail과 VDB from Polygons를 활용해 col과 colvel의 정보를 만들어주었다.

Dop Network 안으로 들어가 Volume Source를 하나 더 만들어 Collision에 대한 정보를 만들어주었다.

이때 새롭게 만든 Volume Source의 SOP Path를 col_source로 연결해준다.

결과를 보면 Static Object와 비슷한 것을 알 수 있다.
하지만 이때 Smoke에 생기는 구멍에 불필요한 부분이 생긴 것을 볼 수 있다. 이 부분은 VDB from Polygons에서 만든 SDF 정보의 내부가 뚫려있기 때문에 발생하는 현상이기에 VDB from Polygons의 Fill Interior를 눌러 해결해준다.

 

Static Object에 비해 까다롭지만 해당 방법에는 큰 장점이 있다. 직접 정보를 만들어주기 때문에 colvel의 정보를 수정해 Velocity에 변형을 줄 수 있다는 것이다.

 

각각 colvel에 multiply 0, 1, 3 해주었다.

3배에선 힘이 세게 작동하는 것을 볼 수 있다. Collision으로서 Volume Source로 충돌을 낼 때에는 연기의 움직임을 쉽게 바꿀 수 있게 된다. 그리고 Torus가 충돌의  조건이 되는 것도 쉽게 이해된다.

이와 같은 동일한 작업을 Static Object로 표현해내는 데에는 한계가 있다는 것을 알고 있자.

 

Volume Source에 대한 추가적인 설명이다.

오늘 이용한 Collision은 그 방식이 매우 독특하다. Operation이 Maximum으로 선택되어 있는 것, Source Scale이 -1인 부분에서이다.

collisionvel에서 Source Scale이 1.5로 되어 있는 것은 주어진 colvel의 정보를 1.5배만큼 세게 쓰겠다는 것을 뜻한다.

SDF로부터 들어오는 col의 정보에 -1이 곱해진다는 것은 안팎을 뒤집는다는 것을 뜻한다. 이때 안쪽의 정보는 1이 되게 된다.

만약 collision의 Source Scale이 1이 된다면 SDF의 정보가 아닌, Fog 정보가 쓰일 수도 있다.

Operation의 Copy는 주어진 정보로 대체한다는 느낌이 있다. VDB Combine의 Copy를 생각하면 이해에 도움이 될 것이다. Pump에 대한 내용은 이후 색깔 Volume을 다룰 때 한번 더 나올 예정이다.

 

 


3. VDB from Particles

 

이제 표면에서만 Smoke가 나오도록 만들어볼 예정이다.

이를 위해선 지금까지의 VDB from Polygons를 활용한 방법으로는 한계가 있다.

표면에서만 Smoke가 나오게 만들기 위해서 지금부터 할 두 가지 방법은 모두 Point를 이용한 방법이다.

하나는 VDB from Particles를 활용한 방법이고, 다른 하나는 Pyro Source와 Volume Rasterize Attributes를 활용한 방법이다. 먼저 VDB from Particles를 활용해보겠다.

 

아무런 기준도 없이 Volume을 그냥 생성해내는 건 쉽지 않다. 특히 모양을 구체적으로 가지는 경우는 더 그러하다. 그래서 Polygon이라던지 Point라던지 어떤 기준을 가지고 Volume을 만들게 된다.

 

VDB from Polygons이 작동하는 순서는 이러하다.

주어진 Geometry에 Polygon의 Bounding Box가 존재한다. 먼저 Bounding Box를 voxel size로 쪼개준다. 이때 쪼개진 각각의 Box를 Voxel이라고 부른다. 이제 표면을 기준으로 가장 가까운 Voxel을 찾고 그 거리값을 저장해준다. 거리값이기 때문에 모두 양수의 값을 가지는데, 이것을 Distance Field라고 부른다. 이를 안과 밖을 구분해서 Signed Distance Field라고 한다.  VDB를 이용하고 있기 때문에 정보의 효율을 따지게 된다. 표면을 기준으로 안쪽과 바깥쪽으로 몇 칸이나 정보를 구할지를 Interior Band Voxels과 Exterior Band Voxels에서 정해주게 된다.

SDF 정보가 아닌 밀도의 정보는 SDF 정보에 -1을 곱해주게 되면, 양수값은 음수값이 음수값이었던 안에 있던 부분이 양수값으로 변하게 된다. 이때 음수값인 부분은 모두 날려버려준다. 이렇게 밀도 정보가 생성된다.

 

그렇다면 VDB from Particles는 어떻게 작동할까? VDB from Polygons와 거의 똑같은 방식으로 작동하는데, 앞에 추가적인 사전내용이 붙게된다.

 

VDB from Particles를 사용하기 전에 VDB from Polygons로 실험 하나를 해보겠다.

Point를 Sphere로 치환해준 뒤, 해당 Point을 기준으로 Volume 정보를 만들어준다. 해당 시스템이 VDB from Particles에서의 과정과 같다. 그리고 해당 세팅에서 문제가 발생한다면 똑같이 VDB from Particles에서도 문제가 발생하게 된다.

Point에 Attributes Wrangle을 달아 @pscale 값을 조절해준다.

이때 @pscale을 줄임에 따라 Bounding Box의 변화를 관찰할 필요가 있다. @pscale을 줄이면 Volume의 사이즈가 줄어드는 것 같이 보이다가 어느 순간 Bounding Box가 크게 줄어들어 거의 사라진 것처럼 보이게 된다. 반대로 @pscale을 키우게 되면 안쪽까지 꽉찬듯한 느낌의 Volume이 생성된다.

 

voxel의 중심에서부터 표면까지를 연결해준다면, 해당 voxel의 값이 음수인지 양수인지를 확인 가능하다.

왼쪽 그림과 같이 Volume의 사이즈가 클 때에는 문제가 없다. 하지만 만약 voxel size보다 Volume의 size가 작다면 어떻게 될까?

Volume의 사이즈가 작아짐에 따라 음수가 되는 voxel의 갯수가 줄어들게 된다.

 

Density 정보를 구하기위해 SDF로부터 Fog 정보를 구하게 된다면, SDF 값이 음수였던 부분은 살아남게되고, 양수였던 부분은 사라지게 될 것이다.

사라지는 voxel들을 최대한 살리는 방법이 있다. 바로 voxel size를 줄여주는 것이다.

voxel size를 반으로 줄여준다면, 살아남는 voxel의 수가 달라지게 된다.

voxel size가 작아지면 작아질수록 작은 내용에 대해서도 음수의 SDF를 구할 수 있고, VDB Polygons를 이용했을 때 밀도로서 Fog 정보를 구할 수 있다.

Point에 대한 밀도를 구하기 위해 두 가지 변수를 잘 다루어야 한다. 

하나는 Point가 Sphere로 대변될 size로 이를 point size라고 축약해서 말하겠다. 다른 하는 voxel size이다. 이때 point size가 voxel size보다 작을 때, 그 정보가 제대로 안 구해지고 사라질 수 있다는 점을 알아야한다. 

 

이제 Sphere의 표면을 밀도로 만들어볼 예정이다. SDF로 표면이 어딨는지 구하는 것이 아니라 표면을 밀도로 만드는 것이다.

scatter 1000, point size 0.05의 값을 주었다. 

오른쪽 사진은 voxel size 0.03에 VDB from Polygons에 Display Flag를 해주었다.

해당 결과는 Sphere의 표면에 대한 밀도를 구하는 시도를 하고 있는 것이다. 중간중간 뚫려있는 부분이 존재하는 것을 보아 완벽한 결과가 아닌 것을 알 수 있다.

이를 해결하기 위해 Scatter로 점의 갯수를 늘려주거나, 각각의 point가 대면되는 Sphere의 사이즈를 키워줄 수도 있다. 또한 voxel size를 낮추는 방법도 존재한다. 

 

Scatter로 point의 갯수를 2000개로 늘려주었다. Sphere의 표면에 해당하는 밀도를 구할 수 있다. 속 안은 아직 비어 있는 것을 보아 정보가 없는 것을 알 수 있다. 

Scatter로 point의 갯수를 5000개로 늘려주었더니 안쪽까지 밀도가 차는 것을 볼 수 있다. 이것은 VDB from Polygons로 이 Sphere의 밀도 자체를 구한 것과 다름이 없다고 볼 수 있다. 

point의 갯수에 의해 속이 꽉 차고 안차는데에는 그 이유가 있다. point가 2000개일 때는 표면에 빈 공간이 있다. 이 뚫려있는 부분에 의해 안팎의 SDF가 모두 구해지게 된다.

하지만 point가 5000개가 되면 표면에 빈 공간이 모두 막히게 된다. 표면이 모두 막히게 됨으로써 안쪽에 있는 부분이 모두 내부라고 인식되어 모두 밀도처럼 표현되는 것이다.

Scatter에서 point의 갯수를 조절하는 것 말고도, point size를 키워줌으로써 표면의 빈 공간을 모두 메워 똑같이 안쪽이 꽉 차게 만들 수 있다. point size가 커지면 커질수록 표면의 두께가 두꺼워지게 된다.

 

우리는 표면이 얇은 결과를 원하고 있다. 그렇기 때문에 point size를 줄이고 Scatter를 늘려주는 방법을 택하겠다.

point size를 계속하여 줄여주게 되면, 어느순간 voxel size보다 작아지는 순간이 온다. 이때 조심해야 될 필요가 있다.

계속하여 point size를 줄이고 Scatter를 늘려주다보면, 예쁜 모양은 아니지만 표면에 대한 정보를 구하고 있는 얇은 Volume의 막을 얻을 수 있다.

 

이제 VDB from Particles를 사용해보겠다. 필요한 내용은 Scatter로 만들어진 point 정보면 된다. 

VDB from Particles로 Fog Volume을 얻었다. 그런데 Scatter에서의 Sphere의 크기보다 VDB from Particles에서의 Volume의 크기가 훨씬 큰 것을 볼 수 있다. 이는 Point Radius Scale 때문인데, 이 내용은 앞서 사용했던 @pscale과 같다. 즉, 치환해줄 때point를 얼마만큼의 사이즈로 쓸 것이냐는 내용이다.

 

Point Radius Scale의 값을 계속 낮춰주다보면, Sphere의  표면에 해당되는 밀도가 보이기 시작한다.

만약 Point Radius Scale의 값이 계속해서 낮아진다면, 어느순간 밀도를 구하지 못하는 순간이 오게 될 것이다. 해상도보다 point size가 작아지는 순간이 오면서 값을 구하지 못하는 경우가 생기는 것이다.

이때  Point Radius Scale의 값이 0.15에 도달하게 되면 값이 사라지는 경우가 발생한다. 하지만 아주 작은 숫자라도 0.15 뒤에 그 값이 더해지면 다시 밀도값이 생성되는 것을 볼 수 있다.

이러한 문제는 voxel size, Point Radius Scale, Minimum Radius in Voxels이 세가지 파라미터의 관계에서 해결해볼 수 있다. 파라미터에 적힌 숫자들을 자세히보면 voxel size와 Minimum Radius in Voxels의 곱이 Point Radius Scale의 값과 유사한 것을 볼 수 있다.

Point Radius Scale > Voxel Size * Minimum Radius in Voxels 의 조건이 달성되어야 밀도 값이 사라지지 않고 구해지게 된다.

이와 같은 조건을 항상 맞추기가 까다롭기 때문에 Point Radius Scale의 파라미터에 (Voxel Size * Minimum Radius in Voxels) + 0.000001 과 같은 값을 넣어둔다면, Point Radius Scale을 관리하기 편해진다.

 

Sphere가 아닌 다른 Object에서도 응용이 가능하다. 

 

 


4. 예제

 

먼저 여태해온것처럼 기본 세팅을 마쳐준다.

Gas Turbulent를 달아주었다.

 

위치정보에 변화를 줄 것이기 때문에 Vop Global을 사용해주겠다. Turbulent Noise는 기본적으로 0보다 큰 값을 가진다. Turbulent Noise 값은 기본적으로 작기 때문에 기존에 들어오고 있는 temperature의 값을 낮춰주고 Turbulent Noise에 Multiply하여 Amplitude 값을 키워주겠다.

결과를 보니 Sphere형태의 Volume으로부터 연기가 나오는 것을 확인할 수 있었다.

이제 Collision의 정보를 생성해 표면에서만 Smoke가 나오도록 해보겠다.

 

Geometry 단계에서 SDF Volume의 col의 정보를, Trail의 정보로 colvel을 생성해주었다.

Volume Source에 collision과 collisionvel의 정보를 추가해주었다.

 

Density를 수정하기위해 Volume Vop에서 multiply를 통해 좀 더 밀도가 진하게 만들어주었다.

 

표면에서만 Smoke가 형성되는 것을 관찰할 수 있다.

Volume Visualization으로 색을 입혀 불꽃같은 느낌을 주었다.

 

새롭게 회전하는 Source를 만들어주었다.

Trail node를 통해 얻을 Velocity Field가 point로부터 정보를 얻기 때문에 많은 정보를 얻기위해 remesh해주었다.

 

Volume Source에서의 colvel의 Source Scale 값을 각각 0, 1, 3을 준 모습이다.

colvel의 세기가 강해질수록 Smoke가 좀 더 Velocity의 흐름에 따라 넓게 퍼지는 모습을 보여주었다.

 

Volume09에서의 과제이다. 지금까지 배워온 내용으로 만들어보았다. 디테일한 Tweak 과정은 아직 많이 어렵게 느껴진다. 더 많은 시도를 해봐야할 필요성을 느꼈다.