炸殺
카테고리
작성일
2020. 7. 12. 05:38
작성자
炸殺

 

물 쉐이더를 좀 더 업그레이드 해보자.

 

 

지난 강의 때 만들었던 물쉐이더를 업그레이드 해봅시다.

지난 포스트에서 완성한 물은 몇가지 아쉬운 점이 있었습니다. 스페큘러라던가 굴절도 전혀 없었죠.

좀 더 사실적인 물 표현을 위하여 스페큘러와 굴절을 추가해봅시다.

 

 

 

 

 

 

Specular 추가하기

 

드넓은 수평선을 보고 있으면 해가 있는 곳에서부터 내가 있는 곳까지 물이 반짝반짝 일렁이던 것을 본적 있으신가요?

림라이트를 이용해 수면을 반짝이게 했더니 외곽선만 따라 물이 반짝이는 것이 아쉬웠습니다.

 

따라서 스페큘러를 직접 만들어 넣어줍시다!

 

 

 

 

Specular

 

이제는 익숙해질 때도 된 스페큘러 공식

lightDir와 viewDir를 더해 둘의 절반을 가르는 Half Vector를 구해준 뒤 normalize를 통해 길이를 1로 만들어주고,

Normal Vector와 Half Vector를 내적(dot)하여 Specular를 구합니다.

하지만 이 상태로는 범위가 너무 넓으니 제곱(pow)을 하여 하이라이트 처럼 범위를 좁혀주면 완성!

 

 

Specular에 대한 자세한 설명은 아래 포스트를 참고해주세요.

https://celestialbody.tistory.com/12

 

유니티 쉐이더 Unity Shader - 08. Blin-Phong Shader 와 Custom Light.

이전 포스트에서 만들었던 Lambert 쉐이더에는 Specular가 포함되어있지 않습니다. 이번에는 Lambert 쉐이더에 Speculer를 추가해 봅시다. Pong Shading Phong Shading이란? 1975년 Bui Tuong Phong씨가 개발한..

celestialbody.tistory.com

 

이렇게 구한 스페큘러를 어떻게 하냐...

 

 

다음과 같이 rgb에 Specular를 더해주게 되면 Specular가 잘 적용이 되지만

Alpha 값은 rim값으로 설정되어 기껏 더해준 Specular까지 Alpha가 먹어버리게 됩니다. Specular가 흐려져버리는 거죠.

 

 

 

Specularfloat의 값을 가지고 있습니다.

구한 specular의 값을 Alpha에 더해줄 경우 specular 값이 1인 부분은 알파도 1이 되므로

rim Alpha의 영향을 받지 않아 ViewVector 따라 흐려지지 않게 됩니다.

 

 

 

 

 

 

굴절

 

굴절은 빛이 투명한 물질을 통과할 때 빛이 진행하는 속도가 느려지고, 이에 따라물질의 경계면을 지나갈 때 방향이 바뀌는 현상을 말합니다.

 

수면 아래 있는 물체는 빛의 굴절에 의해 실제 위치와 다르게 보이게 되는데,

수면이 일렁일 경우 마치 물체가 구겨진 듯 함께 일렁이는 것처럼 보이게 되는 것이죠.

 

 

 

수면 위에 있는 오브젝트는 멀쩡하게, 수면 아래있는 오브젝트는 구겨져 보이게 하려면 어떻게 해야할까요?

 

우리는 이전 포스트에서 텍스쳐를 구겨본 경험이 있습니다.

마치 수면 아래가 '굴절된 것처럼' 보여주기 위해 수면 아래를 텍스쳐로 처리해버리는 것입니다.

 

 

 

Render Texture

Create > Render Texture를 통하여 생성할 수 있는 이 텍스쳐는

카메라에 적용할 시 해당 카메라가 비추고 있는 View를 실시간 텍스쳐로 변환해주는 기능을 가지고 있습니다.

 

첫번째로 Render Texture로 사용할 카메라를 생성해준 뒤

Target Texture에 아까 만들어 주었던 Rander Texture를 넣어줍니다. 

 

해당 텍스쳐를 사용할 메터리얼(위에선 Unlit/Texture 메터리얼을 사용했다)을 만들어준 뒤 텍스쳐를 넣어주면 끝! 

 

이제 이 카메라는 카메라로써의 기능을 잃고 실시간으로 화면을 캡쳐하여 텍스쳐의 기능을 수행하게 됩니다.

Render Texture는 다른 텍스쳐처럼 압축 방식도 원하는 것으로 선택할 수 있으며 사이즈 조절, 안티알리아싱, 민맵 사용 여부도 선택이 가능합니다.

 

Render Texture를 이용해서 실시간으로 반응하는 CCTV영상등을 만드는 것도 가능하겠죠!?

 

다만 Render Texture의 경우 카메라를 하나 더 생성하는 것이기에 비용이 큽니다. (그래도 PC에서는 괜찮습니다)

GrabPass를 사용한다면 굳이 카메라를 하나 더 만들지 않아도 렌더 텍스쳐처럼 카메라 뷰를 텍스쳐로 사용하는 것이 가능해집니다. 무거운 건 똑같지만....

 

 

 

 

 

 

GrabPass와 screenPos

 

  • Grabpass

 

Grabpass를 사용한다면 굳이 카메라를 하나 더 만들지 않아도

쉐이더 코드만으로 Render Texture처럼 카메라 뷰를 텍스쳐로 사용하는 것이 가능해집니다. 무거운 건 똑같지만....

 

 

GrabPass는 Pass의 한 종류로, 현재 화면에 잡힌 내용을 실시간으로 텍스처에 담아 냅니다. 

 

Rander Texture가 카메라를 따로 설치하여 해당 카메라의 view를 텍스쳐로 변환했다면

Grabpass는 현재 보여지고 있는 카메라의 view를 텍스처로 변환하는 것.

때문에 카메라 고정이 불가능하며 화면 밖은 인식하지 못하는 단점이 있습니다.

 

GrabPass는 CGPROGRAM 위에 GrapPass{}를 작성해주는 것으로 사용이 가능해집니다. (저 GrabPass쓰겠습니다!)

또한 다른 텍스쳐들 처럼 Sampler2D _GrabTexture;을 통하여 선언할 수 있습니다.

 

 

 

 

 

 

  • screenPos

 

tex2D를 통해 텍스쳐를 출력하려면 텍스쳐와 그 텍스쳐를 펼칠 UV가 필요합니다. 

여기서 UV로 사용해줄 것이 바로 screenPos입니다.

 

Input 안에 float4 screenPos 선언을 통해 불러올 수 있으며,

ScreenPos는 화면 그 자체를 UV로 사용하는 좌표계입니다.

 

 

우리가 사용할 텍스쳐인 GrabPass는 우리가 보고 있는 화면을 텍스쳐화합니다. 

만약 UV가 카메라의 위치에 따라 맞춰지지 않고 고정되고 있다면

텍스쳐는 실시간으로 변하는데 UV는 그대로이니 엉뚱한 곳이 왜곡되어버리는 현상이 발생하겠죠. 

 

screenPos의 UV를 눈으로 확인하면 다음과 같은데요,

초록색이 기울어진 것을 통해 screenPos는 기본적으로 Perspective(원근투영)으로 되어있는 것을 확인할 수 있습니다.

UV에 원근이 먹으면 안되기때문에 screenPos를 Orthographic(직각투영)으로 변경해줍시다.

 

 

 Perspective에서 Orthographic으로 변경하는 것은 screenPos.rgb에 screenPos.a를 나눠주면 끝입니다.

 

Perspective는 거리가 멀어질 수록 view frustum이 확대되고, 그에따라 오브젝트가 축소되는 개념입니다.

그 수치가 alpha(w)에 들어가 있는 값이기 때문에 a를 곱하면 거리에 따라 확대 축소가 이루어지고,

alpha값으로 나눠주면  Perspective에서 Orthographic으로 전환되는 것이죠. (ScreenPos가 3차원인데 float4인 이유)

 

화면을 돌려도 화면에 맞춰 UV 색상의 위치는 그대로!

 

 

 

이렇게 텍스쳐를 넣어 확인해보면 둘의 차이를 확실하게 느낄 수 있습니다. 

일반적인 메쉬의 UV를 사용했을 땐 해당 오브젝트에 텍스쳐 색종이를 붙여둔 느낌이라면

ScreenPos를 사용했을 땐 모니터 자체에 텍스쳐 색종이를 붙여두고 색종이가 오브젝트에 마스킹된 느낌입니다.

 

 

이제 모든 준비는 끝났으니 GrabPass와 screenPos를 이용하여 수면 및 굴절을 표현해봅시다.

 

 

 

 

 

 

(이제진짜) 굴절 만들기

 

 

가장 먼저 수면 아래를 GrabTexture로 처리할 것이기 때문에 Alpha는 의미가 없어집니다. 관련된 것들을 지워줍시다.

 

각각 GrabTexture와 ScreenPos를 출력해보면 마치 물이 없어진 것 처럼 보이실 겁니다.

옆에 만들어줬던 스페큘러와 림라이트만 동동 떠다니네요.

 

이는 출력이 되지 않고 있는 것이 아닌 현제 실시간으로 GrabTexture가 screenPos를 UV로 출력되는 중! 입니다.

화면을 그대로 찍어 출력하니 아무것도 없는 것 처럼 보이는 것이죠.

여기에다가 이전에 텍스쳐를 구겨줬던 것처럼 노말을 곱해주면....

 

 

UV니까 float2!잊지말자.
수면 아래가 일렁인다!

다만 이렇게 굴절을 만들 경우 수면 위에 있는 오브젝트의 경계면도 살짝 일렁이게 되는데요,

해결할 수 있는 방법이 없는 건 아니지만 훨씬 무거워지기 때문에 많은 게임들이 그냥 일렁이게 둔다고합니다.

 

 

!!!

이 때 버텍스 웨이브가 모세의 기적처럼 양쪽으로 갈라지는 현상이 일어난다면

#pragma에 addshadow를 추가해준 뒤 FallBack은 Tranparent로 변경해주면 해결됩니다.

(이부분은 왜 그러는건지 아직 이해하지 못했습니다..)

 

 

이제 이렇게 만든 굴절과 이전의 만들었던 Reflection을 lerp를 통하여 합쳐줍니다. 

마스크 값에 rim을 넣어 수직으로 내려다보면 GrabTex가 보이고 비스듬하게 보면 Reflection이 보이도록 해주는 거죠!

이에따라 커스텀 쉐이더에 있던 Rim연산을 void sruf로 올려주어 수정한 코드는 위와 같습니다.

 

 

물 아래 있는 부분이 물과 함께 자연스럽게 일렁인다!

 

 

 

 

 

 

+ Depth 검출

 

 

장렬하게 실패했다 내가 꼭 성공하고 만다

 

 

 

 

 

일단 완성.....