top of page
  • 작성자 사진Nobody

(UnityShader) 3D Procedural Noise

최종 수정일: 2022년 6월 12일


(UnityShader) 11 Fragment Advanced : 3D Procedural Noise / Fractal Noise



지난 포스트(링크)에서는 2차원 노이즈를 제작하였고, 결과를 선형 보간 했습니다. 오늘은 가장 유용한 보간 공식 중 하나인 Smoothstep 부터 알려드리겠습니다.




Smoothstep 으로 보간하기


smoothstep(min,max,n) : n이 [min, max] 사이의 값인 경우에 대해서 [0, 1] 사이에서 부드럽게 변하는 Hermite 보간법을 리턴합니다. n 값이 min보다 작다면 0을 리턴하고, max보다 크다면 1을 리턴합니다.



스무스 스텝 함수는 보간 방식에서 가장 자주 사용되고 매우 유용한 함수 중 하나입니다. 위의 사전적 정의가 알아듣기에 너무 딱딱한 감이 있습니다. 저희의 직관적인 이해를 돕기 위하여 실제 사용 예시를 보도록 합시다.



smoothstep 함수로 uv.x 그라데이션을 보간하면 min, max 값에 따라 위와 같이 동작합니다. 위 그래프의 value 값을 색으로 볼 수 있게 만들었습니다. 이것을 눈으로 디버깅 한다고 표현합니다.


그라데이션의 부드러운 정도, 다시 말해서 그래프로 해석하면 기울기를 조절할 수 있기 때문에 정렬 되어있는 리니어(Linear) 데이터라면 뭐든지 보간이 가능합니다.


예를들어 카툰쉐이더를 작성할 때 램프 텍스처의 부드러운 정도를 조절 가능한 기능을 포함 할 경우 매우 유용하게 사용됩니다. 이 부분은 따로 다른 포스트로 다루도록 하겠습니다.



이렇게 부드럽게 보간한 UV 데이터를 가지고 선형 보간 인자로 재사용 할 수 있습니다.


(좌측 : 보간 이후 / 우측 : 보간 이전)

float2 uv = smoothstep(_min, _max, frac(Num * _NoiseSize));


보간 이전 데이터는 날카로운 느낌이 있지만 보간 이후 데이터는 부드러움에서 차이가 납니다.




3차원 노이즈 만들기


(3-dimension noise)


이번 포스트의 본론입니다. 3차원 노이즈 데이터는 불규칙적인 값이 필요한 3d 오브젝트를 위해 사용됩니다. 주로 메쉬 이펙트 계열에서 많이 응용됩니다.


저희는 3d 노이즈로 랜덤한 버텍스 좌표 변화로 버텍스 애니메이션을 만들어 볼 것입니다. 그러기 위해서는 2차원 벡터인 UV 좌표 대신 3차원 벡터인 월드 포지션을 사용할 것입니다.


(WorldPos.xyz)


frac(WorldPos.xyz)


월드 포지션(World Position)이란 유니티 씬의 원점(0,0,0)을 기준으로 하는 월드 좌표계에서의 '미터 단위'(유니티 엔진은 미터 기준) 좌표 값을 말합니다.


float3 WorldSpacePosition mul(unity_ObjectToWorld,v.vertex).xyz; o.worldPos = WorldSpacePosition;


유니티 내장 월드 변환 행렬인 unity_ObjectToWorld와 로컬 좌표계의 좌표 값(Local Position)의 순서로 행렬곱(mul) 하여 계산 할 수 있습니다. OpenGL 렌더링 파이프라인의 과정중 일부와 같습니다.


col.rgb = NoiseTexel(i.worldPos.xy);


아직 NoiseTexel 함수는 float2 밖에 인풋을 못합니다. 월드 포지션의 xy 좌표만 사용했기 때문에 노이즈가 한 쪽 면에만 출력되고 나머지는 정상적으로 출력 되지 않습니다. 이전 포스트와 같은 보간 방식으로 float3 데이터를 받을수 있도록 함수를 바꿔줍시다.



전혀 어려울 것 없습니다. 차원이 하나 더 늘어나서 이제 Z축이 생겼으니 보간도 z축으로 한 번 더 해주면 되는 것입니다. float2 였던 변수도 전부 float3으로 늘려줍시다.



이렇게 만들어진 3D 노이즈 데이터를 버텍스 쉐이더로 가져가서 버텍스 좌표에 사용할 것입니다.


(색으로 출력된 오브젝트 노말)


오브젝트 노말 방향으로 버텍스 좌표를 이동(displacement) 하기 위해서 모든 방향으로 균일한 밀도의 폴리곤을 가진 메쉬를 사용해야 합니다. 저는 삼각형으로 이루어진 정20면체(Icosahedron) 도형을 사용하였습니다. 외부 3d 모델링 툴에서 제공되기 때문에 간단히 만들 수 있습니다.



요령은 버텍스 쉐이더로 넘어오기 전 appdata의 버텍스 좌표(vertex Position)에 접근하여 클립공간(ClipPos)으로 행렬곱 하기 전에 버텍스 데이터를 조작하는 것입니다.

v.vertex.xyz += v.normal * NoiseTexel(o.worldPos) * _Power;


버텍스를 노말 방향으로 움직이게 하려면 이동하길 원하는 값 만큼 오브젝트 노말과 곱해주고, 버텍스 좌표에 더해주면 된다고 이전 URP 포스트(링크)에서 다뤘습니다.



프랙탈 노이즈 만들기


(가장 유명한 프랙탈 구조인 망델브로 집합, 자연물에서도 쉽게 발견 할 수 있습니다)


프랙탈(Fractal) 이란 일부 작은 조각이 전체와 비슷한 기하학적 형태를 말합니다. 이런 특징을 자기 유사성이라고 하며, 다시 말해 자기 유사성을 갖는 기하학적 구조를 프랙탈 구조라고 합니다.


저희가 구현할 부분은 프랙탈 원리 대로 함수 자신을 변수로 삼아 반복되는 구조를 만드는 것입니다.


반복기능 구현을 위해 저희는 for 구문을 사용하여 원하는 만큼 반복할 수 있도록 코드를 작성할 것입니다.


int 변수는 정수로만 이루어진 변수를 뜻합니다. Iteration 횟수 만큼 반복합니다.


위와 같이 for 구문을 작성하면 반복할 때 마다 노이즈 해상도는 2배로 커지고, 버텍스 강도는 0.5배(1/2)가 됩니다.



적당히 색을 넣어서 용암이 흐르는 구체 느낌으로 완성 했습니다.


끝까지 읽어주셔서 감사합니다.


조회수 691회댓글 0개

최근 게시물

전체 보기
bottom of page