강의 중 컴퓨터가 이상해서 제대로 강의를 못한 부분 올립니다. 



우선 플렌을 하나 만들께요. 

플렌 뒤에는... 이것저것 요것이 숨어 있습니다. 지금 안보여서 글치.. 


카메라도 저렇게 설치해야 한다는게 이번에는 중요합니다. 

카메라가 저렇게 보이도록 설치해야 합니다. 



셰이더를 하나 만들고 적용합니다. 만든 셰이더를 열고, 최소한의 것들만 남겨둔채 지웁니다. 

이번에는 Lambert shader로 사용했는데요. 사실 Lambert lighting도 쓸모 없지요. 커스텀 라이트로 만들어 라이팅을 날리는 것도 매우 좋습니다. 아무 필요 없어요. 


그럼 준비가 끝났으면 굴절을 만들 준비를... 




우선 스크린을 캡쳐해야 합니다. 실시간으로요. 

랜더 타겟 기능을 쓰는 것도 좋겠지만 쉐이더에도 있으니.. 



짜잔. 


그랩패스{} 를 추가했습니다. 

이것만으로 편하게 캡 ★ 쳐 ★ 완 ★ 료


물론 패스 하나가 추가되지만 ( ) 


그리고 이번엔 저기서 생성된 샘플러를 받아옵니다. 평범한 텍스쳐 샘플러예요 

이름은 정해져 있습니다. _GrabTexture 

대소문자 당근 중요하죠. 

신기하게도 이 샘플러가 이전 패스의 캡쳐를 받아와요. 


그럼 이번엔 UV가 필요하겠죠 


UV는 .. 화면을 캡쳐한거니까 화면 UV가 필요합니다. 

화면 UV는 screenPos 라고 하고, float4 단위입니다. 

받아서 그대로 Emission 으로 출력해서 봅시다. 




분명히 화면 좌표계의 UV가 받아진 것 같습니다만, 화면을 확대할때나 축소할때 크기가 변합니다!!! 

이를 normalize 시켜주도록 하겠습니다. 



screenPos의 rgb 값을 거리인 w (a라고 해도 동일합니다. xyzw / rgba 거든요) 로 나눠주면, 거리와 상관없이 노말라이즈된 화면 UV를 얻을 수 있게 됩니다. 


 

자 이제 이걸 정리해서 UV로 만들어 봤습니다. 화면의 왼쪽 아래가 0,0 이로군요. 

이제 screen 좌표계의 UV가 구해졌습니다. 이걸 _GrabTexture의 UV로 쓰면 되겠군요 



근데 말이죠. 애석하게도 _GrabPass의 UV는 왼쪽 아래가 아니라 왼쪽 위부터 시작합니다. 

왜일까요. 그러게요. 누가 좀 알려주세요 [각주:1]



하여간 그래서 Y를 뒤집어 줄 필요가 있습니다. 


float2 screenUV = IN.screenPos.rgb/ IN.screenPos.a;

screenUV = float2(screenUV.r , 1-screenUV.g);



이런 식으로 말이죠 


그래서 현재까지의 모습은 이렇게 되었습니다. 



자 이제 UV도 구해졌겠다, sampler도 있겠다. tex2D 함수를 구동시킬 때가 되었습니다. 


o.Emission = tex2D(_GrabTexture,screenUV).rgb;


로 화면을 그려봅시다. _GrabTexture 안에는 뭐가 그려져 있을까요? 


드디어 plane 뒤에 뭐가 있었는지 보이기 시작합니다! 근데 몇 가지 문제가 있네요. 


일단 Game 창은 제대로 보이는데, Edit 창에는 거꾸로 보입니다. 


네 이건 뭐 어쩔 수 없는듯. 스크린 UV를 뒤집은 것이 카메라의 UV 때문이니, edit 창에게는 맞지 않는 UV였던 것입니다. 최종이 카메라니 일단 이게 정상으로 해야겠죠. 


또하나는, 아래쪽 게임 창에서 배경의 하늘이 비쳐 보이지 않고 퍼런 색이 비쳐 보인드는 점입니다. 

이건 그리는 순서 때문인데요, Opaque 순서로 하면 plane보다 하늘이 늦게 그려져서 짤리는 것입니다. 


즉  plane을 그리는 순서를 느리게 만들어서 해결해야 합니다. 

Tags { "RenderType"="Opaque" }


이 부분을 

Tags { "RenderType"="Transparent""Queue"="Transparent" }


로 바꿔서 반투명으로 넣어 버리면, 늦게 그리게 될겁니다. 



네 드디어 하늘이 비쳐 보이네요! 


근데 지금 얼굴이 검습니다... 이게 왜 그러냐면 Plane에서 나오는 그림자가 얼굴과 공에 비쳐서 그렇습니다. 


이것도 날려 보겠습니다. 


맨 아래 FallBack 을 "Diffuse" 에서 "Regacy shaders/Transparent/Diffuse 라고 바꿔주면 그림자가 사라질 겁니다. 




자 그림자가 사라졌습니다. 이제 저 앞에 plane이 있다는 것은 아무도 알아채지 못합니다. 

(Edit 창을 보면 뒤집어져서 쉽게 눈치챌 수 있지만 말이죠) 


그렇지만 지금 분명히 저기엔 Plane이 있고, 자신 뒤의 영상을 카메라로 찍어서 자신에게 계속 힙혀주고 있는, 

마치 광학미체와 같은 영향을 하고 있다는 것을 알 수 있습니다. 


이제 그럼 대망의 마무리를 해 볼까요 



노이즈가 들어간 이미지를 이 셰이더가 적용된 메터리얼에 넣었습니다. 

분명 우리 셰이더는 텍스쳐 한 장이 들어가긴 하지만 이걸 어떻게 사용할지는 생각하지 못했었죠. 하지만 이젠 간단합니다. 


지금 받은 텍스쳐의 r 채널만 받아다가, _GrabPass의UV에 더해줍니다. 물론 다 더하면 너무 세기 때문에 0.1을 곱해서 1/10로 효과를 줄여 놓았습니다. 


훌륭한 노이즈 굴절 효과가 되었습니다!! 




그리고 겸해서, 노이즈 텍스쳐를 흘러가게 하기 위해서 _Time.x를 더해준다면? 

그리고 Play를 눌러서 게임화면을 보시면 ... 굴절되어 흘러가는 화면을 보실 수 있으실 겁니다. 



  1. 하제초아님이 알려주셨습니다. https://www.ntu.edu.sg/home/ehchua/programming/opengl/images/Graphics3D_Viewport.png [본문으로]
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 신희도 2018.05.30 10:38  댓글주소  수정/삭제  댓글쓰기

    교수님 안녕하세요! 유니티 쉐이더 책을 보고 굴절 이펙트를 만들다가 궁금한 점이 생겨서 문의를 드립니다.

    에디터에서 화면이 상하 반전되는 현상이 안드로이드 기기에서도 똑같이 출력이 되더라구요..; 이유와 해결 방법을 좀 알 수 있을까요?ㅠㅠ

  2. 신희도 2018.05.30 12:22  댓글주소  수정/삭제  댓글쓰기

    답변 감사합니다 교수님!
    screenUV = float2(screenUV.r , 1-screenUV.g) <<<요거 말씀하시는거 맞나요?ㅎㅎ

  3. 학생 2020.09.01 13:05  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 유니티 쉐이더 스타트업 보고 공부중인 학생입니다.
    예제를 따라 셰이더를 작성하다가 문제가 생겨 같은 사례가 있나 검색해보았지만 찾지 못해 저자님께 도움을 얻고싶어 댓글 남깁니다.
    이 글과 비슷한 내용을 다루는 챕터인데 screenPos를 노멀라이즈 하는 과정에서 (screenPos.xyz/screenPos.w) floating point division by zero 오류가 발생하면서 셰이더는 여전히 카메라의 영향을 받습니다...
    어째서 발생하는지 이유를 알고싶습니다

    • 캬오 대마왕J 2020.09.01 14:24 신고  댓글주소  수정/삭제

      네 간단하게 하느라 아무래도 에러 안나는 부분은 생략한게 좀 있어서 그러는데요.
      그건 에러라기보다는 워닝입니다.
      말그대로 w 가 0 일때는 0로 나누게 되는 오류가 나는 공식인데 이거 괜찮냐... 라는 워닝이고, 0로 안나누면 사실 문제는 없이 동작합니다.
      하지만 그 워닝을 피하기 위해서 이런 짓을 하곤 하죠 https://chulin28ho.tistory.com/481?category=822622
      보시면
      float4( i.screenPos.xyz , i.screenPos.w + 0.00000000001 );

      을 해서 0로 나누는 것만 억지로 피하고 있는걸 보실 수 있으실 겁니다.

    • 학생 2020.09.01 18:13  댓글주소  수정/삭제

      감사합니다! 문제없는 부분이었군요