본문 바로가기

Houdini/modeling

Radomly generated houses and a village tutorial(한글)

※ 본 글의 내용은 Houdini Apprenctice Non-Commercial 19.0.648 - Python 3 version 으로 작업합니다.

※ 어떠한 상업적 지원도 받지 않았으며, 이 튜토리얼의 원 저작권은 기입된 링크 속 유튜버에게 있습니다.

저는 그저 영상 속 튜토리얼을 따라하며 공부하는 팔로워임을 명시합니다.

따라하는 과정에서 여러 이유로 인해 제 의견이 첨언되거나 원 영상과는 다른 방향으로 진행될 수 있습니다.

먼저 이 튜토리얼의 제공자 톰클른(?)에게 감사를 표한다.

확실히 후디니에서의 모델링과 제너레이팅(반복 재생산 공장 느낌)에 대해

많은 공부를 할 수 있는 튜토리얼이었다.

 

 

완성본의 씬뷰와 넷뷰(네트워크뷰).

톰클루네의 설명을 따라가며 직접 노드들을 정리했다.

중간에 건너 뛴 부분도 있다.

(ex. 원형 창문 뚫기 안함/또 뚫기 귀찮아서 그만)

 

크게 나눈 노드의 그룹은 다음과 같다.

- 1층 : 바닥, 벽
- 2층 : 발코니, 벽, 지붕
- 마을

 

먼저 그리드 노드 생성.

grid (rows 5, columns 5)

 

 

sort 를 하나 붙여준다.
Point Sort를 Random으로 설정.

랜덤으로 저 바닥을 쪼개기 위해.

 

Random 설정 시 Seed 창이 활성화 된다.

톰클루네는 초기에 저기다 $F을 넣고 시작했다.

 

* $F : 현재 프레임 번호
.$F를 넣어두고 재생하면 프레임이 바뀔 때마다 값이 바뀐다.
재생순서에 맞춰서 값이 바뀐다는 건
랜덤 시드로 굉장히 탁월한 거라
$F는 어떤 튜토리얼이든지 꽤 나오는 것 같다.
톰클루네는 초기에 입력해둔 $F를 나중에 다시 수정했다.
저건 뒤에 나오는 다른 노드의 속성을 가져오는 거라.
근데 난 다시 수정하기 귀찮으니까
그냥 뒤부터 설명한다.
그래서 마을 생성 먼저 설명한다.

 

 

이건 만들어진 건물을 랜덤으로 배치해

마을을 생성하는 노드 묶음이다.

 

여기서 foreach노드(블럭)을 쓴다.

foreach 노드는 종류가 몇 개 있는데

그 중에 보통 foreach - point 노드를 많이 쓴다.

이건 point를 중심으로 foreach를 돌린다.

 

foreach
: foreach begin ~ end 사이에 포함된
특정 작업을 반복 수행하는 것

 

원래 copytopoints는 반복 기능이 없다.

여기서는 foreach블럭 안에 있어서 copytopoints 노드가 반복되는 셈이다.

 

copytopoints는 왼쪽에 input으로 받은 개체를
오른쪽 input으로 받은 points에 갖다 붙인다.

 

여기서 copyto points는 완성본 건물의 노드를 input1로 받아서

foreach_begin1을 통해 들어오는 points에 붙이는 거다.

 

여기서 foreach - Metadata의 이름에 주목한다.

저 'AAAA'라고 이름 붙인 노드다.

 

앞서 sort 노드에서 우리는 AAAA라는 문구를 봤다.

그리고

여기에 AAAA가 있다.

 

- foreach_begin의 Method : Fetch Piece or Point
- foreach_begin의 Metadata : Fetch Metadata

 

잘 보면 foreach_begin과 노드 아이콘이 똑같다.

옵션만 'Fetch Metadata'로 바뀐 애다.

Metadata는 foreach 블럭 내에서 반복의 횟수에 관계된다.

 

대충 정리하자면,
grid 새로 만들고
그 위에 scatter(점 찍는 노드)로 점 찍은 다음에
그 점마다 반복해서 건물을 붙이는 과정이다.

scatter의 포인트 수는 32개였다.

그리고 foreach 블럭 안에서 31개였다가

foreach_end 노드에서는 다시 32개가 된다.

 

나는 이걸

포인트를 받아다가 반복문을 수행하고

다시 돌려주기 때문이라고 이해했다.

 

회사에서 외주를 받으면 어차피 다시 줘야되지만
받은 동안만큼은 내 작업이니까 1개가 빠진다는 느낌.

 

톰클루네는 AAAA라고 대충 이름 붙였다.

작명이 많이 귀찮았던 것 같다.
다만 배려심이 좋은 분이라
잘 알아볼 수 있게 붙인 노드명이 AAAA다.
(==별 뜻 없는 노드명이니 신경쓰지 말자)

 

 

metadata 'AAAA'는
Detail이라는 속성을 갖는다.
이걸로 foreach블럭에서의 반복 횟수에 간섭한다.

그리고 그 Detail이라는 속성의 세부사항을 보면
iteration, numiterations라는 속성을 갖고 있음을 알 수 있다.
그 중에서 우리가 쓸 건 iteration이다.

 

detail(~,~,~) 과 같이 쓰는 건 보통 함수(?) 정도로 생각한다.

특정 기능을 수행하는 거다. 고로,

 

detail("../AAAA/", iteration, 0)  은

 

1) detail 값 가져와
2) 현재 폴더 안에(../)

3) AAAA라는 노드 안에서(AAAA/)

4) iteration 값 가져오면 돼

5) 아, 그거 0번에 있더라

 

정도로 이해한다.

 

참고로 코드를 기반으로 하는 대부분의 것들은
0번부터 시작한다.(첫번째는 0번)
여기서 iteration도 첫번째에 있으니 0번이다.

 

 

 

Detail Attributes : iteration, numiterations, value, ivalue

순서로 저장한다고 친절하게 표기되어 있다.

정확하진 않지만 난 저래서 iteration이 0번이라고 이해했다.

 

이제 저 iteration을 왜 굳이 random의 초기 시드로 사용했는지 설명한다.

 

 

foreach는 반복문을 수행한다.

AAAA는 그 횟수에 관여한다.

단순 프레임 변화가 아니라,

마을에 건물이 배치되는 형태가 변화할 때마다

건물의 형태도 변화하는 게 목표다.

따라서 AAAA의 값을 갖다 쓰는 거다.

 

서론이 길었지만 아무튼 저렇게 만들어진 건물을 배치해 마을을 만든다.

이제 다시 건물짓기로 돌아간다.

 

건물 밑바닥 마저 짓겠다.

앞서 grid를 polygon 형태로 만들고

sort 로 grid의 point에 변화수치로 detail값을 가져왔다.

가져온 point들을 묶어줄 거다.

랜덤으로 묶어야 한다.

그래야 똑같은 건물의 형태를 면할 수 있다.

 

rand(detail("../AAAA/",iteration, 0))

 

을 grouprange 노드의 Start 값으로 준다.

grid의 point들을 group으로 묶는데,

그 시작점에 랜덤값을 주는 거다.

rand는 뭘 집어넣든지 0~1사이의 실수를 반환하는 함수다.

그래서 사실 그 안에 어떤 값이 들어가든 의미가 없다.

seed(value)라는 건 그런 거다.

의미는 없지만 변화하면 값이 달라진다.

 

후디니 공식 문서에서는 rand를 저렇게 설명한다.
같은 값을 넣으면 언제나 같은 값을 준다고.
그 변화값으로 보통 $F를 쓴다고까지 친절히 알려준다.

이제 grouppromote 노드를 통해서

auto(라서 자동으로 point로 인식된) 그룹을

primitive로 바꾼다.

 

그래서 노드명을 auto_to_prim으로 지었다.

왜 굳이 노드명을 바꾸냐면,

 

 

blast를 통해 저 primitive들을 제거할 거라서.

(여기서 point는 제거해도 무의미하고, 깔끔하지 않다)

 

blast 노드는 input으로 받은 group을 제거한다.

그래서 위 노드들을 보면

 

grouppromote의 노드명 : auto_to_prim
blast의 노드명 : auto_to_prim

으로 똑같이 떠 있다.

엄밀히 말하면 blast의 노드명은 다르겠지만

보통 제거하는 노드의 노드명을 띄워준다.

 

잠시 정리하자면

1) sort에서 detail(~,~,~)로 metadata AAAA의 값을 받아온 이유
: 점들의 정렬 순서를 변화하게 하기 위해
: metadata AAAA의 detail값은 반복 횟수에 따라 변화하므로

2) groupbyrange에서 rand(detail(~,~,~))을 쓴 이유
: 변화한 정렬 순서에서 빵꾸난 부분의 위치가 랜덤으로 변하게 하기위해

이 부분이 잠시 헷갈려서 한참을 고민하다 해본 정리다.

 

polyextrude는 보통 두께를 줄 때 사용하지만(distance 조정),

polyextrude3 노드에서는 inset을 조정했다.

inset을 -0.3 정도주니까 기존의 바닥면보다 면적이 커졌다.

커진 바닥면에는 polyextrude4로 두께를 준다.

 

distance를 조정하면 되는데,

0.3 정도 올리니까 저 정도 두께가 됐다.

 

 

만들어진 1층 바닥에 transform 노드를 달았다.

translate는 3칸이 있는데

좌측부터 x, y, z값이다.

 

y값에 4 정도를 줬다. 

 

이렇게 1층 바닥과 2층 바닥 완성.

 

이제 1층 벽을 만든다.

 

1층 벽은 1층 바닥에서 크기를 조정하기 전에 뽑아낸다.

polyextrude3 노드에서 바닥 크기를 키웠으므로,

그 직전인 blast 노드의 output을 별도로 가져와야 한다.

polyextrude를 통해 다시 벽을 세운다.

수치는 4를 줬다.

 

참고로, 여기서 내가 쓰는 수치들은 큰 의미가 없다.
세상엔 긴 건물도 있고 짧은 건물도 있으니.

 

polyextrude 노드의 속성 중
오른쪽 하단 Output Geometry and Groups를 보면
Output Back이 꺼져 있는 경우들이 있다.
체크하면 밑바닥이 막힌다.
여기선 중요하진 않고, 본인 선택이다.

 

벽 끝.

창문 뚫자.

 

subdivide 노드를 달아서 면을 나눈다.

Algorithm은 OpenSubdiv Bilinear로 설정한다.

 

Include by Edges에 대해서는 별도로 글을 올리겠다.

 

일단은 저 허접한 그래프를 보고 이해가 됐으면 좋겠다.

여기서는 0~90, 270~360 사이 값으로 넣으면

edge들이 그룹화 된다.

그리고 우리 눈엔 아무런 변화가 안 보일 거다.

 

 

하지만 blast로 지우면 저렇게 된다.

모서리의 edge들을 지운 거다.

왜 지웠냐면, 창문은 모서리에 뚫지 않을 거니까.

물론 뚫어도 감각적인 건물이 되겠지만
여기서는 자제해본다.

 

 

다시 그룹화를 시킨다.

여기서는 group type이 points다.

그리고 keep in bounding Regions의 Enable에 체크를 한다.

쓰겠단 의미다.

 

boundingbox의 크기는 저 정도로 조절해준다.

우리가 원하는 건 벽 가운데에 뚫린 창문이니까.

잘 모르겠어도 일단 뚫자.
노드의 흐름은 뒤에서 깨닫는 게 꽤 있다.

 

blast로 한 번 더 지운다.

이번엔 bounding box의 밖에 있던 points가 전부 지워졌다.

저 점들은 추후 창문이 뚫릴 자리의 표시라는 걸 기억하자.

 

 

sort 노드를 쓴다.

random을 주고 seed에는

아까 봤던 그 변화값을 넣어줬다.

 

grouprange 노드명 : windows
group type : points
range filter : select 1 of 2

 

 

그리고 box노드 하나를 만들고,

copytopoints 노드를 붙인다.

만들어둔 점들 위로 box가 붙었다.

 

맞다.

박스 붙일 자리를 얻으려고 고생했다.

 

 

 

boolean 노드를 붙였다.

boolean은 input으로 받은 것들을 서로 빼주거나 더하는 노드다.

output은 본인이 선택할 수 있다.

여기서는 창문을 빼는 걸로 했다.

 

Set A : Surface

로 설정하면 표면이 빠지고

solid로 제거하면 파인 상태에서 안이 막힌다.

 

이제 발코니 만든다.

sort로 point에 다시 랜덤값을 줬다.

발코니도 어쨌든 랜덤으로 만들어져야 하니까.

 

grouppromote를 통해서

그룹화된 point를 primitive로 바꾸고

 

split 노드를 붙여서 나머지를 분리한다.

 

 

divide 노드를 달고 remove shared edges 를 반드시 체크해준다.

안 그러면 노드가 3각 폴리곤으로 다 분할 돼 있다.

그럼 나중에 발코니가 아니라 정글짐이 될 거다.

 

polyextrude를 달아서 distance를 2 정도 줬다.

벽은 아까 4를 줬고, 발코니의 난간 높이는 절반인 거다.

이것도 개인 취향이다.

 

polywire를 달면 이렇게 edge를 따라서 poly가 생긴다.

발코니 끝.

 

 

이제 벽으로 넘어간다.

split 노드의 오른쪽 output에 커서를 갖다대면

Non-Selected Geometry(output2)라는 문구가 뜬다.

발코니로 선택되지 않은 칸들은 아직 살아있다.

blast는 제거였지만, split는 분리다.

남은 부분이 살아있다.

polyextrude로 살린다.

 

2층 벽 완성.

지붕도 split에서 빼와서 만든다.

발코니 위에 지붕을 만들고 싶은 게 아니라면

split의 오른쪽 output으로 지붕을 만들자.

polyextrude를 통해 이번엔 distance와 inset을 한꺼번에 줄 거다.

저 정도 주니까 저렇게 지붕 형태가 갖춰졌다.

너무 과하게 주면 면이 다 엉킨다.

 

color노드를 달면 원하는 색을 선택할 수 있다.

 

transform 노드로 지붕을 위로 올린다.

아까 1층 벽과 2층 벽을 전부 4로 만들었으니,

여기도 y값에 4를 줬다.

 

아까 만든 발코니랑 벽, 지붕을

merge노드로 합친다.

1층에서 2층으로 올린다.

transform노드를 쓴다.

그리고 merge를 써서 1층과 2층을 전부 합치면,

위와 같은 건물 하나가 나온다.

저건 그냥 건물이 아니다.

프레임이 바뀔 때마다

발코니의 위치, 건물의 패인 모양이 전부 변할 거다.

 

이제 마을을 만든다.

grid하나를 별도로 만들었다.

크기는 100,100을 줬고

rows와 columns도 10,10이다.

더 크게 마을을 짓고 싶다면 더 크게 줘도 된다.
다만 본인 컴퓨터의 사양을 고려하자.

 

scatter는 점을 찍어주는 노드다.

force total count를 대충 만져주긴 했지만 큰 의미는 없다.

Generate를 By Density로 설정했기 때문이다.

그럼 밀도에 따라서 점의 개수가 증가한다.

 

68프레임과 122프레임의 스크린샷이다.

 

따라 만드는 것도 시간이 걸리는데

글로 풀어쓰려니까 더 오래 걸린다.

그래도 공부가 된다.