FX/Houdini_Joy of VEX

Joy of Vex - Assignment #2 & Final Review

Gamestonk118 2022. 8. 12. 14:35

중요했던 부분:

과제 주제:

우리는 이미 닷프로덕트 크로스프로덕트를 배웠고 광원에 대한 묘사를 연습한적이 있습니다. 그렇다면 포인트 광원이 아니라 
1.스피어 광원은 어떻게 묘사할수 있을까요? 이거 실기 숙제로 드리겠습니다 
2.약간 파란빛이 도는 스피어 2짜리 광원과 약간 노란 빛이 도는 스피어 1짜리 광원으로 Box를 비추는 장면을 vex코드로서 만들어 자랑해주시길 바랍니다 

하아아... 막막해보이지만 해보겠다.

 

첫번째 노드블럭 더미들

우선 box뿐만이 아닌 다른 geometry에서도 나타내기 위해 여러 geometry를 생성해준 다음에 switch 노드로 지속적으로 바꿀 수 있게끔 세팅해주었다. 나는 총 box, rubbertoy, 그리고 tommy를 사용해 위의 과제 requirement를 수행해주기로 하였다. 문제는 rubbertoy나 tommy같은 geometry들은 material들을 기본적으로 가지고 있어서 add shader나 clothing/texture를 꺼주어야 했는데 이를 꺼주게 되면 uv map같은 것이 표면에 생성되었다. 이를 없애주기 위해 attribute delete노드를 사용하여 vertex attribute중 uv를 선택하여 그 부분을 scene view에서 안보이도록 설정해주었다. 

두번째 노드블럭 더미들

그리고 곧바로 sphere light source 광원을 생성해주기 위해 sphere geometry를 불러온 다음 transform노드로 프레임이 흐를수록 모션을 생성하여 원운동을 하게끔 만들어 보았다. 이때 문제가 있는데 point 광원과 달리 sphere은 제각기 각기 다른 포인트들의 @P 포인트 위치값으로 이루어져 있어 나중에 광원을 만들때 point()로 불러올때 어려움이 있을 것 같았다. 만약 무시하고 그대로 진행하게 되면 sphere을 이루는 점들 중 오직 하나의 점만 광원 역할을 하게 될거기 때문이다. 그러므로 sphere을 아래와 같이 NURBS타입으로 바꾼다음 모든 geometry를 이루는 모든 포인트의 위치값들이 모두 동일하게 끔 설정해주어 나중에 문제가 없게끔 만들어 보았다.

sphere의 프리미티브 타입을 NURBS로 바꾸어 보았다.
그랬더니 이와 같이 모든 포인트의 위치값들이 서로 동일하게 됬다

그리고 본격적으로 attribute wrangle노드를 연결하여 fake lighting 생성한 geometry에 광원의 위치에 따라 불빛이 반사되는 것같은 효과를 만들어 주기 시작하였다.

wrangle노드가 연결된 첫번째 전체적인 노드 블럭

우선 이는 Day 9때 배웠던 원리를 떠올라 빛이 실존하진 않지만 마치 geometry에서 빛의 산란 원리를 적용하여 알아서 색을 내게 하여 마치 빛이 반사되는 것을 구현해보도록 하였다. 이때 wrangle안에 쓴 코드는 다음과 같이 작성해주었다.

/* prim()를 활용하여 각각의 wrangle node input에 연결된 지오메트리의 point를 가져와 
vector variable al과 bl에 저장*/
vector redPos = point (1, "P", 0);
vector bluePos = point (2, "P", 0);

// 모든 포인트로부터 각각의 광원으로 향하는 방향을 지정해줌 (목적점 - 시작점)
vector redDir = redPos - @P;
vector blueDir = bluePos - @P;
// 각기 향하는 방향 vector값의 크기를 1로 바꾸어줌
vector normRedDir = normalize (ad);
vector normBlueDir = normalize (bd);
/* 0번 인풋에 연결된 geometry의 @N과 모든 포인트로부터 각기 광원으로 흐르는 방향의 vector값 사이의 
내적각도를 구하여라 */
float dotRed = dot (nAd, @N);
float dotBlue = dot (nBd, @N);
// dot()이 포함된 float variable들 자체에 상수를 더해주고 곱해줌으로써 빛이 퍼지는 범위와 밝기를 조정할수 있게하라
dotRed = (angleRed + chf ("ADD_A"))*chf ("MULT_A");
dotBlue = (angleBlue + chf ("ADD_B"))*chf ("MULT_B");
// geometry의 @Cd를 rgb값으로 따로 조절 가능케하여 색깔로서 빛을 더하여라
@Cd = 0;
@Cd = set (dotRed, dotRed/2 + dotBlue/2, dotBlue);

이와 같이 해당 geometry의 normal값과 모든 포인트들로부터 광원으로 향하는 vector값의 내적 각도를 통해 빛의 세기를 구현하여 이와 같은 쉐입을 구현해보았다. 이때 위에서 chf()으로 생성한 파라미터들로 빛이 퍼지는 범위와 밝기를 조절해 줄 수 있었다.

윗 코드를 사용하여 box가 sphere light source에서 나오는 빛을 반사하도록 설정

솔직히 맞게 한건지는 모르겠다. 처음에는 point()가 아닌 prim()을 사용하여 만들어보고자 하였는데 이와 같이 식을 작성해준다고 한다면 미세하게 바뀌는 것을 알 수 있었다. 둘 중 무엇을 사용해야 정답인지는 햇갈리기도 하였다.

/* prim()를 활용하여 각각의 wrangle node input에 연결된 지오메트리의 primitive를 가져와 
vector variable al과 bl에 저장*/
vector redPos = prim (1, "P", 0);
vector bluePos = prim (2, "P", 0);

// 모든 포인트로부터 각각의 광원으로 향하는 방향을 지정해줌 (목적점 - 시작점)
vector redDir = redPos - @P;
vector blueDir = bluePos - @P;
// 각기 향하는 방향 vector값의 크기를 1로 바꾸어줌
vector normRedDir = normalize (ad);
vector normBlueDir = normalize (bd);
/* 0번 인풋에 연결된 geometry의 @N과 모든 포인트로부터 각기 광원으로 흐르는 방향의 vector값 사이의 
내적각도를 구하여라 */
float dotRed = dot (nAd, @N);
float dotBlue = dot (nBd, @N);
// dot()이 포함된 float variable들 자체에 상수를 더해주고 곱해줌으로써 빛이 퍼지는 범위와 밝기를 조정할수 있게하라
dotRed = (angleRed + chf ("ADD_A"))*chf ("MULT_A");
dotBlue = (angleBlue + chf ("ADD_B"))*chf ("MULT_B");
// geometry의 @Cd를 rgb값으로 따로 조절 가능케하여 색깔로서 빛을 더하여라
@Cd = 0;
@Cd = set (dotRed, dotRed/2 + dotBlue/2, dotBlue);

sphere 광원의 vector값을 point()가 아닌 prim()으로 불러왔을때의 결과물

각기 sphere광원에 포지션에 모션을 주어 위치가 바뀔수록 geometry가 반사하는 빛이 알맞게 시간이 지남에 따라 변하는것을 구현할 수 있었다.

그리고 scale값이 커지면 빛의 밝기가 커지고 scale값이 작아지면 빛의 밝기를 작게끔 하기 위해 transform의 geometry의 각각의 scale값을 copy parameter하여 wrangle노드안의 파라미터 각각의 MULT값에 paste relative references를 하여 scale값이 

커질수록 밝기가 세지고 작아지면 밝기가 줄어들게끔 설정하였다. 그리고 이를 CONTROL노드로 전부 옮겨주어 조절하기 쉽게끔 만들어주었다.

각각의 값을 조절할 수 있게 해주는 CONTROL노드

지옥은 이제부터 시작이었다. 그림자를 만들어야 하는데 온갖 시도를 다해보고 해보았는데도 끝내 만족스런 결과를 얻지 못하였다... 

그림자를 만들어줄 기본적인 grid노드

우선 grid노드를 꺼내 그림자를 구현해보고자 실행해보았다. 그다음 vex코드로 어찌해서 그림자를 구현해보아야하는데 도저히 내 머리의 한계를 느꼈다... 아래와 같이 온갖 시도를 다해보았지만 제대로 나오는 것은 없어서 좌절하였다.

우선 첫번째로 grid로부터 각각의 광원까지의 vector값을 만든 다음 이를 @N값과 함께 dot()으로 내적각도를 계산하여 밝기를 구현하고자 하였는데 생각해보면 이는 그림자가 아닌 빛이 grid에 반사되는 결과만 낳게 된다는 사실을 깨달았다.

이와 같이 설정하면 그림자가 아닌 빛이 grid에 반사되어진 다음과 같은 것이 나오게 된다.

빛이 그림자는 나오기는 커녕 grid에 반사되어 모션이 진행될 수록 반사된 빛도 돌아가는 것처럼 보인다.

아래와 같이 이렇게도 해보았는데 그림자 비슷한게 나오긴 했는데 그냥 geometry위에 시커먼것만 나타나서 이렇게 할바에얀 그냥 minpos()를 이용하는게 쉽겠다 싶어서 이것도 폐기하였다. 이는 geometry를 구성하는 모든 포인트들로부터 grid를 구성하는 포인트들까지의 vector값을 구하여 grid의 원래 normal값과 같이 dot()을 적용하여 내적각도를 통한 밝기를 얻으려 했지만 아래와 같은 결과가 나타났다. 

이와 같이 설정하면 광원으로부터 빛이 통과되어져서 나온 그림자가 아닌 geometry아래 시커먼것만 생기게 된다.
위의 코드로 생성해서 나오게된 결과물
그림자를 생성해주기 위한 첫번째 노드 더미 블럭

하아아... 아무리 생각해봐도 답이 나오질 않았다. 그래서 우선적으로는 minpos()를 사용하여 가장 3번 인풋에 연결된 Geometry (해당 tommy)로부터 가장 가까운 포인트 위치들을 vector variable geoPos에 저장한다음 grid의 모든 포인트들로 부터 getPos의 거리까지의 값들을 색깔로 나타내어 보게 해보았다. 우선 grid를 0번 인풋에 연결하고 tommy나 rubbertoy, box같은 geometry를 3번 인풋에 연결하여 아래와 같은 결과를 나타내보게 해보았다.

/* 3번 인풋으로 들어온 geometry로부터 가장 가까운 grid를 이루는 포인트 위치값을 vector variable getPos에 저장*/
vector geoPos = minpos (3, @P);
// grid의 모든 포인트 위치값들로부터 위에서 구한 getPos의 거리값을 구해 float variable d에 저장
float d = distance (@P, geoPos);
// d값을 chf()로 만들어진 파라미터로 조절하여 그림자의 크기 조절
d *= chf ("RAD");
// 이 모든것을 색깔로 나타내어 모든 포인트가 3번 인풋 geometry까지 가까울 수록 어둡고 멀수록 밝아지게끔 설정
@Cd = d;

attribute blur노드를 사용하여 경계가 뚜렷한 그림자 쉐입을 blur처리하여 경계선을 모호하게 만들어 보았다.

rubbertoy의 그림자. Minpos()를 이용해 만들어 보았다.

마지막으로 merge노드를 사용하여 전부다 묶어주어 그림자 위의 geometry가 sphere 광원들로부터 나오는 빛을 반사시키는 모션을 구현해보았다.

마지막 결과물... 그림자가 너무너무 아쉽지만 우선은 여기서 마무리하도록 하였다.

이해가 안되었던 부분:

- NURBS로 sphere광원을 만들어준다음 point()으로 광원을 불러와야 할지 prim()으로 불러와야 할지 만약 특정한 것으로 불러온다면 두 function의 차이점은 무엇일지 생각만큼 이해가 되지 않았다. 

- 그림자를 어떻게 만들어야 할지 감이 잡히지 않는다. 이때 나는 그림자가 광원으로부터 geometry에 부딪혀서 뒤의 그림자의 방향과 스케일이 알맞게 생성되는 그림자를 만들고 싶었는데 이렇게 만드려고 할때 어떻게 만들어야 할지 전혀 감이 잡히지 않아 난감하였다. 더욱 리얼한 그림자를 만들기 위해서는 더욱 많은 시간과 연구를 필요로해야 겠다는 생각을 하였다. 

 

공부하면서 들었던 생각 & 소감문:

우선 마지막 그림자를 구현해보고자 하였지만 확실히 리얼하게 코드로만 구현해보도록 시도하니 많은 어려움을 느꼈다. Joy of Vex를 지금까지 완주하면서 많은 생각이 들곤 하였는데 확실한것은 즉 이번 과제를 통해 안 사실은 코스 하나 도달했다고 절대로 방심하면 안된다는 사실 이었다. 내가 오늘 그림자를 정확하게 리얼하게 구현하지 못한 것 처럼 joy of vex를 완주했다고 자신만만하거나 배움을 멈추면 안되는 것 같다. TWA님께서는 아마 배운 것들로만으로는 정확하게 만들어내지 못하실 것이라고 하셨고 새로운것을 직접 알아내고 찾아봐야한다는 말씀을 하셨는데 나는 후디니를 배움에 있어서 이런것들의 필요성을 간절하게 느낀것 같다. 어쨌든 오늘 과제 도전적이기도 하고 많이 어려웠지만 많은 것을 생각할 수 있게 하는 과제였다. 다음에 시간이 날때 계속 알아보면서 수정을 거듭해야겠다는 생각이 든다.

 

그리고 이번 과제를 통해 완전히 Joy of Vex스터디 과정을 끝나게 되었다. 솔직히 다른 스터디 메이트들이 벌써 일주일 이주일 전에 끝난지라 뒤늦게 끝난 내가 마음이 쓰인다. 그래도 Joy of Vex과정을 지나면서 느끼게 된점은 정말 vex의 기초에 대해서는 정말 어느정도 자신있게 만들어 준것 같다. 코딩을 아무것도 모르는 초보자들이 vex를 기본부터 배우면서 어느정도 다룰 수 있게 하는 레벨까지 커리큘럼을 잘 짜주신 CGWiki님 그리고 한국어 강의와 동시에 CGWiki님의 원본을 더욱 깊게 쉽게 해석하여 수강생 우리들에게 반복적으로 학습을 하게 시켜준 TWA님 그리고 모르는 것이 있었을때 바로바로 알려주고 지쳐갔을때쯤에 계속해서 공부인증들을 해주어 나를 다시 깨워준 이번 스터디메이트들 덕분에 내가 아마 여기까지 온건지도 모르겠다. 다들 감사드린다는 말을 전해주고 싶다. Joy of Vex 솔직히 말해서 쉽지는 않은 것 같다. 아니면 나에게만 해당하는 건지도 모르겠다..ㅋㅋ 분명한건 정말 도전적이었고 갈수록 난이도가 갑자기 껑충 올라가는 부분들이 정말 많았지만 TWA님의 친절한 강의들 덕분에 악으로 깡으로 버티면서 도달한것 같다. 개인적으로는 Day 7, Day 12, Day 17, 그리고 Day 20이 제일로 어려웠던 것 같다. 물론 다른 것들도 어려운 부분들이 많았던 것 같다. 그치만 TWA님의 반복적인 학습과 공부일지 시스템이 정말로 도움이 되었고 무엇보다 서로 공부의견을 공유해준 스터디 메이트분들의 도움이 정말로 컸던 것 같다. 아마 혼자서 공부했으면 Day 10부근에서 해매다가 6주가 지나가 버렸을지도 모른다. 이번 방학때 머릿속에 후디니밖에 떠오르지 않을 정도로 정말 6주간 정신없이 달려온 것 같다. 처음 티스토리를 만들었을때 내가 이것을 어떻게 채워나갈까 감히 엄두가 나지 않았지만, 하면 된다가 정말 실현이 된것 처럼 벌써 어마어마한 양의 공부일지를 작성한 나를 보니 뿌듯 하기도 하다. 후디니 1과정 또한 스터디에는 참여하지 못하지만 나만의 스케쥴에 맞춰서 이렇게 동일한 방식으로 공부를 할 예정이다. 또한 지금까지 배워두었던 vex가 이번 학기에 배우고 있는 Unity C#에 많은 도움이 되는 사실이 신기하기도 하다. quaternion이나 vector의 계산 이런것이 서로 3D상에서 동일하게 통용되는 것을 보니 정말 프로그래밍의 신기함을 느끼는 것 같다. 나는 아직 실무자도 아니고 경력도 없는 그저그런 대학생이지만 정말 진심으로 내가 원하는 분야에서 일하고 싶다. FX쪽을 정말로 파고들고 싶고 그쪽으로 내 커리어를 이어나가고 싶다. 그럴러면 후디니를 배워야 한다. 후디니는 어려운 툴로 유명하고 무료로 진행되는 제대로 된 강의도 거의 전무하지만 TWA님의 좋은 퀄리티의 강의를 통해 내 꿈을 향한 첫 발걸음을 디딜 수 있었던 것 같다. 스터디 메이트분들께서 열심히 하시는 모습에 계속 자극받고 더욱 성장해야 겠다는 생각이 지금까지 공부 기간 내내 들은 것 같다. 아직 넘어야 될 산이 어마무시하게 많지만 지금까지 해왔던 것처럼 차근차근 묵묵히 단계를 밟아가면 후디니에 있어서 완전 자신감이 있겠지?라는 생각을 하게 된다. 세상살이 뭐든것은 점진적 과부하니까... 어쨌든 마지막이다보니 글이 길어진것 같다..ㅋㅋ 마지막으로 이 소감문을 읽어주실지는 모르겠지만 다시한번 선생님들 모두와 스터디원분들께 진심으로 감사하다는 마음을 전하고 싶다 :)