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

Diffuse Warping을 이용한 고급 셀 셰이딩

 

18강입니다! 저번 시간에 이어서 만화같은 툰셰이더를 만드는 얘기를 계속 해 보도록 하지요.

원래는 오늘 외각선을 해버리고 끝내려 했는데, 저번 강의의 리플에 재미있는 얘기가 씌여 있어서... 후후후
계획을 좀 바꿨습니다.

 

마루님 땡큐요 ㅋ

 

 

 

지난 번에는 if 문을 이용해서 1 ~ -1 까지 되어 있던 조명값을
딱! 한 숫자로 나눠서 두 가지 색상으로만 이루어지게 만들었습니다.

일정 숫자보다 크면 흰색을, 일정 숫자보다 작으면 붉은 색을 말이죠. 

그 예제가 아래입니다.  if문을 이용해서, 0보다 크면 무조건 흰색을 내놓고, 0보다 작으면 무조건 붉은색을 내놓으라는 내용이었습니다.

안내놓으면 구워서 먹으리

 

이것 말고  ceil 함수를 이용한 방식도 있었는데,
저번 시간 반응이 별로 안좋아서 설명 안할래요 ㅋ 리플 백개 안달려서 삐쳤뜸.
나 원래 이런 남자임

궁금하면 포프님한테 물어보시덩가 흥ㅋ

흥 난 비싼 남자임

 

 

 

그렇지만 옛정이 있으니 오늘은 다른 방식 하나 더 설명하죠. 요것도 아주 재미있는 방식이예요.
응용도 아주 무궁무진하구요.

일단 시작은 대인배 밸브사에서 발표한 팀 포트리스 논문에서 시작합니다. [각주:1]

 이 논문은 그냥 Diffuse 계산은 너무 썰렁하니까 Warped diffuse를 사용하자는 얘기였습니다.
이 기술이 여기서 처음 사용한 것인지 아닌지는 모르겠지만 제가 본 건 여기가 처음이긴 합니다.
(제가 잘못 알고 있으면 프로그래밍 스승님들 좀 알려주세요 ... )

이 논문에서 그 부분은 다음과 같습니다.

뭔가 줄무늬처럼 생긴 그림이 하나 있습니다.
그리고 '영어' 가 있습니다 .... ............................

 

영어랑 수학 싫어 무서워

어쨌건 위에 설명된 이런 기능을 이용하면

 

이렇게 나온다는 얘기였죠.

 

얼핏 봐서는 이거랑 셀 쉐이딩이랑은 별 관련 없는 것으로 보입니다만... 그렇지 않습니다!
사실 매우매우매우매우매우매우매우  연관이 있어요!!!

일단 주저리 주저리 떠들기 전에, 먼저 구현해 봅시다.

 

 

 

 

Diffuse Warp 구현

 

그러니까 이것은 UV의 구조를 잘 이해하는 데에서부터 시작합니다.

6강을 배웠던 것을 다시 꺼내어 보죠. http://gamedevforever.com/96
6강을 보면 오브젝트에 그림이 입혀질 때, UV 좌표에 따라 그림이 입혀진다고 되어 있습니다.

흐음 그렇군요. 뭐 이제 이거야 아무 것도 아니죠 :D 아하하하하하하

 

근데 여기서 잘 한번 생각해 보세요.

어쨌거나 UV도 float2 의 단순한 '숫자' 란 말입니다.
네 숫자예요.

이미 앞에서도 많이 보셨듯, UV라고 이름붙여진 특별한 무엇을 꼭 받아와서
UV에 집어넣어야 동작하는 것은 아니었습니다.
float2 라면 뭐든지, UV 자리에 집어넣기만 하면 UV인줄 알고 동작한다고 그랬었죠.

즉 뭐가 되던 숫자 2개만 어디서 구해오면 UV를 만들 수 있다... 라는 거란 말이죠.

흐음 ... 알겠어요.

근데

여러분 모두 아시겠지만...

조명 있잖아요 조명...

참 지겹게도 보네요.

 

이거 있잖아요. 이것도 0~1 가 될 수 있잖아요????

-1부터 1인데요? 0부터 1이 아니고.. 라고 하시는 분은 http://gamedevforever.com/233 
"하프 램버트" 부분 다시 보세요!!
*0.5+0.5 를 해 주면, -1~1인 영역을 0~1 로 만들어 줄 수 있습니다.

흐음... 그러니까 저 조명 영역을 UV의 요소로 사용하는 것도 가능하단 말입니다. 0~1만 되면 무조건 쓸 수 있으니까요.
맞지요?

 

 

 

본격적으로 시작해 봅시다.

필요한 것은 두 개입니다. 하나는 warp 시킬 텍스쳐, 또 하나는 Lambert 공식입니다.
그러고 보니 Dot 공식 하나 배워서 참 많을 걸 해 왔군요 ...

 

dot 하나만 대충 배워도 엄청나게 많은게 가능해요...

 

 

 

자아 그럼 첫 번째로, 텍스쳐를 준비합시다.
아래와 같은 텍스쳐를요.
좌측이 어둡고, 우측이 어두운 이미지입니다.  
만들기 귀찮으신 분들은 아래 이미지를 다운받아서 사용하셔도 좋습니다. [각주:2]

이 그림이 3D 데이터에 입혀진다면
 좌측 위는 UV (0,0) 이겠고, 오른쪽 아래는 UV (1,1) 에 씌여지겠죠?
넹넹 좋습니다.

 

 자 그리고 또 준비할 것은, 반도의 흔한 Lambert 연산 공식입니다.
이젠 뭐 몇 번째 하는 거니까 이정도는 눈감고 만드시잖아요?

이번에도 역시  Prevent Negative Result 를 꺼서 음수가 제대로 나오게 만듭시다.

 

 

네 이제 기본적인 준비물은 끝났습니다.

 

그럼 일단 위의 텍스쳐를 불러와서 공에 입혀보도록 할까요.
기존 조명 연산은 끊고, 단순히 텍스쳐를 불러와 공에 입혀 봅니다.
그러면 아래처럼 됩니다.

비치발리볼 돋네....

 

공에 내장된 UV에 배열되다 보니까 저런 이미지가 나왔습니다. 뭐 어쩔 수 없지요. 흠흠. 저게 정상 맞죠 뭐 사실.
그럼 이제 저 공에 들어갈 UV를 직접 ★커스텀★ 으로 만들어 봅시다. Lambert 라이트에서 나온 숫자를 이용해서 말이죠.

 

일단 Lambert 연산에서 LightColor는 더이상 필요 없으니까 그 부분은 날려 버립시다. 우리는 닷 연산된 부분만 필요합니다.

 

지웁시다.

그럼 float 으로, -1~1까지 나오는 dot 값만 존재하게 됩니다.

그런데 우리는 -1~1 이 아니라 0~1 까지의 값이 필요하므로, 하프 램버트에서 사용했던 매직 넘버[각주:3]를 사용해서 0~1로 만들어 줍시다.

 

이제 0~1 까지의 값이 준비되었습니다. 밝은 곳은 1로 나오고 어두운 곳 끝은 0으로 나오겠지요.

이 부분이 좀 이해하기 어려우실 수도 있겠는데 잘 보세요.

UV에서, U가 가로고 V가 세로입니다.

 

 

그리고 위 그림에서, 세로 방향은 별로 의미가 없습니다. 색이 변하는건 가로 방향이지요.
그래서 세로 (V) 는 그냥 상수 0.5를 넣도록 하겠습니다.
그리고 가로(U) 에다가는 위에서 연산한 Lambert 조명 값을 넣는 겁니다.

 

이렇게 다른 값들을 연결해서 UV를 수동으로 만들어 주려면 어떻게 해야 했었지요?

Vector Construct !!! 백터를 조합해주는 이 녀석이 있었습니다 !!! [각주:4]
이 녀석은 숫자1개를 입력해주면 float으로 , 2개를 입력해주면 float2로, 3개를 입력해주면 flaot3로, 4개를 입력하면 flaot4로... 만들어 주는 녀석이었습니다.  

 

 

그러므로 이 녀석을 이용해서 UV를 만들어 보죠.

X는 U와 같으므로, X 에다가 아까 만든 0~1까지 된 dot 연산을 집어넣으면 되겠구요.
Y는 V와 같으므로, Y에다가는 그냥 0.5를 넣으면 될 것 같습니다. [각주:5] [각주:6]

자, 이렇게 UV가 완성되었습니다.

U에는 0~1 까지의 값이 들어가 있고,
V에는 0.5의 값이 들어가 있으므로...

UV는 (0.0 , 0.5) ~ (1.0, 0.5)  까지의 값이 나오게 되는 겁니다.
그림으로 보면, 아래 그림에서 텍스쳐의 가운데를 질러가는 빨간 화살표와 같겠죠.

 

 

 

그럼 이렇게 완성된 UV를 아까 텍스쳐의 UV 입력에 집어 넣어 볼까요?

 

짜잔

 

우와! 셀 쉐이딩이 완성되었습니다!!! 조명 방향에 맞춰서, 그림에 그려진 대로 음영이 표현됩니다!!!

굳이 말하자면 "Diffuse Warp를 이용한 custom texture 방식의 셀 세이딩" 정도 될까요. 텍스쳐가 UV에 맞추어 입혀진다는 것을 바탕으로, UV를 빛의 밝기에 맞는 방향으로 만들고, 조명 대신 텍스쳐를 입혀 놓았다고 말할 수 있겠네요

보통 이런건 공으로 보면 안이쁘죠. 그래서 주전자에 입혀보면 이렇습니다. 좀 더 이쁘네요 :)

 

오오 확실히 공보다 주전자가 낫습니다. ㅎㅎ

 

 

 

이렇게 해서 또 하나의 셀 셰이딩 표현 법을 알아봤습니다. 오늘 강의 끝!!!

 

아이 좋아라

 

 

 

 

 

 

 

... 하려고 하려 했는데,

 

뭔가 이상한 것이 보입니다.

 

 

 두둥.

 

이게 뭐시여. 왠 점이여...

 

네 점이 보입니다. 난데없이. 이게 왜 생긴 걸까요? 어떻게 없앨 수 있을까요?

 

 

점을 없애 봅시다.

 

점을 없애봐라 민소희

 

뭐 사실 조작은 간단합니다. 별거 아닙니다. 원리도 간단합니다.
일단 이렇게 된 이유부터 알아야 겠지요?

texture는 보통 기본 옵션이 wrap 옵션입니다.
Wrap 옵션이란건 프로그래밍적 용어고, 3D max 에서는 'Tile' 이라는 옵션으로 존재합니다.  
아래와 같이, 텍스쳐의 Tiling 수치를 준 다음에 보면 텍스쳐가 연속해서 사방에 계속 찍어진다는 것을 볼 수 있겠지요.

그리고 이 Tile 옵션을 끄면 이렇게 한 장만 나오게 됩니다.

3D max에서는 이렇게 되어 있습니다만... 게임에서는 비슷하지만 좀 다릅니다. 그건 나중에 설명하고.

어쨌건 'Wrap' 옵션이 타일링 되는건, 게임이나 3D max나 똑같습니다. 그러므로 우리가 입힌 텍스쳐도 사실 이렇게 계속 중복되어 있다고 볼 수 있는거죠. 기본이 Wrap 이니까요.

 

굳이 억지로 그림으로 표현해 보자면 이렇게 생겼단 말이겠죠. 사방팔방으로 계속 연속된.... 검은 외각선은 구별하기 쉬우라고 그렸을 뿐입니다. 사실 저 외각선은 없겠죠.

그러므로 가장 어두운 색 옆 색은 가장 밝은 색이고,

가장 밝은 색 옆 색은 가장 어두운 색이 다시 인접하고 있는 겁니다.

 

 

그럼 이제 아까 우리가 만든 UV를 보죠. 우리가 만든건 0~1까지의 float 입니다.


그런데 이 float이 그렇게 작은 수가 아니예요. float의 단계가 얼마나 된다고 생각하십니까?
float 의 범위는 10진수로 하면 10의 38승이나 된데요. 소숫점으로 하면 소숫점 아래 6자리의 정밀도지요.
사실 실제 쓸 때는 부족하기도 하지만, 일반적인 생활에서는 굉장히 넓은 값입니다.

그런데 우리가 그린 이 음영 그림은 그렇게 크지가 않지요. 실무였다면 기껏해야 가로 256 사이즈나 그렸을까요? [각주:7]

그렇다보니까 이미지가 아무래도 좀 번지게 됩니다. 그러다보니 UV 영역이 딱 끝나는 경계선 부분에, UV의 다른 쪽 부분의 찌꺼기가 '번져서' 침범해 올 수도 있다는 겁니다. [각주:8]

3D 게임은 2D와 달라서, 픽셀로 딱 맞게 끝나는게 아니거든요... 아아 그렇군요 ㅠㅠ


 

 

하지만 걱정하지 마세요. 이런 것 때문에 만든 해결책이 있습니다.
아까 max에서 본 Tile 옵션을 끄는 것 같은 해결책 말이죠.

 

 

 

 

일단 텍스쳐 노드를 선택하고, 여기에 있는 Address 옵션을 보면 Wrap으로 되어 있는 것을 볼 수 있습니다.

 

이걸 CLAMP로 바꿔 주면 됩니다! [각주:9]

 

그럼 어떻게 되냐면요.  

Wrap이던 기존 텍스쳐의 '연속' 옵션 기능이

이렇게 바뀌게 됩니다! 마지막 픽셀을 좌아악 이어가는 것이죠! 이것이 3DMax의 타일링 옵션을 끄는 것과 다른 점입니다.
즉 이 CLAMP 옵션을 쓴다면, 밝게 끝나는 부분은 그 밝은 색을 받아서 주우욱 늘어나고, 어두운 부분도 주우욱 늘어단다는 겁니다. 즉  어두운색----->밝은색에서 다시 어두운색 ----> 밝은 색 이 아니라,
어두운색-----> 밝은 색 옆에는 주우우우우욱 밝은 색... 이 되는 것이지요!
이렇게 되면 다소 오차가 생기더라도, 색이 침범하는 일은 없어집니다!!!

 

멋집니다.

그래서 이렇게 단순하게 CLAMP옵션을 켜고 다시 오브젝트를 보면.

 

 

멋지니까 크게 올려야징 ㅋ

와아아아아아아아아아앙

이제 정말 문제가 없습니다 ㅠㅠ 쥘쥘

이번에야말로 정말로 끝입니다. 수고하셨습니다.
이제 아티스트한테 저 텍스쳐를 주기만 하면, 알아서 이쁜 셀셰이딩을 만들 것입니다 이히힛 
우리 아티스트들은 이런거 짱 좋아하거든요.

 

이런거 만들어 주면 아티스트의 표정이 이렇게 바뀌어요. ㅋ

 

 

 

 

참고로, 이 기능은 단순히 셀 셰이딩이 아닌 일반적인 라이팅의 개선에도 사용할 수 있습니다.
빛이 넘어가는 부분의 색상을 완전히 제어할 수 있기 때문에 Fake SSS 에서도 사용할 수 있고, 있지도 않은 역광을 만들 수도 있으며, 조명 연산과는 전혀 다른 색감의 이미지를 만들 수도 있습니다.

단, 아무래도 텍스쳐를 한 장 더 샘플링 한다는 부담이 있다는 건 확실합니다만....
잘 쓰면 정말 독특한 느낌을 맘대로 줄 수 있지요.

 아래 이미지는 ,같은 셰이더에 같은 라이트에
Warp 텍스쳐만 바꿔서 구현해본 이미지입니다.

이렇게 말이죠

 

 

18강.SFX

 

그럼 다음 시간에는, 진짜로 외곽선을 만들어 보도록 하겠습니다.

여러분 그럼 다음 시간까지 안녕~

  1. 이분들은 자신들이 개발한 기법들을 몽땅 다 발표하기로 유명한 분들이라서 대인배라 불립니다. [본문으로]
  2. 이 이미지는 공부를 위해서 만들었습니다. 사실 세로는 1픽셀만 되어도 충분하고, 가로는 2의 제곱으로 만들어 주는게 게임에서 사용하는게 가장 좋겠지요. 뭐 지금은 아무렇게나 만들어도 됩니다. [본문으로]
  3. *0.5+0.5 [본문으로]
  4. math 항목의 맨 아래에 VectorConstruction이라고 있습니당. 설마 벌써 잊으신건 아니겠지요? 이전에 UV 컨트롤 할때 해봤는뎅 [본문으로]
  5. 사실은 그냥 0~1 사이 아무 값이나 넣어도 됩니다. 세로니까요. ㅎ [본문으로]
  6. 아래 글 보면 constant를 하나만 만들어서 세 군데에 연결한걸 보실 수 있으실 겁니다. 이건 상수가 필요한 3개의 부분이 모두 0.5가 필요한 관계로... 하나만 만들어서 연결해준 겁니다. 각각 따로 만들어도 상관 없습니다 ㅎ [본문으로]
  7. 지금 위의 저 그림도 가로 630 밖에 안되는 녀석이기도 하구요. [본문으로]
  8. float 을 사용하면 이런 오차들, 자주 보게 됩니다. float의 정밀도도 그다지 믿을만한 놈이 못 되거든요. [본문으로]
  9. 사실 여기서는 U만 바꿔줘도 됩니다. 그냥 버릇이 되어서 다 바꿨... [본문으로]

댓글을 달아 주세요

  1. Favicon of http://blog.naver.com/mathl 산수 2013.01.13 20:49  댓글주소  수정/삭제  댓글쓰기

    아 너무 기다렸어요 이번화도 넘 재미있고 유익하네요 감사합니다.
    연재도 끝나가는데 이제 어디서 공부하나요 ㅠ
    쉐이더관련 부분을 공부좀 더하고싶은데 어디서 어떻게 공부를 하면 될까요 ?
    궁금합니다.

  2. 파란달 2013.01.22 13:24  댓글주소  수정/삭제  댓글쓰기

    좋은 강좌, 항상 지켜보고(?) 있습니다.