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


안녕하세요. 이 영광스러운 자리에 글을 쓰게 된 오즈라엘이라고 합니다. 꾸벅
많은 네임드분들 사이에 꼽사리 껴서 글을 쓰게 되서 영광스럽기 그지 없습니다 헤헤
첫 글을 쓰게 되어 많이 긴장이 됩니다. 이거 괜히 저의 허접한 실력이 뽀록날까봐 두근두근 하네요 그래서 어렵지 않은 주제로 글을 쓰고자 고민하고 있었는데!!!! 마침 얼마 전 대마왕J님께서 외곽선의 구현에 관한 질문을 주셔서 생각난 김에 정리를 좀 해볼까 합니다. (  TA는 전지전능!!! )
 



개요

외곽선은 렌더링에서 많은 용도로 널리 쓰이고 있습니다. 특히 게임에서는 외곽선이 어떤 종류 어떤 용도로든 꼭 쓰이기 마련이지요. 가장 대표적으로는 비 실사 렌더링인 카툰 렌더링의 중요한 요소로 쓰이고 있지요. 비 실사 렌더링이 아닐지라도 특정 오브젝트를 타겟팅하거나 강조하는 용도로 쓰이기도 합니다. 또한, 이미지 후처리에서 오브젝트의 경계의 검출로 쓰이기도 합니다.

외곽선은 이토록 널리 쓰이는 만큼 그 구현 방법도 여러가지가 있습니다만, 그 중 몇 가지만 정리를 해 볼까 합니다.




오브젝트 확장

외곽선을 만들어 내는 방식 중 하나로 오브젝트를 확장해서 그리는 방식이 있습니다. 오브젝트를 확대해서 그리면 원본 오브젝트와 확대 오브젝트 사이의 공간을 외각선으로 간주한다는 개념이지요.  가장 널리 쓰인다고 볼 수 있겠습니다. 

(오브젝트 확장 방식의 설명 이미지들은 대마왕J님께서 협찬해주셨습니다. 태블릿으로 발그림을 그렸더니 바로 맥스로 만들어주셨다능 T-T 케감동. 역시 TA는 전지전능!!!)

총 2 pass로 이루어집니다.  

pass 1. 일단 오브젝트를 정상적으로 그립니다. 이때 깊이 테스트와 쓰기 모두 활성화 시킵니다.

Pass 2. 그리고는 오브젝트를 확대해서 그리되 라이팅을 끄고 검은색으로 뒷면을 그립니다.  이때 역시 깊이 테스트를 수행하기 때문에 확장 오브젝트의 뒷면 중 정상 오브젝트에 의하여 가려지는 부분은 걸러지기 때문에 오브젝트의 윤곽만 남게 됩니다.

엄밀히 말하면 시야와 오브젝트의 면이 방향이 정확히 수직인 상태의 외곽선만 보이는 것은 아닙니다. 오브젝트와 확대된 뒷면의 사이 공간이 외곽선의 역할을 하는 것이지요. 하지만 시각적으로는 매우 만족할 만한 외곽선이 탄생하게 되지요.

pass 2 에서 오브젝트의 뒷면을 그리는 것은 Cull mode만 변경하면 되는 것이지만, 확장해서 그리는 것은 어떻게 하면 될까요? 단순히 모델 매트릭스의 스케일링만 조절하면 될 것 같지만 그렇지 않아요. 위 그림의 구체같은 단순한 모델은 문제가 없겠지만 아래와 같은 복잡한 모델은 문제가 생깁니다.
모델이 복잡한데다가 원점 또한 모델의 정 가운데 있지 않은 상태로 스케일링을 하면은

결과적으로 실루엣이 어긋나게 됩니다. 우리가 원했던 결과물이 아니지요.

그렇다면 어떻게 확장시키면 되는걸까요? 그것은 어렵지 않아요~ 모델의 정점을 노말 방향으로 조금만 이동해주면 되요~

이때, 고민이 하나 생기게 됩니다. 일정 크기로 모델을 확장해서 외곽선을 그리면 오브젝트와 카메라의 거리에 따라 외곽선의 두께 역시 일정치가 않게 보이는 것이죠. 오브젝트가 어디에 위치하든 일정한 두께로 보고 싶은데 말이죠. 하지만 그것도 어렵지~ 않아요~~ 다음 공식으로 사용하면 되요~

폴리곤 오프셋 = thickness x dist x fovx / width 


복잡할 필요도 없이 간단하기 때문에 많은 카툰 렌더링 게임에서 이러한 방식을 사용해왔습니다.





다만 완벽하지는 않은것이, 닫힌 메시가 아닌 경우는 정상적으로 외곽선이 생기지 않을수도 있지요. 예를들어 머리카락이나 치마 등은 면을 하나로만 만들고 앞뒷면 없이 양면을 모두 렌더링 하는데, 이러한 경우는 어떻게 할 수가 없게 됩니다.


원본 이미지 출처 : http://www.onlifezone.com/4868209




외곽선이 실제 오브젝트의 실루엣과 같은 깊이에 그려지는 것이 아니기 때문에 외곽선과 오브젝트 사이가 침투 당하기도 합니다.


 

원본 이미지 : princess punt




하지만 뭐 이정도 쯤이야 애교로 넘어가줍시다. 큰 문제는, 인접 면이 정점을 공유하지 않는 경우입니다. 육각형처럼 꺾인 면의 라이팅을 불연속적이게 만들경우나 uv좌표가 공유되지 않게 하기 위해 그렇게 모델링 되기도 하지요. 그러한 경우는 오브젝트가 확장되면서 버텍스의 위치가 서로 떨어지게 됩니다. 


 





후처리

외곽선을 후처리로 만드는 방법도 존재합니다. 
일단 먼저 정상적으로 씬 전체를 렌더링합니다.
  1. 그 후, 외곽선이 필요한 오브젝트를 단색으로 그립니다. (빨간색 외곽선이라 가정하겠습니다)이때 깊이 버퍼는 살아있어야 하고  오퍼레이션은  equal로 설정합니다. 그렇게 되면 오브젝트가 실제로 그려진 영역만 단색으로 마스킹 처리가 됩니다.
  2. 그 후 마스킹 버퍼에서 빨간색을 외곽선 두께로 사용 할 만큼 확장합니다. (이 버퍼는 빨간색이 아닌 색으로 초기화 되어 있어야 하겠지요)
  3. 그리고서는 그 확장한 마스킹 버퍼를 오브젝트 원래 영역을 제외하고 화면에 덮어씌우면 됩니다.
pass 3에서 원래 영역 제외는 어떻게 하면 될까요? 그것도 어렵지 않아요~ 
스텐실을 사용할 수도 있고, 알파 채널을 이용할 수도 있는데, 알파 채널을 이용하는 것으로 말씀을 드리겠습니다. 
  1. 먼저, 위의 pass 1에서 마스킹 버퍼에 오브젝트를 그릴 시 알파 채널에 특정한 값을 새깁니다. 물론 블렌딩은 끈 상태로요.
  2. 확장 처리를 할 시 RGB 채널의 값만 확장하고 A 채널의 값은 그대로 둡니다.
  3. 그리고서 화면에 씌울 시 A 채널의 값을 확인해서 씌울지 안 씌울지를 선택을 하면 되는 것이죠.


 





 이런 방식은 완벽하게 오브젝트의 실루엣만을 뽑아내게 됩니다. 즉, 오브젝트 내의 외각선은 표현이 되지 않는것이죠. 엄밀히 말하면 실루엣을 표현하는 것이지 엣지를 표현하는 방식은 아닙니다.



이미지 출처 : >>ㅑ~ 잼나는 건즈2 홍보 영상(http://www.youtube.com/watch?v=gUUtC901pw4




외곽선 검출 필터

후처리로 엣지를 표현하면서 오브젝트 내부의 엣지 역시 표현을 하고자 한다면 외곽선 검출 필터를 이용하면 됩니다. 대표적으로 소벨 마스크와 라플라스 필터가 사용되지요. 게임이 아닌 이미지 필터링에서는 컬러를 소스로 사용 할 수 밖에 없습니다. 그렇기 때문에 오브젝트가 비슷한 색인 구간에서는 완벽하지는 못합니다. 하지만, 게임에서는 깊이 버퍼의 값을 소스로 사용하면 됩니다. 깊이 차이가큰 부분을 오브젝트의 경계로 취급할 수 있기 때문에 큰 문제가 없습니다.





하지만 완벽히 균일한 두께의 깨끗한 외곽선을 만들어내기 힘들고 주로 필터링 시 불연속 경계를 찾아내기 위한 용도로 주로 사용되지요.






정리 가 안되...

말씀드린 방식 이외에도 많은 방식으로도 외곽선을 표현 할 수 있지만 글이 길어져서 이쯤으로 마무리 할까 합니다. 지루하고 긴 글 끝까지 읽어주셔서 감사합니다 꾸벅
(급 마무리가 어색하네요.. 으음.. 작문 실력을 좀 키워야 할 듯 orz)   
반응형
,