Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags more
Archives
Today
Total
관리 메뉴

테드옹의 VFX

Fog Volume의 표면만 필터링하기 본문

Houdini

Fog Volume의 표면만 필터링하기

Tedd_Kim 2024. 11. 7. 00:04

포스팅에 앞서 

 

volumesample : 포지션 벡터를 기반으로 복셀의 값을 샘플링

volumeindex : 인덱스를 기반으로 복셀의 값을 샘플링

volumepostoindex : 포지션을 기반으로 인덱스 값을 구함

volumeindextopos : 인덱스값을 기반으로 포지션을 구함

 

저 함수들을 알고 있으면 대충 간단한 수준에서 필요한 볼륨작업은 문제가 없을 것 같다

 

사이드 프로젝트를 진행하면서 Cloudscape을 만들고 구름 위에서 포인트를 Emssion하는 방식을 알아봤어야했는데

일단 왜 여기까지 왔는지 기록을 해보자면

 

[방법 01. Scatter 노드]

제일 먼저 생각난 그냥 density볼륨에 scatter노드를 다는 것이 있었다.

하지만 나는 볼륨의 표면에다가 emission을 하고싶었는데 그냥 scatter를 쓰면 가장자리가 아닌 깊숙히 위치한 density도 포인트를 생성하기 때문에 대량의 포인트를 뿌리는데 어려움이 있었다.

 

[방법 02. Fog to SDF]

혹은 Fog 볼륨을 SDF로 변환하고 거기에 scatter노드를 쓰는 방법이 있었는데, 이게 정말 fog 볼륨의 표면에 딱붙어서 나오는 Emission을 원했기 때문에 정밀하게 가장자리부분을 따주지 않는 이 방식은 매력적인 옵션이 아니었다.

 

[방법 03. Volume Gradient]

Gradient는 볼륨 데이터의 변화량으로, 가장 큰 변화량이 있는 곳 (즉, 값이 갑자기 0이 되어버리는 가장자리 복셀)을 골라내기에 특화된 작업이다. 하지만 사용해보니 생각보다 정교한 필터링을 해주지 않아서 버렸다

 

[방법 04. Neighbor Voxel Search]

그렇게 해서 접근한게 바로 인근 voxel을 서치하여, 서치 대상인 복셀 바로 옆에 붙어있는 어떤 복셀이든 density 0의 값을 가지고 있으면 가장자리일 수밖에 없으므로 그 방식을 사용해보기로 했다. (사실 항상 쓰고싶었던 방법이었음). 이러면 제일 정확하게 내가 가장자리 복셀인지 아닌지를 판단할 수 있게된다.

 

 

대충 그림으로 설명하자면 저런 식으로 정사각형 3D voxel의 인근 복셀을 서치하여 (3차원인 경우 27개, 2차원인 경우 9개) 하나라도 density = 0의 값을 가지고 있으면 그것이 가장자리 복셀이니까 본인의 density를 0으로 만들고, 대신 서치된 density = 0의 복셀에 임의의 값을 줘서 가장자리를 새로운 데이터로 채워넣는 것이다.

 

이를 하기위해선 for문을 3번 중첩시켜서 반복문을 사용해야하는데, 복셀 수에 구애받지 않고 항상 최대 27회만 실행되는, 즉 시간 복잡도가 선형시간인 알고리즘이기 때문에 코드의 퍼포먼스 또한 좋다고 생각할 수 있다.  

 

하지만 보통 작업에 사용하는 VDB 볼륨은 메모리 효율을 최대화하기 위해서 0의 값은 복셀을 할당하지 않으므로, 코드를 작성하기에 앞서 vdb activate노드를 사용해 복셀의 영역을 Expand 시켜주는 것이 좋다 (추후에 이것을 이용해서 포인트를 얼마나 두껍게 생성할지도 컨트롤 할 수 있음)

 

VDB Activate노드를 사용해 복셀을 2개만큼 Expand 해준 모습

 

그리고 다음과 같이 코드를 작성하여주면 가장자리만 정확하게 따낼 수 있다

 

vector index = set(@ix, @iy, @iz);
int ix = int(index.x);
int iy = int(index.y);
int iz = int(index.z);

for(int z=-1; z<=1; z++) {
    for(int y=-1; y<=1; y++) {
        for(int x=-1; x<=1; x++) {
            vector current_idx = set(ix+x, iy+y, iz+z);
            if(current_idx != index) {
                float neighbor_density = volumeindex(0, 'density', current_idx);
                                
                if(neighbor_density != 0) {
                    @density *= 0;
                } else if(@density == 0) {
                    @density = 0.1;
                    break;
                }
            }
        }
    }
}

 

만약 1복셀 근처의 복셀의 density가 0이 아니라면 나의 density 값을 0으로 만들고 (필요 없으니까)

그렇지 않다면 0.1의 값을 임시로 준 후 반복문을 빠져나간다.

(참고 : 복셀 index는 3차원 벡터이다)

 

이러면 최악의 경우에도 27번의 연산이 돌아가지만 보통 아주 운이 나빠도 9번정도 루프를 돌린 후에 break문에 의해 종료가 된다.

 

가장자리 부분만 따낸 모습

 

이렇게 하고 addpoint함수를 이용해주면 볼륨의 가장자리에 성공적으로 포인트를 생성 할 수 있다

 

가장자리에 포인트를 생성한 모습

 

뭐 이거 쓸 일이 얼마 있겠냐만, 주변 복셀을 서치하는 알고리즘 자체는 굉장히 유용할 수도 있으므로 포스팅 해봤다

 

 

 

글의 서두에 써둔 함수를 이용해서 Point Wrangle로도 비슷한 작업을 할 수가 있다

 

vector index = volumepostoindex(1, 'density', @P);
int ix = int(index.x);
int iy = int(index.y);
int iz = int(index.z);

@density = volumeindex(1, 'density', index);
@pscale = primintrinsic(1, 'voxelsize', 0);

i@__iter = 0;

for(int z=-1; z<=1; z++) {
    for(int y=-1; y<=1; y++) {
        for(int x=-1; x<=1; x++) {
            vector current_idx = set(ix+x, iy+y, iz+z);
            if(current_idx != index) {
                float density = volumeindex(1, 'density', current_idx);
                
                if(density == 0) {
                    @Cd = {1,0,0};
                    @__iter++;
                    break;
                }
            }
        }
    }
}

 

'Houdini' 카테고리의 다른 글

Python & Vex in Solaris  (1) 2025.01.05
Volume Shader Demystifying  (0) 2024.11.08
Solaris + Copernicus에서 표면에 디테일 주기  (1) 2024.10.05
Non Commercial 힙파일 변환  (1) 2024.09.25
USD Solaris 개념 정리  (1) 2024.06.08