
Procedural modeling
- 논리적으로 만들어서 최대한 사용자의 편의에 맞게 대응하는 시스템을 구축
강의 순서
- 전체 제작 과정
- 기술 확인 - 어떠한 기술과 스킬이 필요한가
- 레고 블럭이 어떤 규칙으로 생겼는지 확인
- re) 전체 제작 과정(1번과 유사)
- 모델링(실 작업과정)
- Colors
어떤 과정을 통해 레고블럭이 만들어지는지 전체 제작 과정을 살펴보자.
핵심이 되는 포인트의 위치를 직접 잡아줘야한다.
이렇게 위치시킨 포인트를 바탕으로 어디가 꼭짓점인지 알 필요가 있다.
그리고 포인트를 사용해서 박스를 만들어준다.
밑면 제작을 위해 boolean 노드가 사용된다.
레고끼리의 결합을 위한 스터드(stud)를 만들어준다.
밑면의 스터드가 끼워질 수 있는 홈도 만들어준다.
그리고 시뮬레이션을 위한 proxy를 만들어준다.(convex hull)
완성된 모델링을 가지고 bevel을 추가해서 기본적인 디테일을 추가한다.
subdivide를 적용해서 render를 위한 hard 버전을 만들어준다.
마지막으로 로고를 추가해준다.
레고제작을 위한 기술
- Boolean(Basic)
- 노드의 사용방법
- Box with Custom Points
- 우리가 직접 위치를 지정한 포인트 8개를 가지고 박스를 만들어주는 것
- If 조건문 with Nodes
- vex에서의 if문이 아닌, 노드 단위에서 if 조건문을 만들어주기(알아두면 유용)
Boolean Node

- 벤다이어그램과도 같다.


디폴트 세팅은 boolean의 첫번째 인풋이 결과의 기준(A)이 된다.




Boolean의 유용한 점은 Union으로 합치는 작업을 했을 때, 중간의 불필요한 면이 사라지고 뚫린 상태로 면이 붙는다는 점이다.
Custom points로 box 만들기

위의 이미지와 같은 grid가 존재할 때, 3번 포인트를 임의의 위치로 이동시킨다면, 면의 생성규칙에 어긋나지만 않는다면, 새로운 모양의 면이 될 것이다.

Attribute Copy 노드를 활용해서 포인트를 이동시켜보자.
Attribute Copy Node
- 원본 정보에 레퍼런스가 가진 정보를 넘겨주는 노드
- point의 수가 같다면, 포인트 번호가 일치하는 포인트끼리 1대1 대응으로 정보를 넘겨준다.
- 0번 포인트는 0번 포인트로 / 1번 포인트는 1번 포인트로 ...




뒤집힌 것을 바로잡기 위해서는, 새로운 위치정보를 정의하는 add 노드를 묶어주는 merge 노드에서 add 노드가 연결되는 순서를 조절해줌으로써 해결할 수 있다.

이제 Box를 만들어보자.

상자를 구성할 새로운 8개의 포인트를 모두 가지고 있다면, 이미 상자로써 point / vertex / primitive 규칙을 모두 가진 box에 점의 위치정보만 새롭게 넘겨주는 방식으로 새로운 box를 구성할 수 있다.
이미 box에 사용될 8개의 포인트가 다 준비된 상태에서 box의 면의 규칙만을 빌려서 새로운 box를 만들어낸다는 것이 핵심이다.
if 조건문을 Nodes 단위에서 만들어보자.
'만약 현재 프레임이 24프레임보다 크다면, 포인트의 색이 빨간색이었으면 좋겠다.'

이 식은 불안정하다. 예외에 대한 것(24프레임 이하의 경우)이 정의되지 않았다.


if 안의 조건의 판별은 switch에서 내리게 된다.(현재 프레임이 24보다 큰가?)

만일 switch에 조건으로 현재 프레임이 24보다 큰가? 가 들어가게 되고, 위와같이 세팅이 된다면, result의 결과는 switch의 판별에 관계없이 항상 add1 포인트를 출력하게 된다.
- 포인트가 생성되는 경로가 두가지가 된 것이다.
이 방식이 유용한 경우가 있다.
- 코드로 적어주는 것보다 노드를 사용하는 것이 훨씬 쉬운 경우에 유용하다.



이 방식이 조금 더 명확하게 다가오는 이유는, 조건문을 이야기할 때와 같은 순서로 배치가 되기 때문일 것이다.
'만약 ~한다면, T를 수행해주고, 아니라면 F를 수행해줘.'
위의 노드 순서와 매우 흡사하다.
레고의 비율에 대해서 알아보자.

숫자들(3.2 / 4.8 / 8 / 9.6)사이에 어떠한 패턴이 보인다.
1.6으로 나눠보자. 대략적인 비율이 나오게 된다.

위의 비율로 작업을 진행할 경우, 소숫점의 발생으로 약간의 아쉬움이 발생한다. 위의 비율을 두배로 올려주자.

스터드(stud)가 놓이게 될 위치가 중요하다.
- 첫번째 스터드가 놓이게 될 위치는 원점이다.
첫번째 스터드 위치(원점)를 중심으로 반지름(3)을 알기 때문에 원을 그렸고, 스터드 외각부터 블럭의 외각까지의 거리(2)를 알고 있기 때문에 모서리의 위치를 잡을 수 있다.
첫번째 스터드를 기준으로 두번째 스터드의 위치를 구한다면, 첫번째 스터드의 위치를 기준으로 5x2만큼 이동한 거리(10)에 두번째 스터드의 중심이 위치하게 된다.
레고의 스터드가 접지되는 것을 생각해보면, 레고 안쪽의 벽면의 두께(2)를 알 수 있다.
바닥면에서 스터드의 접지력을 높여줄 수 있는 기믹을 표현해야한다.
이것은 한줄 짜리 레고일 때와 두줄 이상인 레고일 때가 조금 다르다.
한줄 짜리의 경우, 스터드와 스터드 사이에서 접지력을 높여주기 때문에, 스터드와 스터드 사이의 간격(4)을 지름으로 하는 원통형 구조물이 들어가게 된다.
두줄 이상의 경우, 스터드 4개의 위치를 기준으로 하여 중앙부분에 접지력을 높일 수 있는 기믹이 추가된다. 원점을 기준으로 해서 기믹이 놓이게 될 위치는 {5, 0, 5}가 되고, 기믹의 반지름은 (5x루트2)-3이 된다.
레고를 만들어보자.
포인트를 생성하고, 만들어줄 레고의 정보를 달아준다.

X by Z 의 레고 블럭을 만들게 될 것이고, 블럭의 높이로 Y가 사용되는데, 0은 제일 낮은 높이(4)를 가지고, 1은 기본적인 레고 블럭높이(12)를 가지게 될 것이다.

subnet을 사용하는 이유로는, 작업을 완료했을 때의 노드 구성의 깔끔함이 첫번째 이유이고, 두번째로는 작업의 시작이 포인트가 가지는 x, y, z의 정보부터이기 때문이다. 레고빌드로 들어가는 x, y, z 포인트의 정보에 따라 아예 다른 레고가 출력될 수 있어야한다.
1 by n 의 레고 블럭 구조와 n by n의 레고 블럭 구조는 밑면의 접지를 위한 디테일 부분에서 차이가 있었다. 이 부분을 위해서 x 또는 z가 1일 경우와 2 이상일 경우를 나눠서 레고 블럭이 생성되게 될 것이고, 이 때 if 조건문을 사용하게 된다.


N by N 케이스부터 만들어보자.
우리가 만들어줘야할 포인트의 개수는 X x Z 개이다.(강의에서는 2 x 4 = 8개였다.)
for 반복문을 사용할 줄 알아야한다.
subnet에서 8개(X x Z)의 포인트를 생성하게 될 것인데, info에서 이미 하나의 포인트가 내려오는 상황이기에 총 9개의 포인트가 존재하게 된다. 나중에 한개를 빼주게 될 것이다.
for 문이 반복하게 되는 동안, i(for문을 돌리는 카운터)를 a로 나눠준다. 이 때 얻게되는 몫은 z를 기준으로 하는 행의 순서가 되고, 나머지는 x를 기준으로 하는 열의 순서가 된다.


이제 꼭지점을 얻기 위해 어느 포인트가 귀퉁이 인지를 인지시켜야한다.
setpointattrib function을 사용해서 floorcount와 ordercount를 저장해둔다.
- 반복문에서 변수를 attribute로 남기고 싶을때 사용하는 노드이다.
- setpointattrib(0-나자신에 대해, "어트리뷰트 이름", 어디에 저장하고 싶은지, 어떠한 값을 저장하고 싶은지, "set")


꼭짓점을 인식시키기 위해 가장 구석에 있는 포인트 4개를 각각의 그룹으로 지정해주자.


각 포인트에 대해서 외곽의 모서리 쪽으로 @P를 이동시켜준다.(이동거리는 5)

이 포인트는 box의 윗면이 될 것이다. 레고 블럭의 높이를 위해서 attribute wrangle을 가지고 높이를 정의해준다. 얇은 피스의 경우, 높이는 4가 되고, 기본적인 높이의 피스의 경우 높이는 12가 된다.
얇은 피스 / 기본 피스를 구분하는 기준은 detail에서 우리가 초기에 입력해서 넣어주게 되는 y값이다.
(y = 0 : 얇은 피스 / y = 1 : 기본 피스)


attribute copy 노드를 사용해서 원본 box의 면 생성규칙을 가져와서 새로운 box를 만들어준다. 이 때 생성되는 box의 각 포인트의 위치는 우리가 현재 가지고 있는 8개의 포인트의 위치정보이다.


위에서 만들어준 box와 boolean으로 안쪽을 파내줄 box를 만들어주자.

달라진 것은 블럭의 두께인 2만큼 안쪽으로 들어가고 y축 방향으로 -2만큼 내려줬다는 것이다.




이제 레고 블럭의 윗면의 돌출된 부분, stud를 만들어주자.


위와같은 튜브를 만들어주고, copy to points로 stud 위치를 미리 정의해두었던 포인트들에 붙여준다.


Stud는 위에서 만들어준 box에 boolean > Union으로 붙여줬다.
레고 블럭의 밑면을 자세히 보면, Stud가 붙는 위치에 홈이 파여져있다. 이 부분을 묘사해보자.



이번에는 블럭끼리 결합될 때 stud와 접지되어서 접지력을 높여주게 되는 기둥을 만들어주자.
(이 부분은 참고로 1 by N과 N by N이 조금 다른 부분이다.)

attribute wrangle(deep)은 detail의 y값을 가지고 0일 경우와 1일 경우를 판별하여 기둥의 높이를 정해주는 노드이다.

기본적인 레고모양이 만들어졌다.
이제 1 by N의 경우를 만들어주자.
약간의 트릭을 사용해서, 1 by N 이나 N by 1 이나 결과가 같게 나오도록(1 by N 모양으로) 해줄 것이다.

큰 골격은 N by N을 사용하고, 약간의 수정을 해주면 된다.

모서리를 판단하는 방법으로 0번 포인트와 마지막 포인트를 찾아내서 그 포인트를 기준으로 네 개의 점을 만들어주면 된다.
마지막 포인트를 찾아내는 방법으로는 sort > reverse를 활용해서 마지막 포인트의 번호를 0으로 바꿔줘서 찾아낸다.
그리고 접지를 높이기 위한 밑면의 디테일 추가부분도 수정을 해줘야한다.

stud 포인트를 받아와서 마지막 포인트를 blast로 날려준다. 남은 포인트는 축 방향으로 5만큼 이동시켜주고, 접지력을 높이는 기둥을 포인트에 copy to points로 붙여준다.

1 by N 레고블럭도 완성되었다.
이제 여러 버전의 레고블럭을 준비해주자.
- 빠른 시뮬레이션을 위한 Proxy 버전
- Base 버전(지금까지 만들어준 레고블럭)
- bevel 추가 버전
- subdivide 추가 버전
- Logo(LEGO) 추가 버전
Proxy 버전은, convex hull 노드만 달아주면 해결된다.

Bevel 버전은 group으로 특정 edge를 묶어서 bevel을 적용시켜줬다.


subdivide 버전은 기존의 bevel이 적용된 데이터를 가지고 subdivide를 주면, 삼각형 / 사각형이 아닌 다각형이 너무 많아서 면들이 많이 깨져보이게 된다.

이것을 고쳐주기 위해서 remesh 노드를 사용한다.

각진 부분을 살려주기 위해서 group으로 모서리 부분들을 묶어주고, remesh의 hard edges group으로 사용했다.
다시한번 각을 살려줄 부분들을 그룹화해서 bevel을 주고 subdivide 노드를 연결해주면 된다.



이제 logo 부착 버전이다.
logo가 붙게되는 위치를 알아내기 위해서 stud의 맨 윗 부분의 면을 따로 떼어낸다.



이제 각각의 면을 기준으로 중심을 찾아보자.
for - each block을 활용한다.
bound 노드를 활용하면 중심에 대한 위치를 확보할 수 있다.


bound 노드 아래에 add 노드를 사용해서 포인트만 남기고 지워버린다.

이렇게 찾아준 포인트에 LEGO 로고를 만들어서 붙여준다.

이렇게 만들어진 각각의 버전은 다음과 같다.

여러 블럭을 동시에 만들어주려면 어떻게 해야할까?
현재는 직접 N by N 의 블럭을 만들기 위해 숫자를 기입하고 있는 시스템이다.
이것을 1 by 1 부터 5 by 5까지 한번에 만들어내도록 하는 시스템을 구축하려고 한다.
그리고 색 묘사.
이 부분은 구글에 여러 색 조합이 올라와있으므로, 이것을 활용하기로 한다.
현재는 하나의 점과, detail의 정보를 가지고 레고를 만들어주고 있다.
그렇다면 여러개의 점이 각각 detail에 임의의 정보를 가지고 있다면 어떻게 될까?
만일 1 by 1 부터 1 by 4 까지의 블럭을 만든다면?

딱 봐도 뭔가 비효율적이다.
LEGO_Build 노드가 이렇게 복제가 될 이유가 없다.
for each points를 사용해서 각각의 포인트에 대해 lego build가 동작하도록 해주면 될 것이다.

근데 막상 실행시켜보면, 우리가 원하는 결과는 나오지 않는다.
그 이유는 각각의 포인트로 제공되는 detail의 값이 제대로 적용되지 않아서 그렇다.
single pass로 각각의 포인트를 확인해보면 우리가 넘겨주기 원했던 detail의 값이 아닌, 1, 0, 1만 출력하고 있는 것을 확인할 수 있다.
detail 정보가 merge 이전에는 각각의 포인트에 대해 각기 다른 정보를 가지기 때문에 4개의 detail 정보가 존재한다. 하지만, 이것이 merge로 묶이게 되면, 가장 왼쪽 정보의 detail 정보만이 남게 된다.
이 부분이 문제였다. 고쳐야한다.
일단 detail에 있단 x, y, z에 대한 값을 포인트에 저장해준다.(attribute wrangle의 run over를 points로 바꾸면 된다.)

for each 블럭 안에서 attribute promote를 활용해서 points의 x, y, z의 정보를 detail로 넘겨준다.

현재는 생성되는 모든 블럭이 겹쳐보이게 되어서 확인이 어렵다.
각각의 회차에 따라서 조금씩 아래에서 생성되도록 해주자.
metadata를 활용하여 각 회차에 따라서 조금씩 이동할 수 있도록 해줬다.

이제는 1 by N 부터 N by N 까지 한꺼번에 입력해주는 시스템이 있으면 된다.
grid를 만들어준다.

이 grid가 for each 블럭에 들어가게 되면, 각각의 포인트에 대해 레고 블럭을 만들어낼 것이다. 이때 어떠한 x, y, z에 대한 값을 적절히 제공하기만 하면, 우리가 원하는 레고 블럭을 만들 수 있다.




이제 색을 추가해보자.
점을 만들어주고, 각 점에 임의의 색 정보를 입력해준 뒤(팔레트를 만들어주고) 그 중의 한가지 색상을 attribute copy를 사용하여 색을 레고로 넘겨준다.
트랜드의 색 정보가 정리된 사이트를 소개한다.
Coolors - The super fast color palettes generator!
Generate or browse beautiful color combinations for your designs.
coolors.co


핵심은 만들어준 팔레트 안에서 sort > random으로 임의의 색이 들어간 포인트를 선택한 뒤, for - each primitive가 돌아가는 동안 각각의 블럭에게 attribute copy로 색 정보를 넘겨주게 되는 것이다.
이 때, sort의 seed를 각각의 블럭마다 다르게 적용하기 위해서 detail function으로 metadata의 iteration 정보를 가져와서 활용했다.

중간중간 선생님 강의를 잠깐 멈추고 스스로 짜보고 고민해보고 해서 결과를 도출하고
다시 강의를 들으면 역시나 내가 한 방식은 뭔가 산만하고 정리가 안되어있다리...
procedural modeling은 평소에도 관심이 좀 있던 부분이었는데 이것을 이렇게 풀어낼 수 있구나 하는 강의였다.
이것을 좀 응용하면 레고의 다른 파츠들도 만들어볼 수 있을 것 같고, 이런 모델링 방법에 익숙해지면(언젠가는~) 또 다른 모델링을 할 때도 참고하고 스킬을 응용해서 할 수 있지 않을까 싶다.
(가령 규격화된 프랍이 준비되어있을 때 사용자에게 임의의 규격을 전달받으면 그에 맞게 문을 만들어주고 창문을 달고, 여러 extra 어셋들이 붙어있는 건물이 생성된다던지 하는 그런 것들 말이다.)
'규칙이 머리에 있으면 구현은 노동이다.'
노동요를 들으며 노동을 즐기는 사람이 되자... 에헤라디야~