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

포프TV 시작하면서 첫 비디오로 올렸던 건데.. 게임데브 포에버에는 포프TV를 한 번도 공유한적이 없어서 올립니다. 


요새는 시간좀 아끼려고 blog를 대신 주로 비디오(vlog)를 만드는데... 이게 gamedevforver에 올릴만한 성격인지 아닌지 판단이 잘 안서서 여태까지 여기엔 공유를 안했습니다. 앞으로도 어떨지는 잘 모르겠구요...


고로 제 비디오가 맘에 드시면 비디오 오른쪽 위에 있는 아이콘 누르셔서 구독해두세요. 그럼 새 비디오가 올라올때마다(매주 2개) 유튜브에서 보실수 있을겁니다.




"프로그래머가 되고 싶은데 수학을 못해요"라는 질문에 대한 답...


제가 수학성적을 60점에서 100점으로 올린방법은 아래 링크에 있습니다.


http://kblog.popekim.com/2012/05/60-100.html

반응형
,
Posted by 김포프

다신 안볼줄 알았는데.... -_-  요즘들어 다시 복셀(voxel) 에디팅에 대해 잠시 본 바.... 이 기회에 제가 이짓하면서 배운 교훈들을 짧게(정말?) 공유해볼까 합니다. 저번에 썼던 "혼합가능한 RGBM" 글 과는 달리 여기에 나열한 모든 정보/fact 들은 이미 온라인에 다 널려있는 내용입니다. 그냥 그 모든 내용들을 모아 제 의견을 약간 곁들인게 전부입니다.



1. isosurface부터 이해하세요

복셀 데이터를 주무르는 가장 실용적인 방법은 각 복셀마다 iso 값을 대입하는 겁니다. 다른 방법도 시도해봤는데요(물론 제가 발명해낸 기괴한 방법도 한번 시도해봤죠 -_-) 대부분 별로더라구요. 단점들이 있어요. 결국엔 iso 값으로부터 메쉬를 다시 생성해내는게 가장 실용적이고 메모리 친화적인 방법인거 같습니다. iso surface extraction에 대해 설명하는 무수한 논문이 있으나 정작 isosurface가 뭔지를 쉽게 설명하는 자료는 흔치 않더라구요.



여기 저기 뒤진 끝이 이 튜토리얼을 찾았습니다. 설명도 잘해놨구요. isosurface의 개념을 익히는데는 최고인듯 싶습니다. (물론 영어입니다... 이 글도 영문 블로그에 썻다가 한글로 번역하는 놈이기 때문에.. 혹시라도 괜찮은 한글자료 가지고 계신분은 공유좀 부탁 -_-a)



또한 3D Coat의 개발자가 매우 친절하게도 3D Coat에서 사용하는 복셀 데이터의 포맷을 공개해놨습니다. 이 데이터 구조를 들여다보는 것만으로도 복셀 에디팅에 대해 상당히 많은걸 배울 수 있습니다.



복셀 편집에는 큰 관심이 없고 그대신 임시 복셀 데이터로부터 절차적(procedurally)으로 거대한 지형(terrain)을 만드는게 목적이시라면 VoxelFarm inc의 블로그를  읽어보세요. 이 분 정말 천재더라구요. (실제 한번 만나도 봤습니다. 몬트리올 살때 -_-)



2. 복셀데이터는 "간접적" 으로만 변경할 수 있습니다

볼륨 데이터를 표현하기 위해 iso 값을 사용하게 되면 복셀 데이터를 변경할 수 있는 유일한 방법은 iso 값을 변경하는 것입니다. 이 연산들은 간단한 수학인데요. 각 iso 값을 증감하거나 이웃간의 값을 평균내는 등입니다. 물론 distance 함수를 직접 계산해서 새로운 isosurface 모양을 꾸겨넣을 순 있지요. (이 distance 함수에 대해서는 위에 링크를 걸어둔 튜토리얼을 참고해 주세요.)



iso 값과 최종 메쉬의 모양과는 1:1 관계가 없습니다. 즉 "이 edge를 0.2미터 오른쪽으로 늘리고 싶어. 그러니 복셀 값을 0.752로 바꿔야지!"라는 말을 할수가 없어요. 다시 말하면 최종 모양을 세밀하게 조절할 방법이 없습니다. 복셀의 밀도(density)를 아주 높이 올려도 (예: 1센치당 복셀 하나) 역시 마찬가지입니다. 이게 바로 제가 간접적으로만 복셀을 편집할 수 있다고 한 이유죠.



매우 뾰쪽한 edge를 가진 메쉬를 생성하는 새로운 알고리듬들도 있긴 합니다. 하지만 이 방법들은 복셀 편집을 위해 만들어진게 아닙니다. (자세한건 다음 섹션에서....)



3. 전통적인 마칭 큐브(marching cube)면 충분합니다

방금 말씀드렸듯이 iso데이터에서 메쉬를 생성하는 새로운 리서치들이 있습니다. 특히 Cubical Marching SquareEfficient Voxel Octree가 꽤 주목할만한 리서치들인데요.



CMS는 매우 날카로운 edge를 생성할 수 있습니다. 단, 한가지 조건이 있습니다. 폴리곤 데이터로부터 iso값을 capture해야만 하지요. 이 방법은 큐브 표면에서 각 폴리곤 edge가 만나는 거리를 인코딩하는 방법으로 작동하거든요. 근데 실시간으로 이 데이터를 편집하려면(예: C4 engine이나 3D Coat에서 하는 것처럼) 꽤 힘들겠죠?  저도 edge의 교차점을 제대로 보존하면서 이 데이터를 편집할 실용적인 방법을 찾는데 실패했습니다.. ㅠ_ㅠ



EVO는 폴리곤 생성을 하는 기법이 아닙니다. 이건 CUDA에서 레이트레이싱을 하면서 실시간으로 복셀을 화면에 그려주는 기법입니다. 실시간으로 돌리만큼 꽤 빨라요. (보고 꽤 감동받았습니다. 데모한번 실행해보세요 ^_^) 그리고 복셀 데이터를 볼륨이 아닌 표면(2D 비슷한) 데이터로 저장했다는게 꽤 주목할만 합니다. 하지만 똑같은 문제점이 있지요. 이 데이터를 편집할 수 있는 쉬운 방법이 없어요 -_-



즉, 이 새로운 기법들은 모두 폴리곤 데이터를 복셀데이터로 캡춰한 뒤 아무런 수정없이 다시 보여주는데 유용한 방법이란 겁니다.



물론 전통적인 MC와 CMS를 사용해서 메쉬를 생성해내면 결과가 조금 달라집니다. 하지만 폴리곤 데이터를 완벽하게 재현해내는게 목적이 아니라 복셀 데이터 편집이 목적이라면 그 차이점은 무시해도 될 수준입니다. 편집툴에서 고치는 즉시 결과가 보이기만 하면(WYSIWYG) 어떤 방법을 써도 별 차인 없거든요. 하지만 MC가 CMS보다 최소 30% 이상은 빠릅니다. (둘다 최적화를 하지 않은 C# 프로그램에서 테스트해봤습니다. 물론 당연히 멀티쓰레딩은 했구요)



4. 정점버퍼를 최적화하세요

10 x 10 x 10 복셀에서 메쉬를 생성하면 삼각형이 1~2천개 정도 나오는건 예사입니다. 복셀 밀도가 높고 월드가 넓으면, 1500만개 삼각형도 금방 씁니다. 이 엄청난 정점 데이터를 GPU로 보내려면 느리더라구요. 고사양 그래픽 카드와 저사양 그래픽 카드에서 모두 테스트 해봤는데 둘 다 엄청 느렸습니다... -_-



그러니 간단한 정점버퍼 최적화라도 해주세요. 삼각형들 끼리 정점을 공유하고 인접 표면들끼리의 법선을 평균내는 것 등이요. 이러면 정점버퍼의 크기를 30~50% 정도 줄일 수 있습니다.



5. level-of-details을 쓰세요

정점들을 공유하는것 만으론 충분치 않습니다. 1500만개에서 30~50% 세이브해도 여전히 삼각형 수가 800~1000만 정도거든요. LOD 메쉬도 생성해주세요. 복셀 공간에서 메쉬를 생성하려면(아마 그러셔야 할껄요?) 매 n번째 복셀을 샘플링 하는 방법으로 LOD 메쉬를 만들면 됩니다. (LOD1은 매 2번째, LOD2는 매 4번째 등으로요..)



CMS를 사용하면 LOD를 만드는 게 그리 어렵지 않습니다. 이 알고리듬은 교차점 정보를 가지고 있으므로 LOD사이에서 끊김 현상이 없거든요. 하지만 제가 권해드린 것처럼 MC를 사용하신다면 틈이 벌어집니다. 이 틈을 메꾸는걸 아직 시도해보진 않았습니다만.. 만약 그런다면 TransVoxel 알고리즘을 먼저 시도해볼거 같습니다. Don Williams 아저씨도 이 방법을 써서 성공했다더군요.



높은 LOD 폴리곤으로부터 낮은 LOD들을 생성하는 방법도 있습니다. 이러면 복셀 데이터를 직접 사용하지 않아도 되지요. Voxel Farm이 이 방법을 씁니다. 이 방법의 장점이 하나 있는데요. 높은 LOD 메쉬를 재투영(reproject)해서 낮은 LOD 메쉬에 사용할 노말맵을 만들수 있습니다. 하지만 메쉬 단순화(simplification)는 제 입맛에 안맞더군요. 자세한 이유는 역시 다음 섹션에서... -_-



6. 메쉬 단순화(simplication)이 입맛에 안맞을 수도 있습니다

정점 데이터를 최적화 하는 다른 방법으로 메쉬 단순화 기법이 있습니다. Voxel Farm이 이 방법을 사용하고 결과도 그닥 나쁘지 않습니다. 하지만 전 별로 맘에 안들었습니다. 합쳐질 삼각형과 그러지 않을 삼각형들을 제 맘대로 선택할 수 있는 방법이 매우 제약적이기 때문입니다. 이 삼각형들 선택은 어떤 오차계산(error calculation) 알고리듬을 선택하느냐에 따라 다른데요. 모든 경우에 제대로 작동하는 함수를 도무지 찾을 수가 없더라구요.



이 방법이 다른 분들의 용도에 안맞는다는 말은 아닙니다. 그냥 저에겐 안맞았습니다.




자.. 그럼 이정도면 된듯 하니... 즐겁게 복셀을 주무르세요~ 하악x2~ 



포프



반응형
,
Posted by ozlael

많은 모바일 게임들이 카카오톡, 라인, 위챗등 로컬 플랫폼을 통하거나 T스토어, 올레마켓 등 3사 마켓을 통해서 서비스를 진행합니다. 하지만 특정 지역 한정이 아닌 글로벌 타겟으로 진행하는 경우도 많이 있지요. 그러한 경우 전 세계의 유저를 다루기 위해서 국기 이미지를 필요로 하기도 합니다. 유저의 정보라든가 지역 정보등의 UI에서 말이지요.


어느날 갑자기 프로그래머 나부랭이가 국기를 그려달라고 합니다. 열불나서 불꽃 싸닥션 날려주고 싶군요. 전 세계 국가가 220여개가 넘는데 이를 언제 다 그리나요... 아 몰라 배째고 싶지만 그럴순 없으니 이미지 라이브러리들을 뒤져보고 구글님께 문안 인사도 드립니다. 근데 막상 뒤져봐도 딱히 바로 쓸만한게 없네요. 웹용 아이콘으로 제작되서 크기가 굉장히 작거나 쓸데없는 그라데이션이 들어가있기도 하구요. 쓸만한게 있다 쳐도 게임의 UI 스타일에 맞추려면 수작업이 들어 갈 수 밖에 없겠군요. 

어딘가에서 이러한 고민을 하고 계시는 아티스트분께 이 글을 바칠까 합니다


geonames(http://www.geonames.org)에서 가공 안된 퓨어(?)한 국기 이미지를 받을 수 있습니다. 전 세계의 지리 정보를 모아둔 사이트인데 GIS쪽에서 많이 활용하는 데이터베이스인 듯 하더군요. 게다가 무료라는군요(아니라면 알려주세요. 무책임) 보통 우편번호나 지명 등의 정보에 주로 애용되는 듯한데 사랑스럽게도 국기 이미지도 존재하네요. 다음 url 형식으로 접근하면 국기가 나옵니다

http://www.geonames.org/flags/x/(ISO 3166-1 alpha-2 소문자).gif

ISO 3166-1 alpha-2는 두글자로 이루어진 국가 코드를 의미합니다. 주로 co.kr, co.jp 인터넷 도메인 주소에 쓰이는 그 코드요. 예를 들어 코드 부분에 kr로 입력하면 대한민국 국기가 나옵니다.프로그래밍에서도 주로 이 코드를 이용하니까 코더 나부랭이들이 쓰기 편하게 파일명도 이 코드로 저장해주는 아량을 베풀어주세요. 

http://www.geonames.org/flags/x/kr.gif

패턴도 알았겠다 이제 스크립트로 작업을 시작해보면 되겠군요. 일단 이미지를 받아서 파일로 저장하는 방법은 간단합니다. 명령프롬프트(dos터미널)창에서 curl(http://curl.haxx.se/)로 수행하고 그 결과를 파일로 저장하면 되겠습니다.

curl http://www.geonames.org/flags/x/ck.gif > temp.gif

그러면 Cook Islands라는 작은 섬나라의 국기가 temp.gif에 저장됩니다. 이제 국기를 가지고 석고석고돌리고석고할텐데 우리나라 국기를 가지고 장난치기 쪼까 거시기해서 미안하지만 남의 국기로 예를 들도록 하겠습니다. (음.. 예를 들어도 하필 많고 많은 나라중에  쿡섬을 택했는지는 안알랴줌)

그럼 이미지를 받아왔으니 이제 써먹을 수 있게 입맛에 맞게 편집을 해야겠군요. 이제 이 이미지 편집은 지난 포스팅에서 설명 드렸던 image magick를 이용하도록 하겠습니다. 일단 공식 페이지에서 받아서 설치하세요.

그럼 일단 이미지가 604 X 302라는 어마어마한 사이즈라서 크기를 조절해야겠네요. 파일 포맷도 PNG로 써야하구요. 다음과 같이 입력하면 이미지 사이즈가 110x70으로 변경되어 ck.png로 저장됩니다. 근데 조금 고민할게 하나 있는데 국기들의 종횡비가 모두 동일하지는 않다는 것입니다. 그냥 쿨하게 종횡비를 무시하고 사이즈를 고정시켜버립시다. 위에 나와있듯이 사이즈 숫자 뒤에 !를 붙이면 종횡비를 무시합니다. 만일 종횡비를 유지하며 고정 높이 기준으로 맞추고 싶다면 110x70! 대신 x70으로 바꾸시면 됩니다. 반대로 고정 너비 기준으로 맞추고 싶다면 110x70! 대신 110으로 바꾸시면 됩니다.

convert temp.gif -resize 110x70! ck.png

사이즈를 줄이는 과정에서 필터링이 되면서 이미지가 조금 흐려진 느낌이 있네요. sharpen 효과를 추가해줘서 이미지를 선명하게 만들어 주도록 하죠.

convert temp.gif -resize 110x70! -sharpen 0x1 ck.png

 

좌 : sharpen전   우 : sharpen후

음.. 근데 예시로 든 Cook Island 국기 이미지는 문제 없는데 geonames에서 받은 다른 국기 중 이상한 테두리가 있는 것도 있네요. 대한민국 국기(http://www.geonames.org/flags/x/kr.gif)도 이상한 테두리가 있군요. shave 옵션을 줘서 이미지의 테두리를 놈 깎아내기로하죠.

convert temp.gif -shave 2 -resize 110x70! -sharpen 0x1 kr.png

 

좌 : 깎기 전  우 : 깎은 후

이제 이미지 자체에는 큰 문제는 없는데 이쁘지를 않네요. 게임의 ui는 둥글둥글하고 이쁜데 국기는 혼자 못생겨서 맘에 들지를 않네요. 모서리를 둥글둥글하게 좀 깎아주고싶군요. 그럴때는 모서리 부분을 알파로 뺀 마스킹 이미지와 합성시켜버리면 되요.

convert temp.gif -shave 2 -resize 110x70! -sharpen 0x1 -alpha set rounded_corner_mask.png -compose DstIn -composite ck.png

  

좌 : 합성 전  중 : 알파 마스크  후 : 합성 후

아직은 살짝 허전하군요. 이미지 테두리가 필요해보이네요. 테두리 이미지를 오버레이 시키는 방식으로 합성하는것도 추가해보죠.

convert temp.gif -shave 2 -resize 110x70! -sharpen 0x1 -alpha set rounded_corner_mask.png -compose DstIn -composite rounded_corner_overlay.png -compose Over -composite ck.png

  

좌 : 오버레이 전  중 : 오버레이 이미지  후 : 오버레이 후

이제 게임에 가져다 쓸만해졌군요. 근데 여기서 한번 용량 문제도 생각해봐야겠어요. 깃발 이미지 자체는 9KB정도밖에 되지 않아요. 근데 이러한 깃발들이 220개만 생겨도 9KB X 220 = 약 1.9MB라는 엄청난 놈들이 되어버리겠군요. 이미지에 사용되는 컬러 수를 줄여서 이미지 사이즈를 줄일 수 있어요. 컬러수가 줄어들면서 마하밴드가 생길 수 있으니 기왕히면 디더링도 같이 걸어주기로 해요. 64컬러로 제한시켜버리니 이미지 용량이 9KB에서 3KB로 확어드는군요. 애초에 국기들이 보통 이미지가 복잡하질 않아서 컬러를 줄여도 시각적인 차이도 없네요. 근데 사실 이쁘게 되지 않는 이미지도 몇개 생기니 스크립트 돌린 후에 한번씩 쭉 훑어봐주긴 해야해요.

convert temp.gif -shave 2 -resize 110x70! -sharpen 0x1 -alpha set rounded_corner_mask.png -compose DstIn -composite rounded_corner_overlay.png -compose Over -composite -dither FloydSteinberg -colors 64 ck.png

 

좌: true color, 9KB  우: 64 color, 3KB

자, 그럼 이제 다 되었군요. 이제  국가 코드들 루프 쏵 돌면서 이미지들 받아오고 컨버팅하면 되겠군요. 근데 문제는 이 국가 코드 리스트들 중 유효한 글자들을 어떻게 추려내느냐겠네요. 알파벳의 조합 26X26=676개 전부 유효한게 아니라 이 중 220여개만이 실제 할당되어 있기때문이죠. 어떻게 추려내면 될까요?

아 그딴거 몰라요 ㅋㅋ 그냥 쿨하게 26 X 26 루프 다 돌기로 하죠 ㅋㅋ 어짜피 컴퓨터가 힘들지 내가 힘든거 아니잖아요. 아.. 나이 먹으니 실력보다는 잔머리만 쌓이는군요. 유효하지 않은 국가 코드의 이미지를 받아와서 잘못된 이미지는 어짜피 image magick에서 실패하니 상관 없어요. 그럼 bat 확장자로 파일 하나 만들고 다음 코드 복붙해주세요.

SET ALPHABET_LIST=(a b c d e f g h i j k l m n o p q r s t u v w x y z)

for %%i in %ALPHABET_LIST% do (

for %%j in %ALPHABET_LIST% do (

call curl http://www.geonames.org/flags/x/%%i%%j.gif > temp.gif

call convert temp.gif -shave 2 -resize 110x70! -sharpen 0x1 -alpha set rounded_corner_mask.png -compose DstIn -composite rounded_corner_overlay.png -compose Over -composite -dither FloydSteinberg -colors 64 %%i%%j.png

del -f temp.gif

)

)


이제 이 배치파일을 따불클릭해서 뙇! 수행하고 커피한잔 마시고 돌아오면 국기 이미지들이 생성되어 있을겁니다. 한번 쫙 훑어서 검토만 하고 바로 칼퇴근 ㄱㄱ 하시죠.


반응형
,
Posted by 김포프

RGBM이란?

fat 버퍼(채널당 16비트)를 써서 HDR 렌더타겟을 저장하는게 가장 직관적인 방법입니다. 하지만 이러기엔 속도가 너무 느렸고 메모리도 많이 잡아먹었죠. 그래서 보통 HDR을 8비트/채널 버퍼에 인코딩(패킹)하곤 했었습니다. 다양한 패킹 방법들이 존재하는데 아마 그중에서 가장 널리 사용했던건 RGBM 이었을겁니다. (이 링크에는 LogLUV 패킹 방법에 대해서도 잘 나와있으니 잘 모르시는 분은 읽어보세요...무.. 물론 영어 -_-)


패킹이라... 음.. 좋은 놈이지요... bandwidth하고 메모리 둘다 절약해 줬거든요...


RGBM의 단점

근데 문제가 있었습니다.. 아티스트분들이 엄청 좋아하는거 하나를 할 수 없거든요.. 넵. .알파 블렌딩 입니다.. -_-


RGBM버퍼를 사용하면 알파블렌딩을 할 수 없는게 현실입니다. 달리 말하면 투명한 물체들을 못쓴다는거지요... 왜냐구요? RGBM을 사용할때 블렌딩을 하려면 다음과 같은 공식을 돌려야 합니다.


    ( DestColor * DestMultiplier * InvSrcAlpha + SrcColor * SrcAlpha ) / SrcMultiplier


여기서 몇몇 매개변수들을 좀 살펴보지요.

  • DestMultiplier: DestAlpha 채널에 저장되어있습니다. 블렌딩 유닛이 쉽게 사용할수 있지요

  • SrcMultiplier: 셰이더에서 알파채널로 출력(output)합니다

  • SrcAlpha: 투명값입니다. 근데.. 이걸 어디다 저장하죠? 보통 같으면 알파채널이죠.. 근데 RGBM을 사용하면 .... 이미 multiplier를 알파채널에 출력하니... 바.. 방법이..... 아.. 맞다.. "premultiplied alpha" 라는게 있었죠? 셰이더 안에서 SrcColor에 알파값을 미리 곱해주면 됩니다. 대충 해결했군요.

  • InvSrcAlpha: 하지만 바로 여기가 모든게 개판나는 곳입니다. 투명값을 더 이상 출력하지 않으니 InvSrcAlpha를 블렌딩 유닛에서도 쓸수 없고, 이걸 셰이더안에서 DestColor에 미리 곱해줄 방법도 없지요. 넵.. 망했습니다 -_-;


기존의 해결방법

그래서 이 문제를 해결하기 위해 먼 짓을 했냐구요?.... 아무짓도 안했습니다... -_- 그냥 렌더타겟을 하나 더 만들어서 문제를 피해갔지요. 모든 투명한 물체들을 HDR 인코딩 없이 이 새로운 버퍼에 그렸습니다. 그리고 나중에 그 결과를 신(scene) 버퍼에 합쳤지요.


간단히 말해 불투명(opaque)한 물체들은 HDR에 그리고 투명한 물체들은 LDR에 그리는 겁니다. 하지만 이러면 전체화면(fullscreen) 패스를 한 번 더 돌려서 버퍼들을 합쳐줘야 하므로 속도저하의 문제가 있었습니다. 그래서 속도저하를 좀 줄여보고자 투명버퍼의 크기를 절반 또는 1/4로 줄여주는 꼼수를 쓰기도 했지요.


뭐, 이래저래 여전히 RGBM 버퍼만 하나 쓰는것보단 메모리를 더 먹었습니다.


혼합가능한(Blendable) RGBM

자, 그럼 제가 최근에 고안해낸 꼼수를 알려드리겠습니다. 혼합가능한(Blendable) RGBM이라 이름을 붙였구요. 일단 간단히 요약부터 해보죠.

  • 투명한 물체들을 수학적으로 올바른 방법으로 혼합합니다

    • 따라서 별도의 메모리를 필요로 하지 않습니다.

  • 투명한 픽셀들은 이전에 HDR 버퍼에 그려졌던 불투명 픽셀의 multiplier를 그대로 사용합니다.

  • 하지만 정밀도(precision)의 문제가 생길수 있는데요. 특히 불투명 픽셀과 투명 픽셀의 값이 크게 차이가 날 경우 그렇습니다.

    • 투명 물체가 픽셀값을 어둡게 만들경우 벤딩(bending) 효과가 나타날 수 있습니다.

    • 또, 투명 물체가 픽셀을 어느정도 이상으로 밝게 만들수도 없습니다. (불투명 픽셀의 multiplier가 허용하는 것 이상으로 밝게 만드는게 불가능합니다)

자..그럼 이제 좀 차근차근 대충대충 설명해보죠..


일단 수학공식에 맞추기

RGBM 혼합공식을 다시 봅시다:


    ( DestColor * DestMultiplier * InvSrcAlpha + SrcColor * SrcAlpha ) / SrcMultiplier


InvSrcAlpha 때문에 망했다고 전에 말씀드렸죠? 이건 셰이더에서 알파채널 값에 SrcMultiplier를 넣기 때문이었습니다. 그럼 이걸 어떻게 해결할까요? 간단 합니다.. 그냥 SrcMultiplier를 버리면 됩니다.. -_-;;;;; 농담이 아닙니다.. -_-;;;; SrcMultiplier와 DestMultiplier를 같게 만들면 됩니다. 즉 DestMultiplier만 쓰면 되죠. 이따위 짓을 하고 나면 공식이 이렇게 변합니다:


    ( DestColor * DestMultiplier * InvSrcAlpha + SrcColor * SrcAlpha ) / DestMultiplier


이걸 풀면 이렇게 간단히 되죠.


    DestColor * InvSrcAlpha + SrcColor * SrcAlpha  / DestMultiplier


자, 이제 알파채널을 사용하지 않으니 SrcAlpha를 그냥 알파채널에 써주면 됩니다. 끝~



올바른 블렌딩 렌더스테이트 설정

전 하드웨어 블렌딩 연산을 가지고 노는걸 좋아합니다. 가끔 기발한 짓을 할수 있거든요. 예전에 스크린 스페이스 데칼 만들 때도 그런짓을 했지요. 이번에도 또 사고쳤답니다 -_- ㅋㅋ


위에서 보여드렸던 블렌딩 공식에 껴 맞추려면 다음과 같이 렌더스테이트를 바꿔주면 됩니다:

  • SrcBlend: DestAlpha

  • DestBlend: InvSrcAlpha

이렇게 하고나면 위의 혼합공식에 매우 가까워졌지요. 하지만 아직 한가지가 빠져있군요. SrcAlpha를 곱해주는 부분입니다. 이미 SrcBlend를 DestAlpha로 해줬으니 SrcAlpha를 블렌딩 하드웨어에 설정해줄 방법은 없군요. 해법이 뭐냐구요? 전에 했던것처럼 그냥 premultiplied alpha를 다시 쓰면 됩니다. ^_^  셰이더에서 SrcColor에 SrcAlpha를 미리 곱해주세요.


자, 그럼 모두 다 해결된건가요?.... 불행히도 아닙니다.. ( -_-)  위 블렌딩 스테이트 까지 설정해준 다음엔 공식이 이렇게 되거든요.


    DestColour * InvSrcAlpha + SrcColor * SrcAlpha * DestMultiplier


퉤.. -_- DestMultiplier를 나눠줘야 하는데 곱해주고 있군요.... 이걸 고치려면 RGBM 인코딩/디코딩 하는 법을 좀 바꿔주면 됩니다.


RGBM 인코딩/디코딩 방법 바꾸기

RGBM 인코딩/디코딩을 하는 오리지날 방법은 다음과 같습니다.

  • M: max(RGB)

  • encoding: RGB / M

  • decoding: RGB * M

  • alpha encoding: M / 6


이걸 제대로 돌게 만드려면 M을 역으로 뒤집어주면 됩니다. 그러면 인코딩시엔 곱하고 디코딩시엔 나눠줄 수 있습니다. M을 뒤집어주면 대충 이렇게 됩니다.


  • M: 1 / clamp( length(RGB), 1, 6 )

  • encoding: RGB * M

  • decoding: RGB / M

  • alpha encoding: M


max(RGB)대신에 length(RGB)를 사용한 이유는 나중에 투명한 물체가 픽셀을 더 밝게 만들 수 있도록 좀 마진(margin)을 준것입니다. 여기서 M을 계산하는 방법은 좀 핵(hack)이라 생각하는데요. 저 공식에 있는 1(두번 나옴)을 0.125같이 작은 수로 바꾸면 LDR에서 정밀도(precision)가 좀 더 나을겁니다. 그런데 이걸 1으로 두면 나중에 투명패스에서 사용할 수 있는 색상값의 범위가 최소한 0~1은 되니.. 그걸 보장하기 위해 저렇게 뒀습니다.. 뭐든간에... 이것보다 훨씬 괜찮은 M 계산법을 다른 분들이 찾아낼거라 믿습니다... :)


드디어 새로운 블렌딩 공식!

이제 이리저리 다 뜯어 고쳤으니 드디어 공식이 완성되었습니다:


    ( DestColor / DestMultiplier * InvSrcAlpha + SrcColor * SrcAlpha ) * DestMultiplier


이걸 전개하면 다음과 같이 됩니다.


    DestColour * InvSrcAlpha + SrcColor * SrcAlpha * DestMultiplier


무하하하하 -_- 블렌딩 렌더스테이트에서 나온 계산하고 똑같죠? 드디어 완성되었습니다.. -_-v


Alpha 쓰기 끄기

잠만요.. 근데 투명값을 알파채널로 출력(output)해주면 HDR 버퍼에 이미 적혀있던 multiplier를 덮어 쓰겠네요? 그럼 안되지요. 알파값은 블렌딩 유닛에서만 필요한거니, 이게 렌더타겟에 적히는 걸 막아야합니다.


이것도 역시 렌더스테이트에서 alpha 쓰기를 마스킹해주는 것만으로 간단히 해결됩니다.


가산혼합(Additive Blending)

제 방법은 가산혼합과도 잘 동작합니다. 블렌딩 스테이트를 다음과 같이 바꿔주세요

  • SrcBlend: DestAlpha

  • DestBlend: One


주의점

앞서 밝혔다 싶이, 이 방법에는 두가지 단점이 있습니다.

투명한 물체가 픽셀을 너무 어둡게 만들면 벤딩효과가 보일겁니다. 정밀도가 모잘라서 인데요. 이건 이미 버퍼에 있던 픽셀들이 HDR 영역에 있었을때만 보이는 현상입니다.

투명한 물체가 픽셀값을 어느정도 이상으로 밝게 만들지 못합니다. 여기서 어느정도라 하면 불투명 픽셀들이 내뱉어낸 multiplier에서 허용하는 한도입니다.


혼합가능한 RGBM을 어느상황에나 사용할 수 있는건 아닙니다. 특별한 상황에서만 사용가능한데요. 실제 게임 만들면서 그런 상황을 본적이 있습니다. 뭐든간에 성능 또는 메모리상의 이유로 버퍼를 따로 하나 만들수 없고 비주얼 퀄리티를 약간 희생시킬 수 있다면 사용하세요. 결국 비주얼 퀄리티와 메모리간의 밸런스 문제니까요.



p.s.

새로운 콘솔용 게임을 만드시는 분들에게 HDR 패킹은 더이상 필요없을지도 모르겠습니다. 하지만 아직도 후진(?) 하드웨어 용으로 게임을 만드시는 분들이 계실테니, 그분들께 도움이 되었으면 하는 맘에서 오랜만에 동영상이 아닌 블로그 글을 썼습니다.. ^_^



포프였습니다.



반응형
,
Posted by 김포프

사실 게임프로그래머들 중에 release 빌드, 즉 최적화된 코드, 에서 디버깅하는 사람들이 많습니다. 이유는 뭐.. 당연히 debug빌드가 졸라 느린 경우가 있기 때문이지요.


근데 그럴때마다 watch 창에서 로컬변수들 보면 다 깨져나왔죠? ???? 로 나오거나 아니면 아예 잘못된 넘으로 나오던가요. 이걸 해결하려고 디스어셈블리보면서 register에 들어있는 메모리주소를 watch 창에서 casting해서 보는 경우도 있지요. 근데 디스어셈블리 보는게 꽤 귀찮은 짓인건 맞아요..


근데 이게 Visual C++ 2012부터 지원해주더라구요. 사실 공식적인 지원은 아닙니다. 그냥 내부적으로 마이크로소프트사에서 만들어서 다른 용도로 쓰고 있던 걸 누가 찾아낸 것 뿐이거든요. 공식적으로 문서화 되어있는 스위치도 아닙니다. 하지만 현재 VC2012하고 2013에서 다 돕니다. 잘 돕니다.


자 그럼 어케 하냐.... 컴파일러에 이 스위치 하나 넣어주면 됩니다.


/d2Zi+


이 스위치 하나 넣어주시면 됩니다.


실험결과 exe파일은 전혀 손대지 않습니다. pdb파일만 좀 바뀝니다. 고로 그냥 release빌드에서 켜주셔도 아무 문제가 없을것 같습니다.


p.s. 귀찮아서 스샷 생략합니다. 이거 찾아내신 분의 블로그에서 그냥 보세요 ㅎㅎ


끝.


포프



반응형
,
Posted by 김포프

제 전 동료 다니엘이 KGC 2013에서 강연했던 컴오히2의 겨울 렌더링 테크 발표입니다. 다니엘의 부탁으로 제 블로그에 따로 올립니다..




반응형
,