페이셜을 위해선 좀 더 복잡한 작업을 해야 한다. 셰이더를 작성하는 것에 추가로 애니메이션의 셰이프키와 셰이더의 프로퍼티를 연결하는 작업이 필요하다. 블렌더의 드라이버 기능에 해당하는 일이다. 언리얼은 머티리얼 커브라는 기능이 이를 대신하지만, 유니티는 아무래도 없는 것 같으니 만들어주자.
제목이 애니메이션 준비인데 정작 애니메이션은 들어가지도 못하고 있다. 그래도 가장 큰 산인 셰이더 컨버팅이 완료가 되었으니 이제 본격적으로 애니메이션을….들어가고 싶었는데 입셰이더에서 작은 문제가 발생했다.
저게 왜 저러지…?! 아무래도 clamp가 오작동하는 것 같은데.. 싶어서 관련코드를 지워보니
clamp문제가 맞다. 그럼 왜 이런 현상이…아… 버텍스 셰이더!
입의 셰이더 처리 대부분은 UV변형이다. 스케일, 회전, 이동등인데, 이펙트처럼 디테일한 변화가 필요없기 때문에 대부분 버텍스 셰이더에서 처리하고 있었다. 그 중엔 입의 UV를 경계에 맞게 짤라주는 clamp가 들어가 있었는데 이것을 버텍스 기준으로 처리하게 되면 당연히 입이 늘어나게 된다.
그 외 소소하게 입의 회전이 블렌더와 반대로 돌아가는 문제가 있어서 이것도 수정했다. 원인은 이것때문이다.
셰이더 컨버팅 중 문제가 생겼다. 앤젤링의 강도는 엔진마다 차이가 날 수는 있으나, 위치가 다른 것은 문제다. 왜 이런 현상이 일어나는지 조사해야 한다.
가장 먼저 의심된 것은 UV의 컨버팅 여부인데, 이것은 정상이었다. 컬러그리드맵을 씌워본 결과 모든 UV는 정상이다.
앤젤링의 연산에 sin함수를 사용하고 있는데, 이것이 블렌더와 다를 수 있다. 해서 살펴봤지만 이것도 정상이다.
원인을 알아냈다. v의 문제였다. 앤젤링은 뷰에 따라 uv를 offset해주는 해주는 방식을 사용하고 있는데, 엔진의 uv와 블렌더의 uv가 서로 다른 문제가 있다. 블렌더는 아래가 0, 엔진은 위가 0이다. 따라서 v를 움직이려면 부호를 바꿔주어야 정상작동한다. 이건 언리얼도 마찬가지인데, 입이나 눈처럼 uv를 오프셋해주어야 하는 부분에서 특히 까다롭다.
앤젤링의 컬러는 블렌더에선 헤어컬러를 좇게 했다가 엔진에선 커스텀으로 지정할 수 있게 하였다…가 다시 헤어컬러를 따라가도록 변경했다. 하지만 이번엔 엔젤링의 강도가 문제였다. 이를 찾기 위해 셰이더코드를 여기저기 뒤진 끝에, 이건 그냥 텍스처의 문제란 것을 발견했다.
앤젤링은 이런 바코드 텍스처(아휴 작아)를 사용하고 있는데 유니티에선 이것을 Single-Channel로 지정해 사용한다. 채널이 하나라면 자동으로 Linear컬러로 계산되고, 리니어 컬러는 감마처리를 하지 않았기 때문에 사람의 눈으로 볼 땐 좀 더 밝게 보인다. 이를 위해 블렌더에서도 sRGB와 Linear를 정의할 수 있는 프로퍼티를 제공하는데, 이것을 sRGB로 둔 것이 원인이었다.
컬러스페이스를 리니어로 바꾸면 이제 유니티와 비슷해진다.
문제는 sRGB 버전이 더 마음에 든다는 것이다! 이를 위해 포토샵에서 이미지를 어둡게 조절해 봤지만 결과가 약간 다르다.
전보다는, 좀 더 부드러운 느낌이 든다.
이제 얼굴 셰이더를 만들자. 일다나 스킨셰이더를 복사해서 넣어보았다. 아이고 무셔…
얼굴은 예외적인 셰이더 투성이다. 눈, 눈썹, 입, 얼굴 셰이더가 모두 다르고 하나하나 다 복잡하다.그 중에서 가장 쉬운 얼굴 셰이더부터 옮겨보자.
얼굴 제작 중 임시로 마스크를 벗겨주려고 했지만 작동이 되지 않는다.
URP에서 알파블렌드말고 알파테스트는 어떻게 가동시키는걸까. 이런저런 자료를 찾아봤는데 HLSL에선 2가지 방법이 가능했다.
clip(-1);
if (a<0) discard;
그냥 셰이더 모델 바꿔주고 알파값만 지정해주면 될 줄 알았는데, 그게 아니었다. 레거시 셰이더에선 AlphaTest커맨드로 가능하다고 하지만, 내가 사용하는 것은 HLSL이기 때문에 작동하지 않았다. 이걸 알아내느라 몇 시간을 쓴걸까… clip()함수 사용법을 몰라서 한참을 뒤졌다. 0보다 크거나 같으면 출력, 작으면 폐기된다.
어쩐지 렌더타입과 큐와도 관계가 없다. 알파테스트가 아니라 오파쿠여도 작동된다. 뭐 내부가 어떻게 돌아가는 건지 하나도 모르겠어. 이런 점들이 셰이더 공부에 난이도를 더한다. 정보가 매우 파편화되어 있어 검색해도 그게 답이 아닐 때가 많다.
타임오버. 얼굴이 아직 끝난 게 아니라는 것이 문제다.
12.25
얼굴은 마무리를 했다. 눈썹이 빨갛게 나오는 문제가 있는데 이건 데이터 문제이니 나중에 손보면 될 것이다.
입셰이더를 컨버팅을 했는데 한 번에 잘 작동할 리는 없다… 타임오버이니 여기서 끊고 커밋을 해두자.
12.26
입 셰이더를 짜려고 유니티를 열었는데, 블렌더에 비해 홍조가 너무 붉게 나오는 것이 눈에 띄었다. 그러고보니 눈썹부분도 너무 붉게 나오는 것이 수상하다.
홍조는 버텍스 컬러의 R채널의 정보를 받는다. 눈으로 봐도 꽤나 차이가 난다. 왜 이런 차이가 날까? 처음엔 float와 half의 정밀도 차이인가 싶었는데, 아차! 컬러스페이스!
블렌더의 버텍스컬러의 노드를 받아오는 부분은 감마가 자동으로 계산되는 것 같다. 리니어 컬러스페이스는 인간의 눈으로 볼 때 흰색이 좀 더 부각되어 보인다. 이를 맞추기 위해선 2.2를 제곱해야 한다. 따라서 버텍스 컬러를 받아오는 이 코드는
half4 colorMask = IN.color;
이렇게 수정되어야 한다.
half4 colorMask = pow(IN.color, 2.2);
이제야 제대로 된 색이 출력된다. 참고로 다시 리니어 스페이스로 돌리는 건 0.45제곱을 하면 된다.
하지만 컬러는 어차피 버텍스 단위로 계산되니, 프래그먼트보단 버텍스에서 하는 게 조금이라도 더 좋을 것 같다. 따라서 코드를 버텍스 셰이더쪽으로 옮긴다.
OUT.color = pow(IN.color, 2.2);
하지만 이 코드는 블렌더와 완벽히 호환되지 않는다. a는 언제나 리니어 컬러 스페이스로 계산되어야 한다. 따라서
하지만 컴파일창에 경고가 떠있다. pow함수는 음수가 지원되지 않는다고 한다. 물론 color는 음수를 사용할 수 없지만, 에러를 막기 위해 절대값변환을 해줄 필요가 있다.
OUT.color.rgb = pow(abs(IN.color.rgb), 2.2);
이제 문제없을 것이다. (아마도?)
입셰이더를 컨버팅하다가 알게 된 충격적인 사실. 유니티의 UV.v의 원점 위치도 블레더와 같이 아래에 있다. 언리얼만 위에 있는 것인가.
입의 셰이더도 완료. 다음은 눈.
1차 구현. 곤충이 되어버렸어!
곤충눈 현상은 텍스처 설정이 Repeat이기 때문에 나타나는 현상이었다. Clamp로 바꾸면 해결된다. 코드는 죄가 없다.
눈까지 완료. 하지만 블렌더와는 다르게 유니티에선 한가지 셰이더를 더 만들어야 한다. 바로 눈썹셰이더이다.
미루어놓았던 문제는 반드시 말썽을 일으킨다. 눈썹이 그렇다. 눈썹의 외곽선을 어떻게 처리할 지 고민을 하다가 부피가 크지 않아서 미루어 놓았었는데 결국 역풍을 맞았다.
그래도 다행인 것은 블렌더의 셰이프키 시스템이 어지간해선 말썽을 부리지 않는다는 것이다.
윤곽선을 표현하기 위해 이렇게 눈썹의 토폴로지를 다르게 변경해도, 별다른 말썽을 일으키지 않는다. 조금만 수틀려도 모든 데이터를 일그러뜨리는 맥스의 모핑시스템은…아휴, 말을 말자.
하나 더 다행인 것은 얼굴데이터 이전 스크립트를 미리 만들어 놓았었다는 것이다. 이 스크립트는 얼굴데이터에 연결된 머티리얼과 셰이더에 연결된 드라이버를 원래 캐릭터의 정보로 바꿔준다. 수동으로 하자면 하루는 족히 걸릴 작업을 버튼 한번으로 끝낼 수 있다. 고마워. 과거의 나!
12.27
얼굴 노말을 위한 버텍스 그룹을 지정할 때 WeightTool을 사용하면 다른 그룹을 건든다. 애드온의 버그인 것 같아 보인다.
아이라인 화장을 위한 마스킹을 버텍스 그룹으로 지정해두었는데, 이것이 깨지는 현상이 있었다. 편집하는 그룹 외에 다른 그룹은 꼭 잠가둘 것.
얼굴의 명암이 지저분하게 나오는 현상이 있어서 이를 조사했다. 원인은 커스텀 노말 계산부분의 노말라이징.
부드러운 얼굴 셰이딩을 위해 구형 노말과 원래 노말을 섞어서 쓰고 있는데, 구형노말을 계산할 때 노말라이즈를 빼고 있었다. 이는 의도적인 것이었는데, 어차피 픽셀셰이더로 넘긴 후엔 노말라이즈 해줄테니 버텍스셰이더에서 해줄 필요 있을까? 싶었지만 이것이 잘못된 생각이었다.
원인은 일찍 찾았는데 이걸 위해서 또 얼굴 데이터를 한바탕 청소했다. 좋아. 하는 김에 회색지대에 있던 작업들을 끝내버리는 편이 어떨까.
이것 또한 미루어왔던 작업이다. 얼굴은 데이터의 복잡도로 인해 틀이 잡히기 전까진 외곽선을 그리지 않고 있었다. 턱선을 그어주는 것과 그렇지 않은 것은 분명한 차이가 있어 보인다.
윤곽선은 여러개의 셰이프키는 필요하지 않지만, 그래도 고작 얼굴 실루엣 잡아주는데 이 정도 데이터를 쓰는 건 좀 낭비같다는 생각은 든다. 하지만 초심을 유지하기로 하자. 퀄리티를 낼 수 있다면, 어떤 비효율도 감수하기로.
다시 눈썹으로 넘어가고 싶은데 타임오버. 눈썹은 최종적으로 머리카락 위에 그려져야 한다. 큐를 어찌어찌하면 된다고 들었는데… 지금은 방법을 모르겠다. 스샷은 느낌을 보기 위해 ZTest를 건너뛰어 출력한 것이다. 그 어떤 오브젝트가 가리든 출력하기 때문에 사용할 수 있는 방법은 아니다.
…자러 가려고 했는데 문득 생각이 나서 테스트. 오.. 됐다. 렌더큐는 그리는 순서를 말하는 것인데, 이걸 아무리 정렬해봤자 작동하지 않는 게 당연하지[…] 일단 헤어의 렌더큐를 2낮춘다. 그 다음 눈썹의 ZTest를 Always로 그린다. 물론 다른 오브젝트위에 출력되어야 하니 ZWrite는 켜두어야 한다.그리고 헤어보다 높은 큐를 할당하면 된다. 즉 렌더순서는 헤어 -> 눈썹 -> 기타 셰이더가 된다.
그런데.. 이거 낮춰도 되나…높이는 건 많이 봤는데 낮추는 건 못봤는데… 일단 되니까 넘어가기로 하자.
본격적인 작업에 앞서 손의 라이브러리를 정의할 필요가 있었다. 이번 작업에서는 기존에 사용하던 오토리그 프로의 Fist컨트롤러를 뺐는데, 첫번째 이유는 컨트롤러본이 많을 수록 제어할 것이 늘어나 작업에 혼선이 많아지기 때문이고, 또 다른 이유는 포즈라이브러리가 그만큼 강력하다고 생각했기 때문이다.
손이 쥔 것도 아니고, 편 것도 아닌 애매한 모양이라 이런 걸 미리 정의해두면 편하다. 이걸 왜 하냐면…
칼자루같은 걸 쥘 때, 먼저 손을 활짝 편 후에 Fist로 블렌딩해서 표현하면 좋다. 손에는 관절이 많기 때문이다. 또한 손가락만 선택하고 싶을 때 선택용 그룹으로도 사용할 수 있다.
두번째로 세팅할 건 애니메이션 시의 잔상이다.
이건 프레임이 떨어지더라도 잔상이 없는 편이 좋을 것 같은데… 처음에는 이 잔상이 디퍼드 렌더링의 고질적 문제라고 생각했으나..챗GPT에게 물어본 결과 사이클은 디퍼드가 맞는데, 이브이는 포워드라고 한다. 이 놈을 믿어야 할까…싶다가도 이것저것 옵션을 찾아본 결과.
씬의 이브이 렌더러 샘플링 옵션에서 디노이징을 끌 수 있는 옵션을 발견했다!
이 옵션을 끄면 잔상이 사라진다. 대신 애니메이션 시에 안티앨리어싱이 나오지 않는다. 하지만 그게 뭐 중요한가. 눈이 맑아진 느낌이다.
세번째 설정은 프레임레이트이다.
우연의 일치일지는 몰라도 앨리스 모델의 경우, 목표치인 30frm에 근접한 수치를 보여줬다. 그냥 이대로 작업해도 되지만, 키가 많아지면 프레임레이트가 느려질 것은 불보듯 뻔한 일이다. 그리고 이 경우, 의도했던대로 애니메이션을 제작하기 힘들어진다. 재생속도가 느려지기 때문이다.
그래서 프레임스킵과 관련된 내용을 열심히 찾아본 결과…뜻하지 않게 빙GPT가 해답을 알려주었다.
대충 해석 :
뷰포트의 애니메이션 속도를 일정하게 유지하려면 다음과 같은 절차를 따르면 됩니다.
타임라인을 봅니다. 거기에 Playbak이라고 써진 드랍다운메뉴가 있습니다. 이걸 누르면 팝업메뉴가 뜹니다.
메뉴에 Sync라고 써진 메뉴가 있습니다. 이걸 ‘Frame Dropping’으로 바꿉니다.
도움이 되었길 바랍니다! 다른 질문이 있다면 알려주세요.
어쩐지 절차가 구체적이다 싶어서 살펴봤더니 정말로 있다. 오오!이제부터 빙GPT를 욕하는 건 나를 욕하는 것이다.
프레임 레이트는 여기에서 바꿀 수 있다. 시험삼아 60frm을 바꿔봤더니 2배빠르게 재생된다.
이제 준비에 필요한 모든 문제는 해결한 것처럼 보인다. 본격적으로 작업을 시작해보자.
오늘의 깨달음
회전 중심 포인트를 3D커서로 맞추고, 커서를 원하는 위치에 두면 해당축을 중심으로 회전시킬 수 있다. 맥스를 쓸 당시 바이패드에 있던 이 기능이 그리웠었는데 이로서 해결됐다.
만들다 보니 좀 감이 안잡힌다. 다리 거리를 어느정도나 띄워야 할까? 속도는 적절할까?
그… 일단 러프하게 만들어서 넘겨보자. 뭐라도 되겠지…
이제 유니티로 가보자. 이제 정식 데이터를 만들 것이므로, 프로젝트 파일을 리셋한다.
오늘의 깨달음
VS Code에서 변수선언위에 나오는 ‘xx references’의 문구들은 Code Lens라고 한다. 코드 초반엔 도움이 될 지 모르겠으나(사실 초반에도 그닥…) 덕지덕지 붙은 모양이 그렇게 깔끔하지는 않다. 이걸 없애려면 세팅(ctrl+,)에서 code lens를 검색한 후에 꺼주면 된다.
이제 깔끔해졌다.
이제 셰이더 컨버팅을 해야 한다. 유니티에서 기본 Unlit을 만들면 CG스타일로 짜주는데 URP에선 HLSL스타일을 권장한다고 한다. 아휴, 하나만 해라. 좀… 가뜩이나 배우기도 힘든 셰이더인데.
첨부터 다 짜긴 시간이 아까우므로 일단 예전에 짠 셰이더를 주섬주섬 고쳐쓰자.
다행이 잘 작동하는 것 같다.
12.22
본격적으로 셰이더 컨버팅을 하려는데… 생각해보니 난 스크립트로 노말맵을 구현해본 적이 없다. 이에 대한 자료를 찾던 중에 마둠파님 블로그에 친절하게 설명이 되어있는 것을 발견.
대부분의 게임이 그렇겠지만, 대전 격투에서는 특히 더, 대기 자세는 모든 모션을 통틀어 가장 중요하다. 각종 모션이 이 동작에서 파생되고, 가장 처음 보여지는 모션임과 동시에, 캐릭터의 개성이 표현되는 부분이다. 또한 모션이 너무 깨져서도 안되고 피격 판정을 위해 어느 정도는 네모 안에 들어와야 한다. 동시에 예뻐야 한다. 처음부터 어렵다.
믿기 어렵겠지만, 이 자세를 얻기까지 3시간이 걸렸다.
문제는 계속 튀어나온다.
치마아래 엉덩이라인이 보이는 것이 뵈기 싫어서 조금 더 수정. 포즈는 일단 이대로 두고 다음 날 다시 보는 것이 좋다. 이렇게 하면 좀 더 객관적인 시각으로 작업을 대할 수 있다. 때때로 형언할 수 없을 정도로 엉망인 경우도 많기 때문에…반드시 시간을 두어야 한다.
닐리
달래
오로라
달래와 오로라에겐 공통적인 문제가 발생한다. 상의의 통이 커서 실루엣 보간을 안해도 된다고 생각했는데, 생각보다 더 예쁘지가 않다. 오로라는 가방끈을 쥘 수 없게 설계해놨는데, 자세를 잡다보니 이 쪽이 어울려서 추가해야 할 필요가 있다.
12.20
오로라의 팔꿈치쪽에 실루엣 보간용 본을 설치.
완전하다고는 할 수 없으나, 팔꿈치의 실루엣을 제법 보완해준다.
앨리스가 좀 뻘쭘해 보인다. 너무 정석적인 자세인데, 딱히 어울리는 자세도 없다는 게 문제… 동양검 잡듯이 꺾어잡기는 싫고…
오로라는 특별한 것이 없다. 로봇은 대체로 리깅이 쉬운 편이고, 오로라 본체도 주렁주렁 달린 것도 그닥 없기 때문에…라고 생각했는데 예상밖의 문제는 늘 발생한다.
오로라의 컨셉이 문제가 된다. 평소엔 로봇팔이 가방 속에 들어가 있어야 한다. 필요할 때마다 가제트 팔처럼 튀어나와야 하는데, 이를 위해 스케일 애니메이션을 할 생각이었으나… ARP의 관절 시스템이 이를 쉽게 허용해 주지 않는다. 으.
수동으로 FK/IK를 구현하기엔 방법도 모르거니와, 구현한다고 해도 인터페이스가 ARP와 달라 여러모로 불편한 점이 많다. 그렇다고 ARP를 커스텀하자니 익스포트 문제가 걸린다.
좋은 방법이 없을까…
12.15
오늘의 깨달음
제약조건 Track to는 구식이니 쓰지 말 것. 결과값도 안좋다. Damped Track을 써야 한다.
결국 스케일을 위해 팔의 본은 회전값을 카피하는 별도의 본을 사용하는 걸로 세팅했다. 어차피 팔 본이 살아서 넘어가기 때문이 이는 매우 비효율적이지만, ARP는 세팅 그대로 사용하는 편이 좋기 때문에 방법이 없다.
이제 엔진확인이 필요하다. 엔진을 켜니 유니티 클라우드를 사용하지 않으면 클라이언트 구동을 하지 못할 것처럼 써놨는데, 된다.
다행이다. 잘 넘어온다.실린더가 넘어오지 않는 건 커스텀본 세팅을 빠뜨렸기 때문이다.
하지만 데이터 최적화면에서 아쉬운 면이 있다. 팔 본을 최소화시켰다고 하더라도 어쨌거나 이 본들은 넘어오는 정보이고, 최종적으로 유니티에선 사용하지 않는다. 심지어 키도 잔뜩 붙어있다. 뭐 별 수 없다.
그른데 Damped Track이 오작동한다. 작동하긴 하는데 결과값이 이상하다.
데이터를 따로 만들어보니 읭…? 잘 넘어오는데?
는 정보가 제대로 넘어오지 않은 것 같다. 왜 옛날이름…
아마도 익스포트에 시간차가 있었나보다. 작업을 진행하다보면 이렇게 별 것 아닌 문제로 시간을 잡아먹을 때가 있는데, 이 오류를 찾는 과정도 작업의 일부이라고 생각한다. 하지만 그래도 역시 시간이 아깝다.흑흑
이걸로 데이터가 잘 넘어오는 걸 확인했다. 이제 미러하고, 인물작업에 들어가보자.
아직까지 딱히 어려운 점은 없다. 오늘은 타임오버.
Data Transfer를 이용하면 한 번 잡아놓은 웨이트를 계속 쓸 수 있어 정말 편하다.
12.16
힙은 늘 문제다, 하지만 이미 닐리를 작업하며 지옥을 거쳐왔기에 비교적 수월하게 작업했다.
영상을 찍는 건 window+G키로 되는데, 앞뒤로 버튼 누르는 걸 좀 짜르고 싶어서 이것저것 편집기를 찾다가 윈도우에 내장된 동영상 편집기가 있다는 사실을 알았다. 그런데 이게 더 이상 지원을 안하는지, Clipchamp란 걸 새로 받으란다. 막상 받아서 해보니 오.. 생각보다 훌륭하다. 하지만 이걸 알아보느라 작업은 거의 못하고 타임오버. 나머지는 내일 해보자.
12.17
오로라 머리카락 리깅 작업 과정. 난 블렌더를 좋아하지만, 포즈모드와 오브젝트 모드를 오가는 리깅 인터페이스는 좀 수정되어야 한다고 느낄 때가 많다. 딱히 더 편한 방법을 제시하라면 또 그건 모르겠지만…
지금까지는 단발머리가 없었다. 앨리스는 묶은머리, 닐리는 본이 너무 많아서 머리에 쓸 여분의 본이 없었고, 달래도 땋은 머리. 때문에 고민이 된다. 얘는 치마도 없기 때문에 본이 남는데, 가닥을 더 세분화할 것인가? 아니면 그냥 덩어리로 묶을것인가?
이론적으로 여유가 남으면 쓰는 게 맞긴 한데, 디테일하다고 무조건 예쁜 실루엣이 나오지는 않는다. 그림을 그릴 때도 모든 가닥을 하나하나 그리지는 않으니까.
그른데 어찌됐든 이건 망한 듯. 다시 해보자…
본이 디테일하다고 무조건 예쁜 실루엣이 나오지 않는,..는 착각이었다. 본은 다다익선이다.!
파이썬 for문의 continue의 용도를 알게 됐다. 사실 파이썬은 정식으로 배웠다기보단 블렌더 스크립트를 사용하며 곁다리로 배운거라 이런 기본적인 용법을 잘 모른다. break는 루프를 아예 멈춤, continue는 이후 구문을 실행하지는 않지만 루프는 유지.라는 뜻이다. 지금까지는…? 그냥 if로 밀었다.(…) 이러면 코드가 점점 뒤로 밀려서 가독성이 떨어지게 된다.
챗GPT에게 또 한 수 배웠다!
노트 : Skirt_Scaler의 스페이스는 로컬이다. 트랜스폼을 쓰지 말 것.
12.12
스커트 본의 모든 걸 자동화하려니 모든 상황에는 맞지 않는 문제가 일어난다.위처럼 허리를 약간만 숙여도 치마가 지나치게 올라가는데
실제로는 원형이 최대한 유지되는 편이 낫다. 하지만 이걸 수동으로 할 수 있을까? 끔찍하게 귀찮을텐데…
ARP기본보다도 못한 느낌…
12.13
여전히 치마에서 삽질 중이다. 좋은 방법이 떠올랐다.싶다가도 정작 해보면 적절하지 않은 시도를 반복하고 있다. 삽질 중엔 별다른 말이 없이 열심히 머리만 굴리는데, 기록을 해놓으면 좋다는 걸 알면서도 그럴 여력이 안된다. 수정이 워낙 잦기 때문이다.
결론적으로 달래의 스커트는 닐리의 그것과는 다르다. 허벅지 부분을 터놓았을 뿐인데 움직이는 방식이 다르므로 생각을 다르게 접근해야 한다. 윗부분은 닐리의 것과 같지만 아랫부분은 차라리 앨리스의 치마와 비슷하다. 팔락거리지는 않지만 그렇다고 옆다리에 의해 당겨지지도 않는다.
충분히 고민했지만 수동조작외엔 답이 없다는 결론에 이르렀다. 작업 시작할 땐 얘는 달린 게 얼마 없어 쉬울 줄 알았는데 또 다른 벽을 느끼게 해주었다. 조작이라도 최대한 쉽도록 세팅해보자.
다리를 올리면 작동하는 스케일러는 내비둔채로, 아랫쪽의 치마만 들 수 있는 컨트롤러를 새로 만들었다. 원리는 Copy Rotation의 영향값에 드라이브를 건 것이다. 즉, 다리에 스냅된다.
처음의 계획은 컨트롤러를 하나로 통제하는 것이었지만, 생각보다 잘 움직이지 않아 앞/옆/뒤의 컨트롤러를 분리했다. 그 전에도 캐릭터 전용 컨트롤러를 만들 예정은 있었지만, 그게 달래는 아닐거라고 생각했다. 디자인이 별 게 없었으니까…
사람손을 많이 거치긴 하지만, 어쨌거나 이제 수동이라도 치마 실루엣을 제대로 보존할 수 있게 되었다.
활도 완성. 화살이 좀 짧길래 늘렸는데 그래도 짧아보인다.
치마만 해결되면 나머지는 쉽다. 달래 리깅 완료
포즈를 잡을 때 회전툴이 작동하지 않는 버그가 있었다. 리깅을 하다보니 액션데이터에 쓸데없는 값이 무수히 많아 발생하는 문제라고 추측된다. 액션을 지우고 리셋해주면 정상작동하긴 하나, 추후 데이터가 커지면 또 어떤 문제가 발생할 지 모른다.일단은 메모
닐리의 리깅만 보름째 이어가고 있다. 이 과정은 내게 많은 삽질을 시켰고, 많은 깨달음을 주었고, 마침내 작업이 마무리될 즈음, 보이지 않던 것들이 보이기 시작한다. 코트의 본은 당연히 어깨에 가까운 트위스트 본의 자식으로 둔다면 돌아가지 않는데, 이걸 왜 자동화불가능하다고 생각했을까? 또한 그렇게 마음에 안들던 소매의 웨이팅은 단순한 스무싱으로 깔끔하게 해결이 된다. 인내는 썼지만 열매는 역시 달았다.
개발인생 최고의 난이도였다. 이제 이게 유니티에서 잘 돌아가기만을 빌어보자.
본이 워낙 많아 뚫는 건 어쩔 수 없지…
이렇게 말하니 끝난 것 같은데, 사실 아직 안끝났다.흐흐흫응ㄱ
12.9
드라이브 이전과정에서 오래된 드라이버를 붙이는 현상이 있었다. 또한 존재하지 않는 채널을 복사하려다가 오류를 뱉는 현상도 발견되었다. 이는 테스트 과정에서 중복되거나 유효하지 않은 드라이버를 설정한 것으로, 애드온의 코드는 죄가 없다. 두 경우 모두 드라이버창을 열어서 채널을 삭제하면 해결된다. 기록차원에서 적어두자.
드라이브가 너무 민감하게 반응하다보니 다리를 살짝만 굽혀도 팬티가 보이는 현상이 있어서 이를 둔화시켰다.
드라이버를 이전하는 코드가 필요하다. 제약조건이야 블렌더의 기본기능인 symmetrize가 잘 복사해주는데 드라이버는 수동으로 해야 하기 때문이다. 지금까지는 수동으로 이동하고 있었으나 이것이 매우 번거롭다.
챗GPT에게 물어보니 엉뚱한 답만 내놓는다. 별 수 없이 인터넷 문서를 뒤졌으나, 드라이버를 새로 추가하는 법에 대한 설명뿐, 기존의 드라이버에 접근하는 방법을 쉽게 찾을 수가 없었다. 결국 뒤지고 뒤진 끝에 드라이버는 fcurve의 하위 클래스이긴 하지만, 액션의 fcurve와는 분리되어 있다는 사실을 깨달았다.
animation_data
action
fcurve
driver – 여길 뒤져봤자 아무 것도 안나온다.
animation_data
drivers
driver – 여길 뒤져야 한다.
이걸 알아내는데 1시간을 소비. 아오 빡쳐! 이걸 더 헛갈리게 하는 것이 drivers에 접근하면 리턴되는 값은fcurve클래스이다. 뭐 좋아. 발견했으니 이제 붙여야지. fcurve는 스크립트 짜면서 정말 피하고 싶은 요소중 하나이다. data_path와 array_index, group을 조합해서 본을 수동으로 찾아야 하기 때문이다. 방법이 없는 건 아닌데 신경쓸 것이 많다. 뭐 어떡해… 해야지..
드라이버를 수동으로 구성해주려면 각 항목에 대해 알아야 한다. 스택익스체인지에서 깔끔하게 정리해놓은 이미지를 발견할 수 있었다.
좌우의 본은 반전되어 적용되고 x,y,z등도 반전시킬 수 있도록 체크박스를 만들었다. 잘 작동되는 것 같다.버그가 없기를.
두께가 있는 모델링은 고민이 생긴다. 처음에는 가장 가까운 버텍스로 웨이트값을 이전하려고 했는데, 완벽하지가 않다. 노말 기준 계산이 아닌 구형 기준 계산이기 때문. 가장 좋은 것은 그냥 솔리디파이를 다시 주는 것인데, 이처럼 할 경우 반대쪽면에 손을 댔을 때(UV나 폴리곤최적화)가 문제가 된다.
하지만 얻는 것이 더 많으므로 이 쪽이 좋아보인다. 그렇다면 솔리디파이를 합치는 것은 리깅 이후로 미루어야 한다는 결론. 재작업은 불가피해 보인다. 머리가 나쁘면 몸이 고생이라더니 딱 그 꼴이다.
목요일부터는 가족여행으로 인해 한국에 없다. 여행다녀오면 기억이 모두 초기화되지 않을까..!
12.4
이렇게 자세한 리깅은 처음인지라 뭐하나 쉬운 게 없다. 리깅은 본설계와 웨이팅을 합친 과정인데, 설계가 끝난 후에도 웨이팅 과정에서 본을 수정할 일이 생긴다. 맥스는 이것이 꽤 번잡시러운데 블렌더는 이게 쉽다. 그나마 다행스러운 일이다.
드디어 팔과 코트를 합쳤다. 문제는 더 커졌다….
아우..맘에 안들어…
12.5
소매야 그럭저럭 됐는데 코트자락에 대한 좀 더 정확한 보간이 필요하다. 예상했던 가설들은 전부 빗나갔다. 실험을 해보자.
이것은 웨이트 이전용 프록시 메시이다. 이제 이걸
여기에 페이스 보간으로 웨이팅 이전을 한다.
바로 이 옵션이다. 그리고 아마추어를 씌우면 웨이팅값이 이전된다.
유니티 엔진은 영향받는 범위를 4버텍스 이하로 줄여야 한다. Limit Total을 걸어보자.
결과! 벌써부터 찌그러진다. 생각보다 더 민감하게 반응한다.
그렇다면 처음 세웠던 웨이팅방식으로 돌아가보자. 바로 그라데이션 방식이다.
이론적으로 영향버텍스가 4개를 넘지 않는다. 하지만 이걸 노말라이즈하면
엉뚱한 계산이 나온다.
여기까지 놓고 보면 결과는 명확하다. 마디마다 본을 늘리는 방법뿐이다.
기어이 이렇게 되었다.
팔을 들어올렸을 때의 처리는 자동화할 수 없다는 결론을 내렸다.
수동으로 보간하도록 하자.
12.6
지긋지긋한 코트 리깅을 끝내고 이제 나머지를 진행할 수 있다. 마음이 편하다.
12.7
머리카락은 정해진 공식이 있기에 어렵지 않다. 갈래가 좀 더 많으면 좋겠지만… 이미 코트에 너무 많은 본을 할애했기 때문에 더 이상 늘리는 것은 부담이 된다. (적다고는 하나 그래도 100개쯤 된다.)
더 예쁘게 표현될 수 없을까?를 고민하던 끝에 3축을 모두 계산에 넣는 방법이 떠올라 적용했다. 옆으로 다리를 벌렸을 때 옷이 공중에 떠 있는 문제가 있고 계산이 논리적이지도 않지만, 적어도 파묻히는 것보단 낫다.
덕분에 드라이버 코드는 좀 더 복잡해졌다. 이젠 진짜진짜 최종
드디어 다음과정으로 갈 수 있다.
11.26
블렌더의 스트레칭본은 뭔가 이상하다. 부모본의 스케일을 건드리는데 자식본이 별 반응이 없다.
내가 아는 지식선에서의 자식본이라면 이렇게 왜곡이 일어나야 한다. 그런데 이 왜곡이 일어나지 않는다. 그건 물론 좋은 일이지만, 대체 어찌 된 것일까.
블렌더의 본 관계도를 보면 스케일 상속유형을 정할 수가 있다. 그런데 여기에… None라는 항목을 선택하면
왜곡이 일어나지 않는다!! 개쩌는데?! 이거 엔진에 넘어갈까?
에이……..유니티는 개쩔지 않았다.
엔진 켠김에 닐리 치마도 잘 넘어가나 확인해두자.
다행이다. 이건 문제가 없다.애초에 본설계할 때 주변 본과 관련이 없도록 설계한 보람이 있다.
하지만 의문이 하나 더 남는다… 스트레칭 본은?!
에이…. 그럴 줄 알았어…스트레칭 본은 반드시 부모의 스케일이 1로 고정될 때만 사용하여야 한다.
치마에서 한바탕 고생을 한 덕분인지, 코트는 비교적 수월하게 느껴진다. 헬퍼본을 안쓰고 어떻게든 해보자싶었는데 헬퍼본 하나 심으니 모든 게 해결된다… 진작에 쓸 걸 그랬다.
11.27
..수월하긴 한데, 그게 쉽다는 뜻은 아니다.어질어질하다..
아이고야…
11.28
기본은 됐는데 몇가지 문제들이 눈에 띈다. 바로 저런 문제들.
팔을 위로 들었을 때 트위스트본이 꺾이는 문제가 있다. 내일 해결해보자.
트위스트본이 꺾이는 문제는 공간 행렬과 관련이 있다. 자세히는 모르지만 컴퓨터 그래픽스에서의 회전은 본의 공간이 회전하는 것이고 공간계산은 사인과 코사인 연산을 통해 이루어진다. 연산을 줄이기 위해서인지는 몰라도 180도를 넘어가면 이 값들을 마이너스로 처리를 하는데 이 과정에서 이런 뒤틀림이 일어난다.
부모본이야 상관이 없지만 자식본은 연결된 채 갑자기 같이 돌기 때문에 몹시 안좋은 결과를 가져온다. 그런데 이걸 어찌 풀어야 할까 고민하던 차에 회전값상속을 끌수가 있다는 사실을 알았다.
그럼 요래 움직이는데… 스케일은 안넘어갔지만 이건 넘어갈까?
오읭ㅇ?!! 이건 넘어간다?!
애니메이션 정보를 보니 키를 만들어서 보간하고 있다. 긍정적인 결과다. ARP에서도 잘 넘어갈까?
이럴수가! 아름답게 넘어간다!
그른데 아직 쓸 데가 없네… 음 일단 되는 걸 알았다는 사실로 만족하자.
팔의 메쉬가 튀는 원인은 알아냈다. 문제가 되는 버텍스들이 서로 다른 본에 반씩 걸쳐있는 것이 문제였다. 보통이라면 이것들이 크게 문제가 되진 않지만 회전제약을 걸어놓은 상태라 문제가 된다….라지만 그래도 문제가 되면 안될텐데?
오늘의 깨달음. 오일러 회전방식이 애니메이션 뿐만 아니라 현재 상태에도 영향을 끼친다. 수잔의 XYZ축을 모두 45도로 설정한 후 각기 오일러 회전방식을 다르게 해주었더니 결과가 다르게 나왔다.
각방으로 연구를 해보았으나, 결국 완전한 방법은 깨닫지 못했다. 쿼터니온은 사람이 못알아듣는 값이고 결국 오일러값을 조절해야 하는데 이 값이 왜 이렇게 들어가는지 이해하지 못했다. 아득한 수준너머의 일이라고 생각하고 그냥 괜찮은 오일러값을 고르는데 집중해야 할 것 같다.
11.24
트위스트 본의 첫번째 본은 다리를 옆으로 뻗었을 때 분명 x축으로 90도가 돌아간다. 이는 앞으로 뻗었을 때와 옆으로 뻗었을 때의, 그리고 그 중간 보간과정에서 어느 축으로든 회전각의 합이 90도가 된다.라는 계산에서 시작한 작업이었지만, 실제로는 오일러 계산에 의해 선형적으로 보간되지 않는다.
하지만 그렇다 해도… 강제로 90도로 맞춰주면 되는 것 아닐까? 비율적으로는 z에서 x로 각의 비중을 넘겨줄 때 그 값이 비선형적이라 해도 어쨌거나 90도에 맞추어 재계산해주면 되는 것 아닐까? 싶어서 다시 스크립트를 짜려고 보니… 이제는 이 많은 내용을 한 줄에 표현하기가 불가능하다. 함수가 필요하다.
단점은 파일을 열 때마다 스크립트를 한 번 실행해주어야 한다는 것이다. 이것마저 귀찮다면 제작이 끝나고 애드온에 함수를 내장시켜놓으면 될 것이다.
하지만….
문제는 여기서 끝나지 않았다. 이번엔 본이 커튼처럼 뼈가 있는 부분외의 부분들에 볼륨이 나타나는 문제가 나타났다.
분명 이것은 본의 가닥이 많아지면 해결이 되는 문제이긴 하다. 하지만 그럼에도 완전히 해결되지는 않는다.
그렇다면 윤곽을 유지하기 위해 보더모양의 본을 설치하고 지금까지 만들었던 줄기본을 헬퍼로 쓰는것은 어떨까
러프한 구현결과…어? 괜찮을 듯? 보더에 단계를 둔다면?
훨씬 나은 결과!
게다가 가닥본들은 이제 헬퍼본들이기 때문에 엔진으로 넘기지 않아도 된다는 장점도 있다.
오늘의 깨달음.
드라이버는 본이 지워져도 캐시에 남아있다. 때문에 같은 이름의 본을 추가하면 드라이버가 그대로 전승된다. 이는 본의 제약조건을 복사할 때 매우 효과적인데, 드라이버가 지워질 것이 무서워서 symmetry를 사용하지 않고 일일이 수기로 복사해주는 번거로움을 줄여준다. 물론 캐시가 날아가면 못쓰므로 파일을 다시 열기 전에 작업을 끝내도록 하자.
버텍스 웨이트 툴에서 copy는 가장 나중에 선택한 점의 값을 기선택한 점에게 복사해주는 것이다. 스무스를 주지 못하는 웨이트를 잡아야 하는 특성상 옆의 값을 따와야 하는데, 이럴 때 좋다.