꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

 

 

 

첨부 파일은 이 프로젝트에서 사용된 모델링과 에니메이션 파일(Fbx)가 들어 있습니다.

반응형
,
Posted by 알 수 없는 사용자

 

 

 

반응형
,
Posted by 터너 (TerNer)

[이전 글]

The Modernest C++ #0 - 시작하며

The Modernest C++ #1 - 템플릿 메타프로그래밍 (Template Metaprogramming)


요번 2주 동안 정말로 말도 못할 정도로 바빠서 컴퓨터 앞에도 못앉고 개발도 못하고 살았네요.. ;ㅅ; 그래서 이번주 부터는 버닝하고(?) 글을 써보도록 하겠습니다 ㅠㅠ 용서해주세요 독자 여러분들 m(_ _)m



타입을 가지고 놀아보기, type_traits


자, 저번 연재 글 까지는 템플릿 메타프로그래밍의 정의를 알아보았다면 이번 글 부터는 실전에서의 템플릿 메타프로그래밍을 접해볼 시간입니다. 템플릿 메타프로그래밍 자체가 가독성에서 불리한 면을 가지고 있지만 그래도 잘 따라오셨으면 좋겠습니다 :^)


우선 시작하기에 앞서, boost 라는 라이브러리 이야기를 해보도록 하겠습니다.



<boost 라이브러리의 로고 이미지>


https://ko.wikipedia.org/wiki/Boost - 위키피디아 <Boost> 항목


boost는 여러 개발자들이 모여서 개발한 하나의 큰 라이브러리 프로젝트이며 상기한 위키피디아 링크에서 볼수 있듯이 실제 C++ 표준 위원회에 소속된 개발자 분들이 다수 참여하여 만든 라이브러리라 믿고 사용할수 있습니다. 


boost 라이브러리는 mpl 이라는 이름의 템플릿 메타프로그래밍 라이브러리를 가지고 있습니다. 굉장히 실사용에 있어서 용도가 방대하고 기능도 많아 boost 라이브러리를 채택하는 프로젝트에 있어서 많이 사용되는 라이브러리 이기도 합니다. 이런 부분에 있어서 우리는 모범 답안이자 교과서와도 같은 mpl 라이브러리를 보고 따라 구현해보면서라도 템플릿 메타프로그래밍에 대한 이해도를 높이는게 중요합니다.


C++11 표준에서는 이미 boost 라이브러리의 여러 템플릿 메타 함수[각주:1]를 채택하여 이용중에 있는데 (is_integral, is_floating_point ….), 우리는 이제 이런 내용을 직접 구현해보는 시간을 가질겁니다.




시작하기 전에..


우리는 시작하기 전에 몇가지 기본적인 기능을 가진 메타함수를 작성해야 합니다, 이를 테면 두 타입을 비교하는 is_same 과 같은 것들을 구현해 놓으면 상속등을 이용하여 더 쉽게 프로그래밍 할수 있게 되기 때문에 후술하는 메타 함수들은 적어도 구현 방법 정도는 알아두시는 것이 좋을것이라고 생각합니다.


또, 메타 함수들을 직접 구현해보면서 우리가 C++ 언어를 배웠을때 스쳐지나갔던 상식들을 다시 일깨워 보면서 템플릿 메타프로그래밍에 있어 중요히 요구되는 C++의 문법이나 기능들을 다시금 살펴봄으로서 조금더 생소한 기술에 다가가는 시간을 가져보도록 하겠습니다. 조금은 어려울지 모르더라도 기본적인 C++ 지식만 가지시면 쉽게 따라오실수 있으실겁니다.




<두 타입을 비교하는 is_same>


is_same 은 C++11 표준안에 채택된 메타 함수이자 boost::mpl 라이브러리에도 포함되어있는 가장 기본적인 메타 함수입니다. 이 메타 함수의 기능으로는 템플릿 인수로 받은 두 타입이 동일한지를 체크하는 간단한 메타 함수입니다. 하지만 이런 간단한 기능을 가진 메타함수는 나중에 등장할 많은 메타 함수의 상속 대상이 되는 등의 쓸모가 굉장히 많으므로 절대 놓쳐서는 안될 중요한 메타 함수입니다.


우선 is_same 메타 함수의 구현 내용을 보도록 하겠습니다 :




자, is_same 메타 함수는 위 스크린샷에서 보실수 있듯이 굉장히 구현이 쉽습니다. 4번 라인의 is_same 구조체의 정의를 보시면 일단 메타 함수의 목적 자체가 두 타입이 동일한지 여부를 판단하므로 템플릿 인수를 두가지 받도록 구현이 되어있습니다. 여기서 주목해야 할건 is_same 구조체의 기본 정의의 value 값이 false 라는 점입니다.


왜 false 인지 의아해하실분이 있으시리라 믿는데, 이는 밑 10번 라인에 위치한 템플릿 특수화를 이용한 is_same 구조체의 정의를 보시면 됩니다.


템플릿 특수화 (Template Specialization) 란?


http://www.gamedevforever.com/323 - 친절한티스님 < 템플릿 특수화 (Template Specialization) >

https://en.wikipedia.org/wiki/Generic_programming#Template_specialization - 영문 위키피디아 < 템플릿 특수화 >


템플릿 특수화는 템플릿 매개변수를 특정한 값이나 타입으로 미리 정해주며 정의함으로서 클래스·함수 템플릿 호출시 미리 정해준 값이나 타입에 맞춘 정의에 맞게 동작하도록 하는 방법을 말합니다.


10번째 라인의 is_same 의 특수화된 정의를 보면 기존 정의에 템플릿 매개변수로 정의한 T1, T2 가 템플릿 매개변수 T 와 같을시에 분기하게 되는데 쉽게 생각해보자면 이렇습니다. 20번째 라인에 호출한 is_same 구조체의 템플릿 매개변수 값은 [T1 = int, T2 = float] 가 될것이며 22번째 라인에서 호출한 is_same 구조체의 템플릿 메개변수 값은 [T1 = int, T2 = int] 가 될 것입니다.


컴파일러는 이를 [T = int] 로 인식하게 됩니다, 우리의 똑똑한 컴파일러는 is_same 구조체의 템플릿 매개변수인 T1, T2 가 똑같으므로 하나의 T로 인식하게 되는겁니다. 그게 논리적으로도 맞는 처리 방식이기도 하기 떄문입니다. 그런 처리 방식을 거치게 되어 만일 T1, T2 가 동일한 어떠한 타입인 T를 가지게 되었다면 value 값이 true 가 되게 됩니다. 하나의 트릭과 같은것이죠. 꼼수라고 볼수도 있고 말이지요


그렇다면 실행 결과는 어떻게 되었을까요?



아주 잘 동작합니다, 첫번째 출력은 int 와 float 가 같은 타입인지 비교하는 식이었으므로 false (0) 이 반환되었고, 두번째 출력에 있어 int 와 long 이 같은 타입인지 비교하는 식이었으며 false (0) 이 반환되었습니다. 마지막으로 int 와 int 가 같은 타입인지 비교하는 식이었고 true (1) 가 성공적으로 반환되었습니다. 이런식 입니다. 템플릿 메타 프로그래밍은 쉬우며 우수한 성능을 자랑합니다. 마치 C#의 리플렉션을 C++ 에서 더 싸게 구현하는 느낌입니다.


다음으로 이제 실전에서의 메타 함수들을 보도록 하겠습니다.




<타입을 검사하는 메타함수>


타임을 검사한다는 것은 어떠한 타입이 void 인지 포인터 타입인지, 참조 타입인지, 실수형 데이터 타입인지, 정수형 데이터 타입인지 등을 쉽게 구분할수 있도록 하는 기능을 말합니다. 위 is_same 함수를 이해하신 분들은 어떻게 구현할지 쉽게 파악이 가능하실거라 믿습니다. C++11 표준이나 boost::mpl 라이브러리에 있는 type_traits 의 메타 함수들을 쉽게 도표로 정리 해보자면 이렇습니다.


(도표에 없는 메타 함수는 우리가 직접 구현할수 없거나 매우 힘든 것이기에 뺐습니다, 이를테면 is_class 와 같은 메타 함수는 Visual Studio에 __is_class 와 같은 형태로 컴파일러 확장이 정의되어 있기 때문에 구현이 가능하였고 이를 문법상으로 처리하는건 불가능하기 떄문에 추가를 하지 않았습니다. 혹시라도 방법을 알것 같은 분이 있으시다면 여기 댓글로 다셔도 좋고 C++ 표준 의원회에 보내보시는것도 좋을것 같습니다)




이름


기능

is_void<T>


T 가 void 타입인지 검사


is_null_pointer<T>


T 가 nullptr_t 타입인지 검사


is_integral<T>


T 가 정수형 데이터 타입인지 검사


is_floating_point<T>


T 가 실수형 데이터 타입인지 검사


is_array<T>


T 가 정적 배열인지 검사


is_pointer<T>


T 가 포인터 타입인지 검사


is_fundamental<T>


T 가 정수형 혹은 실수형, nullptr_t 타입인지 검사

 

is_arithmetic<T>


T 가 정수형 혹은 실수형 타입인지 검사


is_compound<T>

 

T 가 정수형 혹은 실수형, nullptr_t 타입을 제외한 모든 타입인지 검사


is_reference<T>


T 가 참조형 타입인지 검사


is_member_pointer<T>


T 가 멤버 객체 (변수) 혹은 멤버 함수 포인터 인지 검사


is_const<T>


T 가 상수 타입인지 검사


is_volatile<T>


T 가 volatile 형식 한정자를 가진 타입인지 검사


is_signed<T>


T 가 부호 있는 수를 가지는 타입인지 검사


is_unsigned<T>


T 가 부호 없는 수를 가지는 타입인지 검사




자, 이 메타 함수들이 우리가 직접 구현해볼 메타 함수들입니다. 너무 많고 장대하다고 걱정하지 마셔도 되는것이, 대부분 is_same 과 같은 메타 함수만 작성할줄 알면 충분히 구현할수 있는 내용의 메타 함수들 뿐이기 떄문에 심하게 걱정은 안하셔도 좋습니다 :^)




<is_void, is_null_pointer 메타 함수 구현하기>

먼저 도표로 보여드렸다시피, 이 두 메타 함수는 타입을 체크하는 기능밖에 하지 않습니다. 다른 많은 타입을 체크하는 것도 아닌 그저 단 하나의 타입을 체크하는 것에 불과하기 때문에 구현이 매우 쉽습니다. 사실 저는 저 두 함수의 실 사용 상황을 C++ 프로그래밍을 하면서도 겪어보지 못했기도 했지만 그래도 연습삼아 구현해보는 시간을 가져봅시다.


우선 코드를 직접 봅시다 :





3, 4번째 라인의 true_type 과 false_type 은 그저 매번 값을 나타내기는 귀찮으니 값을 나타내주는 인터페이스와 같은 역활을 합니다. 7, 9번째 라인의 is_same 은 위에서 서술한 그대로입니다. 이제 12, 14번째 라인을 눈여겨서 보시면 되는데 그저 is_void 함수는 템플릿 매개변수인 T 를 받아 void 와 비교하거나 C++ 표준 라이브러리에서 지원하는 nullptr_t 와 비교하는 작업 뿐입니다. 아주 쉬운 작업이지요.


자 이제 출력 값을 한번 보겠습니다. 예상대로라면 순차적으로 0 1 1, 0 1 1 1 이 나와야 정상일 것입니다 :



엥? 예상했던 결과와는 사뭇 다릅니다. 출력 값을 따라 살펴보니 const 한정자를 붙인 타입은 동일한 결과가 아니라는 값을 내놓고 말아버립니다. 이러면 프로그래머가 const 한정자가 붙은 타입에 대한 메타 함수를 더 작성해야 하는걸까요? 아닙니다. 여기서 우리는 하나의 트릭을 더 이용해야 합니다, 바로 const 한정자를 제거해주는 메타 함수를 작성하는 것입니다. 여기서 부터 약간 머리가 아파지는 구현이 나오게 됩니다, 하지만 걱정하지 마세요. 어려워도 독자 여러분들에게는 크게 어렵지 않은 구현이라는 것을 알고 있기 떄문에 바로바로 코드를 보고 설명을 해보는 시간을 가지도록 하겠습니다.


백문이 불여일견이라고 일단 코드를 보도록 하겠습니다 :





우리는 이제 12, 14, 17, 19번째 라인을 잘 지켜봐야 합니다. remove_const 라는 이름의 메타 함수가 추가되었습니다. 이 함수들은 특이하게도 다른 메타 함수와는 다르게 value 와 같은 값을 내놓지 않고 타입을 내놓습니다. 이것이 템플릿 메타프로그래밍의 강점중 하나라고 볼수 있는데 메타 함수의 결과값은 값에서 부터 타입까지 지원이 가능해서 더 유연하다는 점을 들수 있을것 같습니다.


나중에 메타 함수를 이용한 if 문 분기와 같은 고급 기법을 설명해드릴때 remove_const 메타 함수와 같이 타입을 결과로 내놓는 메타 함수를 아주 많이 보게 되실것입니다 :^)


잡담은 줄이고 이제 remove_const 메타 함수의 구현이 어떻게 되었는지 살펴봅시다. remove_const 메타 함수는 말 그대로 const 한정자를 제거해주는 함수인데 이 또한 템플릿 특수화를 이용하여 구현하였습니다. 14번째 라인을 보시면 아실수 있듯이 그 어떤 타입을 받던 const 한정자를 받으면 분기를 할수 있도록 처리가 되었습니다. 쉽게 말해 const T 에서 T 만 때올수 있도록 특수화를 한것입니다.


이제 이를 이용해서 17, 19번째 라인을 보시면 remove_const 메타 함수를 이용하여 입력된 템플릿 매개변수 값을 넘기고 메타 함수의 결과 타입인 내부 타입 type 을 호출함으로써 그 어떤 타입을 템플릿 매개변수로 보냈던 간에 is_same 메타 함수는 const 한정자가 없는 순수한 타입만을 받게 되고 이로써 const 한정자가 붙은 여부를 가리지 않고 타입 분간이 가능하게 되었습니다.




<is_integral, is_floating_point 메타 함수 구현하기>


이제 부터는 살짝 어려워집니다. 곰곰히 생각해봅시다. is_integral은 분명 정수형 데이터 타입인지를 체크하는 메타 함수인데 이를 어떻게 구현해야 할까요? 정수형 데이터 타입만의 특성이 있을까요? 안타깝게도 없습니다. 우리는 이것을 대응되는 타입을 수동으로 전부 구현해야 합니다. 걱정하지 마세요, 그렇다고 거창한것도 아닌 그저 코드 몇줄이면 되니까요. 이럴떄 매크로를 이용하면 되는겁니다.


is_integral 의 구현부를 보도록 하겠습니다, is_floating_point 메타 함수는 is_integral 메타 함수와 구현 방식이 매우 비슷하기 떄문에 생략하도록 하겠습니다. 사실 스크린샷을 찍을려는데 모니터에 넘쳐서.. :



is_same 메타 함수는 지금은 필요가 없으므로 뺐습니다, 12번째 라인을 보면 is_integral 이 아닌 is_integral_base 라는 메타 함수를 먼저 선언해 준 다음에 14번째 라인을 보시면 특수화를 쉽게 매크로로 할수 있도록 define 한 뒤에 16~27번째 라인까지 모든 정수형 데이터 자료형을 특수화 해줍니다. 안타깝게도 이러지 않으면 구현할수 없기 때문에 이런식으로 구현을 해줄수 밖에는 없습니다.


30번째 라인을 보면 is_integral 메타 함수를 정의 해준 다음 is_integral_base 구조체를 상속한 뒤 템플릿 매개변수로 const 한정자를 제거한 타입을 넘겨주고 있습니다. const 한정자가 붙은 여부에 따라서 정수형, 실수형의 여부가 달라지는건 아니기 때문에 제거를 해주고 넘겨줍니다. is_integral_base 메타 함수가 만들어진 이유도 const 한정자를 제거하기 위함입니다.



잘 출력됩니다. 실수형 데이터는 정수형이 아니므로 정직하게 false (0) 을 내놓는 것을 보실수 있습니다. is_floating_point 메타 함수도 별것 없이 위와 동일한 방법으로 float, double, long double 과 같은 타입을 특수화 하는 것으로 쉽게 구현이 가능합니다.




<is_fundamental, is_arithmetic, is_compound 메타 함수 구현하기>


이번도 뭔가 이름이 길고 거창해 보이실수 있으시겠지만 이 세가지 메타 함수는 동일한 조건이 있습니다. is_arithmetic 메타 함수는 그저 타입이 정수형, 실수형일때 값을 반환 하는것이며, is_fundamental 메타 함수는 is_arithmetic 에 nullptr_t 를 검사하는 로직을 추가해주는 것과 동일하다고 생각하시면 됩니다. 마지막으로 is_compound 메타 함수는 정수형, 실수형, nullptr_t, void 가 아닌 모든 타입일때 true 를 반환하는 간단한 함수입니다.


이를 어떻게 구현해야 할지, 방금 전처럼 매크로를 써야 하나 생각 하시는 분들도 있을지 모르겠으나 그러지 않고 방금 구현한 코드가 있기 때문에 그것을 재활용 하면 됩니다.


자, 뭔가 많이 생겼습니다. 우리가 봐야할건 64번째 라인부터 76번쨰 라인까지 전체를 다 봐야 합니다. 우선 value_type 이라는 메타 함수는 그저 템플릿 매개변수로 비형식 매개변수로 bool 타입의 값을 받아서 true 일시 true_type, false 일시 false_type 과 동일하게 동작하도록 구현한것입니다.


비형식 템플릿 매개변수란?


템플릿 매개변수는 타입 뿐만 아니라 정수형 데이터 값도 받을수 있습니다. 이를테면 int 와 bool 같은 형식도 받을수 있기 때문에 이를 이용해서 수학적 연산도 가능하며 여러가지 처리가 가능합니다. 이용하는 방법은 쉽게 typename 이 아니라 이용할 타입을 적고 그 뒤에 템플릿 매개변수 이름을 적으면 됩니다.


예) <정수형 데이터 타입> <템플릿 매개변수 이름>


이제 이것을 이용해서 68번째 라인의 is_arithmetic 을 구현하였는데 뭔가 좀 허전합니다. 원래 구현할려던 생각과 같으면 매크로를 무진장 쓰고 있었을 텐데 간단히 한줄이면 쉽게 구현이 가능하게 되었습니다. 먼저 구현한 is_integral 과 is_floating_point 를 이용하여 구현하였고 어차피 두 메타 함수는 타입이 아닌 결과 값을 반환하므로 value_type 메타 함수를 상속하여 false_type, true_type 을 구분할 수 있도록 하였습니다.


그렇게 is_arithmetic 메타 함수의 조건과 맞게 is_integral, is_floating_point 메타 함수를 이용하여 정수형이나 실수형 데이터 타입인지 구분할 수 있도록 하였습니다. is_fundamental 메타 함수는 is_arithmetic 메타 함수와 동일하지만 nullptr_t 혹은 void 인지 확인하는 로직이 추가되므로 is_null_pointer 와 is_void 메타 함수를 추가하여 구현할수 있습니다. 


is_compound 메타 함수는 정수형, 실수형, nullptr_t, void 를 제외한 모든 타입인지를 검사하는 메타 함수 이므로 그저 is_fundamental 메타 함수의 결과 값에 NOT 연산자를 부여해서 값을 뒤집어 value_type 메타 함수에 넘겨 줬습니다.


생각해보면 value_type 은 비형식 템플릿 매개변수를 받는 메타 함수 이며 지금까지 구현한 모든 값을 반환하는 메타 함수는 오직 true, false 만을 반환 하였으므로 그를 이용하여 OR 연산자를 이용해 조건을 만들어줌으로서 코드를 재사용 할 수 있었던 것입니다. 결국에 value_type 메타 함수로 전달되는 값은 하나일테고 OR 연산자의 결과이기 때문입니다.






예상했던 대로 아주 잘 처리가 됨을 알수 있습니다. 이런 처리를 하고도 타입을 체크하는데 드는 부하는 0 이며 사실상 컴파일 타임에 모든 연산이 이루어 지기 때문에 실행시간에 발생하는 부하는 없다고 봐도 무방합니다.




<is_pointer, is_reference, is_const, is_volatile, is_signed, is_unsigned 메타 함수 구현하기>


뭔가 많아진것 같지만 기분탓 일겁니다. (..) 하지만 상기된 6가지 메타 함수는 모두 구현 방법이 비슷해서 그렇게 크게 어렵지는 않을 것입니다. 그저 특수화를 이용해서 어떤 한정자나 키워드가 붙어있는지 알수 있으므로 구현 방법은 오히려 매우 쉽다고 볼수 있을 정도의 메타 함수이니 잘 따라와 주셨으면 좋겠습니다 :^)





뭐..뭔가 길어보이지만 기분 탓 일겁니다. is_pointer 메타 함수는 인터페이스가 있는데 이는 is_integral 때와 동일하게 const 한정자를 제거하기 위함이며 템플릿 특수화를 이용해 모든 타입에 대하여 포인터 연산자(기호)가 붙어있는 타입의 경우 포인터로 판별하도록 구현하여 포인터 타입인지 체크하기가 용이해졌습니다.


is_reference 메타 함수는 약간 특이한데, 독자 분들중 몇몇 분들은 '어? 왜 & 가 두개 붙는 것도 있지?' 하시는 분들도 있으리라 생각합니다. 그 이유로는 rvalue reference, lvalue reference 에 대한 대응이며 rvalue reference 타입일 경우 T&&, lvalue reference 타입일 경우 T& 로 템플릿 매개변수에 추가가 되므로 그렇게 처리하였습니다.


is_const 와 is_volatile 메타 함수는 그저 특수화를 이용하여 is_pointer 와 동일하게 const 제한자가 붙어있는 모든 타입에 대하여 true 를 반환하도록 작성하였습니다.


이제 대망의 is_signed, is_unsigned 차례인데 106, 113번째 라인을 보면 아실 수 있듯이, value_type 을 이용해 구현을 합니다만 템플릿 매개변수 T 로 받은 자료형을 -1 과 0 으로 초기화 하여 임시 변수를 만들어 비교함으로서 signed, unsigned 인지 판별하도록 구현할 수 있습니다. 만일 타입이 signed 라면 -1 보다 0 이 당연히 클것이고 unsigned 라면 -1 은 0 보다 크기 때문에 이를 이용한 하나의 트릭과 같은 방법입니다.


그리고 unsigned, signed 를 판별하는 메타 함수에 클래스나 기타 잘못된 정보가 들어오면 안되므로 _is_arithmetic 비형식 템플릿 매개변수를 이용해 T 로 넘겨받은 타입이 정수형이나 실수형이 아니라면 false 를 반환하도록 구현할 수 있습니다.




잘 출력 됩니다. is_signed, is_unsigned 구현부에서 정수형, 실수형 데이터 타입 이외의 타입은 false_type 을 상속 받게 했는데 잘 작동되나 싶어 signed, unsigned 를 상속한 열거형의 타입을 넣어 봐도 false_type 으로 나오니 아주 성공적입니다.




<is_member_pointer<T> 메타 함수 구현하기>


이 메타 함수는 어떠한 클래스나 구조체 내부에 있는 멤버 함수나 멤버 변수임을 판단하는 메타 함수 입니다. 우리가 구조체나 클래스의 멤버 포인터의 타입을 이용해 매개변수를 만들때와 같은 상황을 생각해보면 하나의 규칙이 있음을 알게 됩니다. 바로 타입으로 사용할 멤버를 소유하는 클래스나 구조체의 명을 앞에 써준 후에 :: 연산자를 이용하게 되는데 이를테면 int foo::* 이렇게 정의를 한다면 foo 클래스 혹은 구조체에 있는 int 형 변수의 포인터가 됩니다. 


이를 이용해서 구현해준다고 생각하면 매우 쉽게 구현할 수 있습니다 :




구현 방식은 대략 위에서 설명한 내용과 동일합니다. 122번째 라인을 보시면 일단 모든 타입을 받게 메타 함수를 정의 한 후, 124번째 라인의 정의에 따르면 만일 템플릿 매개변수로 받은 타입이 멤버 포인터와 같은 형식이라면 122번째 라인이 아니라 124번째 라인에서 정의한 메타 함수가 호출되게 되며 true_type 을 상속하는 is_member_pointer_base 메타 함수가 호출되게 되는겁니다.


잘 작동 하는지 결과를 보자면.. :






아주 잘 작동하는 것을 보실수 있습니다. 




<is_array<T> 메타 함수 구현하기>


이 메타 함수는 위 도표에 나와있는 그대로 정적 배열인지 판단하는 메타 함수 입니다. 동적 배열은 포인터이기 때문에 확인이 불가능하지만 동적 배열은 확인이 가능하고 심지어는 정적 배열의 인덱스 까지 확인할수 있는 메타 함수를 구현할 수도 있습니다. 이는 후설토록 하겠습니다. 




코드를 보시면 아실수 있으시겠지만 135번째 라인을 보시면 템플릿 특수화를 이용하여 따로 배열의 크기를 받는 arrSize 라는 이름의 비형식 템플릿 매개변수를 이용하여 주솟값 연산과 같은 번거로운 작업을 거치지 않고서도 정적 배열의 크기를 얻어올 수 있습니다. 133번째 라인은 크기가 지정되지 않은 정적 배열을 타입으로 받아올수 있도록 구현한 것이며 문법상 틀린 부분이 없기 때문에 잘 동작합니다.



잘 작동하는 것을 확인할 수 있으며 객체던 기본 자료형 타입이던 여부를 떠나서 모든 정적 배열에 동작이 됨을 확인 할 수 있고 크기가 지정되지 않은 정적 배열 형식도 처리할 수 있음을 확인할수 있습니다.




마치며..

연재를 시작하게 됬을때부터 누차 말씀드린 내용이지만서도 템플릿 메타프로그래밍은 굉장히 프로그래머로써, 효율로써 상당히 이득인 기술이기 때문에 익혀두시면 분명히 도움이 될거라고 저는 믿어 의심치 않습니다. 위에 적어놓은 내용을 보시고 직접 따라 구현해보시면서 템플릿 메타프로그래밍에 대한 이해를 넓히셔서 자신만의 코드를 작성해보는 연습을 꼭 하시기 바랍니다. 제가 위에 적어놓은 메타 함수들 뿐만 아니라 메타 함수의 종류는 그 쓰임새에 따라 굉장히 많기 때문에 이를 잘 습득하고 상황에 맞게 구현하는게 굉장히 중요하다고 생각합니다.


새벽 4시에 쓴글인데 7시가 다되서야 끝났네요. 원래는 메타 함수 1~2개씩 연재글을 나눠쓸까 하다가 그건 너무 괴씸한것 같아서 이렇게 한꺼번에 써봅니다. 다음 글 부터는 if_ 나 템플릿 메타프로그래밍을 이용한 수학 연산 라이브러리를 작성하고 그에 대한 내용을 써보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다.






  1. 메타 함수란 이전 글에서 볼수 있었던 is_int 와 같은 객체를 메타 함수라고 합니다. 컴파일 타임에 호출되어 처리될수 있는 하나의 함수와 같은 기능을 합니다. [본문으로]
반응형
,
Posted by 터너 (TerNer)
[이전 글]


템플릿 메타프로그래밍 (Template Metaprogramming)

https://ko.wikipedia.org/wiki/%ED%85%9C%ED%94%8C%EB%A6%BF_%EB%A9%94%ED%83%80%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D - 위키피디아 '템플릿 메타프로그래밍'

템플릿 메타프로그래밍은 말 그대로 C++ 의 템플릿을 하나의 프로그래밍 언어로 생각하여 프로그래밍 하는 것이라고 보는게 좋습니다. C++ 의 주요 기능인 템플릿은 OS나 아키텍처에 종속되는 기능이 아니기 때문에 크로스 플랫폼에도 용이하기도 하면서 튜링 완전한 점에 있어서 아주 다양한 기능을 작성할수 있다는 점에 주목해야합니다.

위 코드는 템플릿 특수화를 이용하여 가장 간단하게 구현해본 템플릿 인수로 넘겨진 타입이 int 인지 식별하는 메타 함수 'is_int'를 구현해본 코드입니다. is_int 의 구현부를 보면 아무리 봐도 런타임에 관여할 부분은 없다고 생각이 드실것 같다고 생각된다면 맞습니다. is_int 코드는 절대 런타임 오버헤드가 발생하지 않습니다. 그저 컴파일 타임에 정해지는 값이기 때문입니다. 이처럼 템플릿 메타프로그래밍은 런타임에 관여하지 않고 컴파일 타임에 연산을 수행하기 때문에 RTTI 보다는 저렴하며 여타 OS를 지원하기도 굉장히 쉽습니다.

하지만 천하의 템플릿 메타프로그래밍에도 치명적인 단점은 존재합니다. 예를 들자면 이렇습니다 :

  • 가독성에 있어 심하게 문제가 발생합니다.

'엥? 그냥 구조체에 열거자 넣은게 뭐가 어때서..' 하시는 분들이 있으실지도 모르지만 나중에 배우시게 될 if_ 와 같은 템플릿 메타 함수는 구현하는데에는 큰 문제가 없지만 메타 함수를 호출하는데에 있어 크게 문제가 발생합니다. 곧 그 가독성의 블랙홀에 빠지시게 될것입니다. (너무 가독성의 구멍이라 헤어나올수가 없..)

  • 컴파일러에 심하게 의존적입니다.
Visual Studio 같은 경우 복잡도가 일정 이상 넘어가면 컴파일이 되지 않는 오류가 발생하는 경우가 있고 위 위키피디아 링크에서 보실수 있듯이 컴파일러마다 템플릿을 사용하는 방법이 달라서 문제가 발생하기 쉽습니다. 하지만 걱정마세요. boost 라이브러리는 그 어떤 컴파일러든 구동할수 있도록 프로그래밍할수 있다는 것을 보면 우리도 컴파일러의 제한에 영향을 받지 않는 코드를 작성할수 있을테니까요. :^)
  • 컴파일 시간이 증가할 수도 있습니다.
위에서 언급했다시피 템플릿 메타프로그래밍은 컴파일 타임에 연산을 수행하기 떄문에 컴파일 시간이 증가하는건 당연한 일 일지도 모릅니다. 중소규모 프로젝트에서는 이런 점이 크게 작용하지는 않지만 대규모 프로젝트의 경우 이 점이 큰 변수가 될수 있다는 점을 잊어서는 안됩니다.
  • (연산의 경우) 모든 연산은 상수식일 수 밖에 없습니다.
간단한 예로 어떠한 수의 팩토리얼이나 제곱의 수를 구하는 연산의 경우에는 적어도 컴파일 타임에 컴파일러가 예상할수 있는 값으로 연산을 하기 떄문에 런타임에 다른 값을 연산하는 것과 같은 행위는 할수 없습니다. 이런 점에 있어 템플릿 메타프로그래밍은 C++11 의 constexpr과 매우 유사한 형태를 가집니다.

모든 이치가 다 그렇듯이, 템플릿 메타프로그래밍도 양날의 검이라 불리울 만큼 장단점을 모두 갖추고 있는 모더니즘 설계 방식입니다. 만약에 독자분이 이를 감수하고 이용할수 있다면 이용하는 것이 굉장히 이로울 수 있습니다. 조금의 클럭사이클도 아껴야 하는 상황이라면 선택이 불가피할 수도 있을것이고 말이지요.

다음 글에서는 템플릿 메타프로그래밍을 실제로 사용하는 법과 직접 구현해보도록 하겠습니다. 정적 다형성(static polymorphism)을 이용한 분기나, 템플릿 타입을 가지고 노는 여러 메타 함수 부터 시작하여 템플릿 메타프로그래밍을 실전에 써먹어보면서 독자분들로 하여금 영감을 얻으실수 있도록 도와드리는 시간을 가질수 있도록 연재해보겠습니다.

반응형
,
Posted by 터너 (TerNer)

안녕하세요! 이번에 게임 개발 포에버에서 연재를 하게 된 게임 그래픽스 프로그래머를 꿈꾸는 지망생 터너라고 합니다. 비록.. 필드 경험은 없지만 5년 동안 달려온(..) 결과로 얻은 프로그래밍 노하우와 지식들을 한번 공유해보자 하여 이렇게나마 연재를 하게되었..는데 그릇이 워낙에 작아서 도움이 되실법한 글을 작성할수 있을런지는 모르겠지만 잘 봐주셨으면 좋겠습니다. :^)

연재할 내용은 Modern C++ Style 위주로 작성 될것 같고 보시는 내내 도움 되셨으면 좋겠습니다. 앞으로 연재할 내용의 오탈자나 이상한 부분은 댓글로 직접 문의하셔도 좋고 제 블로그(http://cloudcie.blog.me/)에서 문의해주셨으면 하는 바램입니다.




시작하며..

C++은 (물론 다른 언어도 그렇지만) 템플릿이라는 강력한 무기를 가지고 있습니다. 우리는 이 템플릿과 포인터를 이용하여 엄청난 기능들을 구현할수 있고 또 프로그래머로 하여금 실수를 줄일수 있는 기능을 만들수 있다는 점에 주목해야 합니다. run-time에 처리할 연산을 compile-time에 처리하여 실행시간을 최소화 시키는데 도움을 주는 템플릿 메타프로그래밍이나, 그를 이용하여 프로그래머는 컴파일 타임에 분기점을 두는 정적 다형성과 같은 유용한 기능들을 구현하고 이용할 수 있습니다.

또한 이런 컴파일 타임의 처리에 중점을 두는 방식이 오래전부터 Modern C++ 스타일로 자리를 잡아가고 있으며 실제로 C++11 에서는 type_traits와 같은 템플릿 메타프로그래밍 라이브러리를 표준으로 채택하기에 이르렀습니다. 제 개인적인 견해로는 꼭 Modern C++ 스타일을 따를 필요는 없지만 좀더 도움을 줄수 있는 형식에 기반을 맞추는것이 도움이 많이 되지 않을까 싶기도 합니다. 이러한 방법은 런타임 연산 시간에 안좋은 영향을 미치는 경우는 거의 없을 것이기도 하니까 말이죠.

제 연재글에서는 기초적으로 boost 라이브러리의 내부 템플릿 메타프로그래밍 라이브러리인 mpl의 if_ 나, is_scalar 와 같은 메타 함수들을 직접 구현해보기도 하면서 템플릿 메타프로그래밍에 대한 기초적인 지식을 좀 더 익혀보고, 의문을 풀어보면서 연재글의 제목처럼 Modern C++ 을 애용하게 되는 길을 한 발자국씩 걸어가는 길을 걷게 될것입니다.

(※ 저도 이 연재 글을 작성하면서 틀린 점이 많을수 있다는 것을 상당히 확신하고(?) 있기 때문에 독자 여러분들도 제 글을 읽어보시면서 저와 함께 토론도 하면서 알아가는 장을 마련해나갈수 있었으면 좋겠습니다 :D)



반응형
,
Posted by ozlael

게임은 어떻게 만드나요?

라는 질문에 뭐라고 대답해야 할까요? 누군가는 어떤 말을 해야할 지 고민을 할 테고, 누군가는 쌍욕부터 할 지도 모르겠습니다. 어찌되었건 이런 질문을 받았을 때 공통적으로 느끼는 감정은 '혼란'일 것입니다. 대답해주려니 막막하고 어디서부터 풀어나가야 할 지 혼란스러워진다는 것입니다. 질문을 받은 사람이 이러한 감정을 느끼는 원인은 간단합니다. 바로 질문 자체가 막연하기 때문이죠.

제가 뜬금없이 이런 질문을 언급하는 것이 의아하실지도 모르겠습니다. 이런 질문을 하는 사람이 있을까 싶으시겠지만 실제로 인터넷 커뮤니티(특히 뇌입어)에 많이 올라오는 질문 중 하나입니다. 사실 이러한 비(非)개발 커뮤니티는 이런 허접한 질문이 올라올 수도 있다는 생각이 들 수도 있겠습니다. 하지만, 중요한 것은 개발 관련 포럼이나 커뮤니티에도 이러한 수준의 질문이 가끔 목격된 다는 것입니다. 제가 다소 공격적으로 "수준"이라는 표현을 사용했지만, 여기서 말하는 "수준"이라는 것이 별 뜻은 아닙니다. 질문 내용의 기술적인 난이도가 낮아서 수준이 낮다고 표현하는 것이 아닙니다. 기술적인 수준은 낮아도 질문 자체의 수준이 높은 경우는 얼마든지 많습니다. 제가 말하는 "수준이 낮다"고 표현하는 것은 질문 내용에 성의가 없기 때문입니다. 

예를 들어서 누군가 다음과 같은 질문을 올렸다 생각해보겠습니다.

광선검은 어떻게 만드나요?

이 질문이 뭐가 문제인지 의아해하시는 분도 계실지도 모르겠습니다. 하지만, 제가 보기에는 이 질문이 앞서 언급하였던 게임을 어떻게 만드냐에 대한 질문과 다를 바가 없어보입니다. 마찬가지로 막연하고 정보가 부족하기 때문입니다. 이로 인해서 받아들이는 사람 마다 질문의 의도를 각자 다르게 해석하고 각자 다른 초점의 정보를 말해주게 될 것입니다. 

"광선검"이라는 단어를 들으면 어떤 사람은 스타워즈의 라이트세이버를 떠올릴 것이고 어떤 사람은 건담의 빔샤벨을 떠올릴 것입니다. 

어떤 사람은 모바일 타겟으로 파티클로 빔샤벨을 만드는 방법을 안내할 것이고 어떤 사람은 PC 타겟으로 이미지 블룸 효과를 처리하는 방법을 안내할 것 입니다. 어떤 사람은 그래픽 리소스를 제작하는 방법을 안내할 것이고 어떤 사람은 손잡이에서 빔이 나오는 에니메이션 처리를 수행하는 스크립트를 안내할 것입니다. 이처럼 질문부터가 모호하고 정보가 없으면 질문자와 답변자 모두에게 만족으러운 결과를 도출하지 못하게 될 것입니다. 이는 당연한 결과입니다.

하지만, 의외로 이러한 모호한 질문들이 생각보다 많이 발견됩니다. 물론 예를 들어 말씀드린 질문은 극단적인 예일 뿐이고 사실 더 많은 정보를 포함한 질문들이 대부분입니다. 하지만 여전히 적은 정보만으로 모호한 질문을 물어보는 글들이 많이 보이곤 합니다. 비단 유니티 관련 포럼 뿐 아니라 언리얼, 코코스 등 다른 엔진 관련 포럼에도 나타나는 현상들이고 게임 뿐 아니라 개발 관련 된 커뮤니티라면 어디에서든지 존재하는 현상들입니다.

때문에 질문을 올리는 방법에 대한 가이드를 말씀드리고자 합니다. 물론 이 글이 객관적인 것은 아니며 어떤 기관에서 표준으로 인정한 양식도 아닙니다. 단지 제 개인적인 경험과 노하우를 통해 말씀드리는 것이니 절대적인 가이드라기보다는 참고사항으로만 생각해주시면 감사하겠습니다. 


육하원칙

기본적으로, 육하원칙(5W1H)을 생각하시면 됩니다. 누가,언제,어디서,무엇을,어떻게,왜 육하원칙은 초등학교에서  기본적으로 배우는 만큼 기초적이고 중요한 원칙입니다. 대부분의 질문은 이 육하원칙에서 한가지를 도출하는 과정이 될 것입니다. 어떠한 현상이 왜 생기는지를 물어보고자 한다면 나머지 다섯가지인 누가,언제,어디서,무엇을,어떻게에 대한 정보가 제공이 되어야 할 것입니다. 반대로 어떻게 해야하는지를 물어보고자 한다면 나머지 다섯가지인 누가,언제,어디서,무엇을,왜가 제공이 되어야 할 것입니다. 무조건 이 여섯가지 요소를 반드시 지켜야 한다는 것은 아닙니다. 질문에 따라서 모든 요소가 들어갈 필요는 없는 경우도 있습니다. 다만, 가능한 이 원칙에 입각하여 질문을 작성한다면 크게 모호해질 일은 없을 것입니다. 물론, 이 요소들을 명시적인 도표로 만들어서 작성하거나 하라는 것이 아닙니다. 글의 문맥 안에 이러한 요소들이 명시적이나 암묵적으로 포함되기만 하면 됩니다. 사실, 각각의 요소들에 대한 명확한 기준은 없습니다만 각각의 요소에 주로 담기게 되는 내용을 설명드려볼까 합니다.


누가(who)

"누가"는 대부분 질문자 본인이라 생각 할 수도 있습니다만 당연히 질문자가 이름이 뭔지 거주지가 어딘지따위는 중요하지 않습니다. 중요한 것은 직군이 어디인지 지식 수준이 어느정도 인 지 입니다. 그래야 어느정도 레벨까지 설명을 해 줘야 할 지 가늠할 수 있기 때문입니다. 다만 이러한 것은 질문 내용에서 간접적으로 표현 되는 경우가 많아서 명시적으로 언급해주지 않아도 되는 경우도 많습니다. 예를 들어서 NavMesh.Raycast()의 성능에 대해 물어는 글이 있다 치면, 그 질문의 작성자는 일단 프로그래머이일 것이며 네비게이션 메시에 대한 기본 지식은 가지고 있다고 가정해도 무방할 것입니다. 또한, "누가"는 사람에만 국한 되는 것이 아니라 시스템의 객체가 될 수도 있고 오브젝트의 인스턴스가 될 수도 있는 등 다양한 시각으로 반영될 수 있습니다.


언제(when)

"언제"는 몇시 몇분 몇초의 절대적인 시간이 아니라 재현 시퀀스의 타이밍이 되는 경우가 대부분입니다. "버튼을 세번 눌렀을 때" 혹은 "앱이 수행되자마자 항상" 등 재현 시퀀스상의 시간이 될 수도 있습니다. 때문에, "어떻게"와 같이 묶여서 표현 될 수도 있습니다. 


어떻게(how)

그렇다고해서 "언제"와 "어떻게"가 동일하다는 이야기는 아닙니다. "어떻게"는 주로 구현 방법에 대한 설명이 되는 경우가 많습니다. 어떤 기능을 어떻게 구현했냐에 대한 설명이 대부분일 것입니다. 주로 이 부분에 대한 설명이 자세할 수록 좋습니다.

구현을 어떻게 했고 무엇이 문제인 지에 대한 설명은 사실 말로만 설명하기에는 부족한 경우가 많습니다. 이러한 경우에는 보조적인 자료를 사용해주는 것이 좋습니다. 특히 코드에 관한 것을 물어볼 때에는 반드시 소스 코드가 포함되어야 합니다. 무슨 함수를 어떻게 썼다라는 것을 말로만 설명하는 것이 아니라 해당 코드를 반드시 올려주어야 합니다. 더 나아가서 프로젝트를 올려주시면 정확한 판단을 하기가 훨씬 수월해집니다. 포럼 사이트나 게시판마다 파일 업로드 용량 제한이 달라서 프로젝트 파일을 직접 올리지 못하는 경우가 대부분입니다. 구글 드라이브나 드랍박스에 파일을 올린 후 링크를 걸어두시는 식으로 하면 여러 커뮤니티에 질문을 올릴 때 유용합니다. 물론 프로젝트를 올려두었다 하더라도 문의하고자 하는 부분의 핵심 코드는 본문에 포함되어야 합니다.


어디서(where)

"어디서"는 말 그대로 문제가 발생하는 위치입니다. PC나 모바일 등 물리적인 위치가 될 수도 있고, 어느 클래스 어느 함수에서 발생하는 지 등의 논리적인 위치가 될 수도 있습니다. 위치 역시 최대한 자세히 적어주시는 것이 좋습니다. 예를 들어서 모바일 기기에서만 발생하는 문제라면 단순히 "모바일 기기"라는 표현으로 끝내는 것이 아니라 해당 기기의 모델명과 OS 버젼 등은 필수적으로 기입되어야합니다.


무엇을(what)

"무엇을"은 6가지 요소 중 가장 중요한 요소가 될 수도 있습니다. 구현하고자 하는 것이 무엇인 지 혹은 문제가 발생하는 현상이 무엇인 지 등 원하는 것이 정확히 기술되어야 합니다. 

어느날 사장님이 오셔서 이런 말을 했다고 가정해보겠습니다.

보스를 추가하고싶은데 메카닉이면서도 허접해보였으면 좋겠어. 또한 모던하면서도 클래식한 느낌이 나야해

이 말만 툭 던지고 자리로 돌아간다면 사장이고 뭐고 다 때려엎고 싶을 지도 모릅니다. 설명이란 것은 자세하면 자세할 수록 좋습니다. 여기서만큼은 과유불급이란 것은 존재하지 않습니다. 하지만 사실 아무리 말로 잘 표현하더라도 정확한 표현을 하는데에는 한계가 있을 수 있습니다. 백마디의 말 보다는 한장의 이미지가 더 효과적이라는 것을 항상 염두해두시길 바랍니다. 이까 그 사장님이 다시 돌아와서 다음과 같은 이미지를 던져주고 간다면 그나마 화가 조금은 풀릴 지도 모르겠습니다.

왜(why)

"왜"는 불필요한 정보라고 느낄 수도 있겠지만 이 역시 상당히 중요한 정보입니다. 만일 특정 함수의 오류에 대해 문의하는 것이라면 왜 그 함수의 사용 의도가 파악되어야 합니다. 그래야만 의도하는 방향 내에서 해결책을 제시할 수 있습니다. 또한, 용도에 맞지 않는 사용법으로 사용하는 것을 막을 수도 있습니다. 혹은, 더 나은 방법이 제시 될 수도 있습니다. 예를 들자면, 주기적인 타이밍으로 들어가는 도트데미지를 Update() 함수에서 구현하는 방법에 대한 문의를 한다면 코루틴을 사용하는 것으로 제안 할 수도 있을 것입니다.


아웃라인(outline)

육하원칙 외에도 글의 아웃라인을 서론과 본론으로 갖춰주시는게 좋습니다. 글을 자세히 적다보면 글이 길어질 수 밖에 없는데 서론 없이 바로 본론으로 들어가버린다면 질문에 대한 집중력이 떨어질 수 밖에 없습니다. 그렇다고 너무 아웃라인의 형태에 집착하지 않아도 됩니다. 글의 도입부에서 어떤 주제에 대한 질문인지 대략적으로 밝히고 진입하는 정도만 되어도 충분한 아웃라인을 갖추었다고 봐도 무방합니다.


퇴고

사실, 육하원칙이니 글의 아웃라인이니 그런 것 보다는 퇴고가 가장 중요합니다. 글을 길게 썼든 짧게 썼든 글을 최종 업로드 하기 전에 퇴고를 반드시 거쳐주시길 바랍니다. 제3자의 입장으로 글을 다시 한번 읽어가면서 내용이 이해가 가는지를 판단해보십시요. 의외로 자신이 글을 이해가 가지 않게 썼다는 것을 발견 하는 경우가 많을 것입니다.

이렇게까지 많은 것을 고려하면서 질문글을 써야하나 하는 의문을 제기하는 분도 간혹 계십니다. 물론 질문이란 것이 가볍게 던질 수도 있는 것입니다. 다만 자세한 질문을 할 수록 더 나은 답변과 토론이 오갈 수 있을 것이라 생각합니다. 또한, 질문을 적는 사람은 개인이지만 질문은 많은 사람들이 읽습니다. 더 많은 사람들이 더 많은 시간을 투자해서 함께 고민하는 것이기 때문에 질문 글에도 그만큼의 정성이 들어가는 것이 예의라고 생각합니다.


다시 광선검

마지막으로 다시 광선검을 예로 들어보겠습니다. 

광선검은 어떻게 만드나요?

라는 표현은 다음과 같이 바꿀 수 있겠습니다.(실제의 문의 내용이 아니라 제가 가상으로 작성한 글입니다.)

안녕하세요. 광선검의 검흔 효과 구현에 대한 조언좀 구하고자 문의글을 올립니다. 저희가 모바일 액션 RPG를 만들고 있습니다. D&D나 던파같이 사이드뷰인데 배경이 미래다보니 스타워즈 광선검이 무기로 등장합니다. 광선검 자체를 만드는 것은 문제가 없었습니다만 검흔 효과를 만드는데 애를 먹고있습니다.

검광 부분은 막대기 모델에 따라서 검광 텍스쳐 판때기 2개를 X자로 교차되게 간단하게 만들었습니다. 케릭터에게 쥐어줘보니 뭐 그럴싸합니다. 모바일이라 화면도 작은데다가 광선검이 클로즈업되서 보일 일이 없다보니 판때기로 박은게 티가 잘 안납니다. 근데 단순히 이것만 가지고 검을 휘둘러보니 뭔가 어색합니다. 영화에서 보면 검을 휘두르면 삼각형 모양의 블러가 생깁니다. 게임에서 이런게 안생기니 심심한 느낌입니다.

저는 단순 모델러라 이펙트를 잘 모릅니다. 신생팀이라 이펙터도 따로 없구요. 그래서 옆자리 프로그래머랑 같이 에셋스토어 뒤져서 trail 이펙트를 붙여봤는데 영화에서의 느낌은 나지 않네요. 그냥 일반적인 무협 게임의 검기같은 느낌일 뿐 광선검 특유의 느낌이 나지 않습니다. 검흔 자체가 불투명이어야하는데 빨간 화살표처럼 투명 그라데이션이 들어갑니다. 전체적인 헤일로 효과 없이 노란색 화살표처럼 검 진행 방향의 단면이 선명하게 떨어져버리구요. 영화에서는 순간적으로 검의 모양이 부채꼴로 변하는 듯한 느낌이라 이 이펙트와는 전혀 다른 느낌입니다. 

영화에서의 느낌을 내려면 어떤식으로 구현해야할 지 아니면 어떤 에셋을 사용해야 할 지 조언좀 부탁드립니다. 혹시 몰라서 파일을 올려두었습니다. 프로젝트라 광선검 부분만 따로 패키지로 추출한 점 양해 부탁드립니다.

다운로드 링크 : 어쩌고저쩌고

감사합니다 :)


P.S. 먹튀는 악질 행위입니다. 가끔 답변을 받으면 답변만 확인하고 질문들을 삭제하는 분도 계시는데 이러한 행위는 비매너 행위입니다. 커뮤니티는 다른 사람들과 정보와 노하우를 공유하는 공간입니다. 혼자만 알고싶으면 애초에 커뮤니티에 질문을 올리는 것 부터가 모순이라는 점을 말씀드리고 싶습니다.


로보트 이미지 출처 : Stephane Halleux

게임 trail 효과 이미지 출처 : SWTOR



반응형
,