4. 왜 물은 물처럼 보이는 걸까?

반응형

앞의 글 참조
1. http://mgun.tistory.com/1282
2. http://mgun.tistory.com/1294
3. http://mgun.tistory.com/1304

법선맵을 사용한 범프맵핑으로 잔물결을 표현하고 주위의 정경이 비치는 화면반사 환경맵 텍스처를 이용하여
표현하고, 물의 특성인 가까운 물밑일 수록 물 아랫바닥이 투과되어 보이고 멀수록 수면에 주변 정경이 비쳐 보이는
효과를 적용하기 위한 라이팅(프레넬)을 추가해 준다.


여기 까지가 이 전 글에 다루었던 내용이다.
이제 실제로 만들어 가며 되짚어 보자.

위의 이미지를 보면 위의 글에서 언급했던 "화면반사"의 모습이 뚜렷이 나타난다.
이는 환경텍스처를 사용한 것이라고 하였는데 게임에서 이 환경맵을 사용하는 방법은 크게 두가지 이다.
하나는 퍼포먼스를 위한 정적 환경 텍스쳐 맵이고 다른 하나는 당연히 퀄리티 위주의 동적 환경 텍스처 맵이다.

우선 환경텍스처를 보면 환경텍스처는 일반적으로 큐브 형식의 환경맵이나 구 형식의 환경맵을 사용한다.
큐브 환경맵은 만드는 방법도 상당히 간단하다.
현재 카메라에서 90도씩 회전하면서 장면을 찍으면 된다.



큐브 환경 맵.
http://telnet.or.kr/directx/graphics/programmingguide/advancedtopics/pixelpipe/envmap/cubicenvironmentmapping.htm

큐브맵 적용 : http://blog.naver.com/sorkelf/40157348191

구 환경 맵은 큐브 환경맵과 조금 다르다.
구 형태의 맵은 개체의 주위의 360도의 시야를 어안렌즈로 본 것처럼 2D로 표현한 것이다.


구 환경 맵 : http://telnet.or.kr/directx/graphics/programmingguide/advancedtopics/pixelpipe/envmap/sphericalmap.htm
어안렌즈에 대한 설명 : http://mwultong.blogspot.com/2007/04/fisheye-lens-gallery-flickr.html

간단히 말하자면 환경맵이란 모델 표면의 배경등이 비추는 모습을 재현하기 위해 필요한 텍스쳐로
이 텍스쳐를 이용하여 물의 반사를 구현하고자 한다.

정적 환경 맵은 이렇게 미리 만든 환경맵을 설정해서 사용하면 되는데 이렇게 하면 약간의 리얼리티 감소를 감안해야 한다.
예를들어 하나의 케릭터가 삐까번쩍한 판금계열의 갑옷을 입고 가만히 서 있는다고 생각해 보자.
주위 환경이 갑옷에 반사되어 이쁨이쁨하게 보일텐데 갑자기 다른 유저가 다가오면 그 유저의 모습이 내 갑옷에 반사되어야 하지만
환경텍스쳐가 미리 설정되어 갱신되지 않기 때문에 여전히 주변 환경만 이쁨이쁨하게 반사해 준다.
그래서 매 프레임 또는 몇몇 프레임 마다 환경텍스쳐를 동적으로 생성하여 갱신해 준다.
큐브맵일 경우 동적 환경 텍스쳐를 설정해서 사용해 보았는데 나름 갠춘...느려지긴함...이다.
구형 환경맵은 동적으로 생성해 보질 않아서 모르겠다.ㅜㅜ.


 
만약 수면에 비치는 주위 정경의 환경맵을 동적으로 만들고자 한다면
시점 위치를 수면에 대해서 반전시킨 가상 시점으로부터 정경을 텍스쳐에 그리면 된다. 


수면을 렌더링 할 때, 잔물결을 표현하기 위해 사용하는 법선맵으로부터 법선벡터를 구하고, 픽셀단위의 반사벡터를 구한 후
이에 따라 환경맵을 샘플링
하면 된다.   정방사를 기준으로 생각해 보자.

정반사는 입사벡터와 반사벡터의 크기가 같고, 입사각과 반사각의 크기가 같다.
위 그림에서 보면 시야벡터 -E와 법선벡터 n이 주어졌을 때 

반사벡터 R은 시야벡터 -E와 크기가 같고 입사각과 반사각이 같음을 알 수 있다.

입사벡터 -E(또는 L)의 역벡터인 E를 n의 연장선상에 투영시킨 투영벡터 (N dot E)N을 구한다.
투영벡터에 대한 내용은 이곳(http://mgun.tistory.com/1790)을 참조하자.

(R을 구하기 위해 L과 N을 내적하든 V와N을 내적하든 각도가 같으니 어차피 결과는 같다.

이에대한 내용은 이곳 http://mgun.tistory.com/1817을 보면 된다.)


 

입사벡터인 -E의 시작위치를 원점에 위치시키고 여기에 (E dot N)N을 더하면 입사면에 투영된 벡터의 위치를 구할 수 있다.
위 그림을 보면 입사벡터 -E에 (E dot N)N을 더하면 입사면에 투영된 위치를 구할 수 있고, 두번 더하면 반사벡터 R을 
구할 수 있다.

벡터 투영에 대한 내용은 "벡터의 내적의 의미"를 참고하면 된다.


반사벡터 R = -E + 2 * dot(N,E)*N
그렇다면 R + E = 2 * dot(N, E)N 이 된다.


위 화면은 UDK의 머티리얼 에디터에서 기본적으로 제공하는 Reflection Vector를 시각적으로 보기 위해 Diffuse에 연결한 것이다.
렘버트 공식은 dot(L,N)이다.

Normal로 Fluid Normal을 넣긴 했지만 0,0,1을 넣어도 되고, 그냥 적당히 넣으면 된다.
명암의 차이가 굉장히 또렷하다.

여기에 이전에 언급했던 하프 렘버트를 구현하면 아래와 같다.

여기까지를 공식으로 적으면 dot(L,N)*0.5 + 0.5가 된다.


아잉 야해~!.....ㅡㅡ;..죄송...
왼쪽부터 기본 라이팅에 diffuse와 specular와 normal 텍스쳐를 입힌것, rembert, 평준화시킨 rembert이다.

기본 렘버트는 너무 명암비가 극심하고 평준화 시킨 렘버트는 명암이 제대로 구분이 되는것 같지만 부드럽지가 않다.
여기에 특정 계수를 곱해줘 보자.

확실히 이전보다 더 부드러워 졌다.
여기까지를 공식으로 적으면 pow( (dot(L,N))*0.5+0.5, 지수n ) 이 된다.
욘석을 하프 렘버트 라고 부르며 게임에서 아직 많이 사용되고 있는 방식이다.

앞모습을 보니 확실히 구분이 간다.
왼쪽부터 렘버트, 평준화 시킨 렘버트, 계수까지 곱한 하프 렘버트 이다.

어쩌다 보니 반사벡터를 이야기 하다가 여기까지 넘어와 버렸다.
다시 원 주제인 반사벡터로 돌아가 보자.

반사벡터의 공식인 R = -E + 2 * dot(N,E)N을 그대로 만들어 봤더니 Reflection Vector을 하나 넣을 때와 같은 결과가 나온다.

왼쪽부터 차례로

1. Reflection Vector

2. E Vector, 즉 Camera Vector       

3. 올바른 Normal Vector을 추가한 Reflection Vector

이 그림은 해당 모델의 올바른 Normal을 텍스쳐와 방향으로 표시화 한 것이다.

어쩌다 보니 Reflection에 대해 너무 주저리 주저리 말한 것 같다.
글도 너무 길어진 듯....
여기서 일단 끊고 다음 글에서 굴절좀 알아보자!!!

참고 : https://udn.epicgames.com/Three/MasteringUnrealAdvancedMaterialsKR#반사(Reflections)

추가적으로 렌더몽키에서 rambert를 만드는 법은 포프님의 책"셰이더 프로그래밍 입문"을 보면 자세히 잘 나와있다.

rambert.rfx


렌더몽키의 렘버트. 공식을 보면 알겠지만 dot이 끝.


렌더몽키의 평준화 렘버트. dot 결과물에 *0.5 + 0.5를 해주고 있다.


렌더몽키의 하프 렘버트. 특정 지수를 곱해주고 있다.


언렬 머티리얼 에디터가 사용하기는 무척 편리하지만 너무 무겁...
그리고 개인적인 생각으로 shader 언어를 처음 배우는 사람한테는 렌더몽키만한 툴은 없는것 같다.ㅇㅇ




이번 회 글 쓰기는 상당히 힘들었음...
게을러 지기도 했고 봐야할 책도 좀 늘어난 데다가 또 다시 게임에 몰입을 해버려서 시간을 다 소비.....
다음 회는 빨리 ~써야지..쓰고싶다..써야할텐데...써지겠지?.....ㄷㄷ

 



TAGS.

Comments