셰이더 그래프에 대한 내용만 보시겠다면 밑의 항목만 보시면 됩니다.
물체 셰이더 그래프
배경 셰이더 그래프
소프트웨어 마에스트로를 하면서 만들었던 프로젝트는 어몽어스, 구스 구스 덕을 레퍼런스로 해서 만들었습니다.
그렇기에 그림자 또한 어몽어스와 유사한 방식으로 동작하였으면 했죠.(배경은 보이지만 그림자에 가려진 물체는 안 보이는 것을 의미합니다.)
제 파트는 아니었지만 관심이 있었던 주제였고 저 나름대로 자료를 찾아보았습니다.
그중에는 셰이더 코드를 작성하여서 해결하는 방법도 있었지만 이미 다른 용도로 셰이더 그래프가 존재하였기에 해당 방법을 혼용해서 쓰긴 어려웠습니다.
결국 프로젝트가 끝날 때까지 근본적인 해결을 하지 못하여서 아쉬웠습니다만...
최근 방법을 찾아냈습니다.
같은 시행착오를 겪으시는 분이 또 있을 것이 분명하기에 이렇게 글을 남겨봅니다.
출처 : https://www.irasutoya.com/2019/07/blog-post_87.html#random
그런데 그때 닌자가 나타났다...
그림자에 숨을 이미지는 '닌자'로 정했습니다.
추후 사용할 배경의 이미지는 닌자와 어울리는 건물로 했습니다.
유니티의 2D 그림자는 Shadow Caster 2D와 Light 2D를 활용해서 구현할 수 있습니다.
저희가 원하는 건 그림자에 가려진 오브젝트는 안 보이는 것입니다.
정확히는 그림자에 가려졌을 때 배경과 같이 특정한 부분만 보이고 캐릭터와 같은 물체는 안 보이는 것입니다.
첫 설명에도 나와있지만 4대의 카메라를 사용하는 방법입니다.
아무래도 4대의 카메라와 3장의 렌더 텍스처를 사용하는 건 부담이 큽니다.
그렇기에 적용하기에는 아쉬운 방법입니다.
체념을 하셨다면 이 방법을 쓰셨을 가능성이 높습니다.
이전 프로젝트 또한 이 방법으로 구현했습니다.
스텐실 버퍼는 픽셀을 화면에 그릴지 말지를 판단하는 용도로 사용할 수 있습니다.
하지만 이 방법에 치명적인 단점이 존재합니다.
Universal Render Pipeline Asset에서 Renderer List를 Renderer 2D Data에서 Universal Render Data로 수정하게 됩니다.
2D Light는 Renderer 2D Data에서 동작하는 것이기에 교체하는 순간 사용할 수 없게 됩니다.
Light가 아니기에 시야의 구간이 칼같이 나누어집니다.
이 또한 2D Light를 포기한 결과라고 볼 수 있습니다.
분명 보라색 닌자의 스프라이트 순서가 보통 닌자보다 높게 설정되어 있습니다.
하지만 보통 닌자가 더 위에 그려져 있습니다.
그 이유는 스텐실을 활용하는 오브젝트들이 다른 오브젝트보다 늦게 그려지기 때문입니다.
그렇기에 기존 유니티의 스프라이트 정렬을 활용할 수 없게 됩니다.
Blend Style을 수정하여서 해결하는 방법입니다.
이 방법이 저희가 원하는 방법일 것입니다. 2D 조명을 그대로 사용하면서 시야 밖 오브젝트를 표시해 주지 않으니까요.
하지만 HLSL을 직접 수정한다는 점이 문제입니다.
HLSL로 작성된 셰이더를 적용해야 하기에 셰이더 그래프를 사용할 수 없게 됩니다.
결국 이 글을 찾아보셨단 건 2가지를 포기하지 못하신 겁니다.
HLSL 수정하기에서 사용된 방법을 셰이더 그래프로 구현할 수 있다면 얼마나 좋을까요?
출처 : https://docs.unity3d.com/Packages/com.unity.shadergraph@17.0/changelog/CHANGELOG.html
셰이더 그래프는 완벽한 상태로 나온 기능이 아닙니다. 계속해서 업데이트하고 있는 기능입니다.
그렇기에 아직 존재하지 않는 기능도 있고 수정된 기능도 있습니다.
제가 도전하는 것 또한 그러한 것이 아닐까 싶었지만 결국 방법을 찾아냈습니다.
출처 : https://forum.unity.com/threads/2d-graphics-urp-12-now-available-with-unity-2021-2-beta.1139596/
제가 사용하고자 하는 노드는 Unity 2021.2버전에서 추가되었습니다.
2D Light Texture 노드는 Sprite Custom Lit Shader Graph에서 사용할 수 있는 노드입니다.
출처 : https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@17.0/manual/2d-customlit.html
2D Light Texture에 대한 문서는 따로 없지만 Custom Lighting in 2D 문서에서 정보를 찾아볼 수 있습니다.
문서에 따르면 2D Light Texture는 4개의 Blend Style에 대한 렌더 텍스처를 제공해 줍니다.
Blend Style은 광원을 어떻게 처리할지 선택할 수 있는 것입니다.
Renderer 2D에 등록된 기본 Blend Style 4개입니다.
마지막에 with Mask가 붙은 광원은 별 차이가 없어 보이는군요.
with Mask가 붙은 블렌딩 스타일을 활용해 주기 위해선 Sprite Editor의 Secondary Textures에서 _MaskTex를 등록해 주어야 합니다.
기존과 다르게 RGB에서 R의 값이 높은 영역에만 빛이 들어가는 것을 볼 수 있습니다.
Renderer 2D에 적힌 Mask Texture Channel을 수정해주시면 다른 색깔 영역으로도 수정이 가능합니다.
4가지 블렌딩 스타일 모두 한 오브젝트에 중복하여 적용 가능합니다.
적절하게 사용하여 분위기를 만들어주는 것이 좋을 것 같군요.
저희는 2D Light Texture를 사용하기 위해 Sprite Custom Lit Shader Graph를 사용해야 합니다.
비교를 위해 아무것도 연결 안 했을 때의 모습을 보겠습니다.
Base Color에 설정된 색깔로 표시되는 걸 볼 수 있습니다.
빛이 존재함에도 영향을 안 받습니다.
Sprite Custom Lit은 별도의 처리를 안 했을 때는 Sprite Unlit과 동일한 결과를 내는군요.
2D Light Texture이 어떤 값을 가지고 있는지 확인해 보기 위해 간단하게 Sample Texture 2D와 Screen Position을 활용해 봤습니다.
오브젝트에 셰이더를 적용하고 빛을 쬐어봤습니다.
빛이 적용되는 범위에 하얀색이 칠해지는 것을 볼 수 있습니다.
빛의 색깔을 바꾸자 색깔의 영향을 받습니다.
2D Light Texture는 빛의 값을 저장하고 있는 거군요.
조명에서 블렌딩 스타일을 2번째로 수정해 봤습니다.
다시 처음으로 돌아가는군요.
2D Light Texture는 LightTex0으로 설정되어 있기에 1번째 블렌딩 스타일에 대한 값만 가져온 것입니다. 조명은 2번째 블렌딩 스타일인데 가져오는 값은 1번째 블렌딩 스타일이어서 이러한 결과가 나온 것입니다.
즉 Sprite Custom Lit 셰이더를 만드는 순간부터는 각각의 블렌딩 스타일에 대한 처리를 직접 제작해 주어야 하는 것을 알 수 있게 됐습니다.
하얗수록 1에 가깝고 검을수록 0에 가깝습니다. 그렇기에 빛의 세기를 Alpha에다가 집어넣어 주면 0에 가까울수록 투명해질 것입니다.
앞서 살펴본 2D Light Texture의 값을 활용할 수 있겠군요.
전체 셰이더 그래프입니다.
2D Light Texture에서 몇 번째 블렌딩 스타일을 사용할 것인지 체크해 줍니다.
이후 2D Light Texture의 RGB 중 가장 큰 값을 찾아주는데 빛의 세기를 고려해 주기 위해섭니다. 흰색과 빨간색, 파란색, 초록색은 동일한 빛의 세기로 간주되는 것이지요.
RGB의 가장 큰 값을 찾아내면 MainTex의 A와 비교하여 작은 값이 Alpha에 적용됩니다. 이렇게 하는 이유는 MainTex의 투명한 부분을 고려해 주기 위해서입니다.
또한 2D Light Texture의 값과 MainTex의 값을 곱하여서 빛의 색상을 적용해 줍니다.
왼쪽이 방금 만든 셰이더, 오른쪽이 기본 Sprite Lit 셰이더입니다.
오른쪽의 경우 4번째 블렌딩 스타일로 하였지만 _MaskTex를 적용 안 했기에 Additive 효과만 나타나는 것을 볼 수 있습니다.
왼쪽의 경우 의도한 대로 빛의 세기가 약한 곳일수록 투명해지는 것을 볼 수 있습니다.
여기까지만 보면 완성된 것 같아 보입니다.
하지만 문제가 존재합니다.
Shadow Caster 2D에 의해 만들어지는 그림자는 Light 2D의 Shadows 항목에 존재하는 Strength의 값에 따라 희미해질 수 있습니다.
이 값이 희미해질 경우 2D Light Texture에서의 값도 희미해집니다. 그렇기에 벽 그림자에 의해 가려져야 함에도 보이게 되는 것입니다.
그리고 어두울수록 투명해진다는 점은 배경이 빛의 영향을 안 받을 때나 유용한 것 같습니다. 배경이 빛의 영향을 받게 되면 배경에 그림자가 생깁니다. 그로 인해 그림자에 의해 안 보이는 것인지 물체가 투명해서 안 보이는 것인지 구분이 힘들어집니다.
배경이 그림자에 가리더라도 보이려면 그림자의 세기가 약해야 하는데 그림자의 세기가 약해지면 투명해져서 안 보여야 할 오브젝트가 보이게 됩니다.
문제가 일어나는 원인을 정리하자면 이렇습니다.
위 두 가지가 상충되어서 문제가 발생하는 것이지요. 이러한 점을 해결하기 위해선 셰이더 그래프가 하나 더 필요합니다.
지금 저희가 겪고 있는 문제를 요약하자면 2D Light Texture의 값이 배경은 0(검은색)이면 안 되고 오브젝트는 0(검은색)이어야 합니다. 검은색보다는 밝은색을 원하는 입장에선 굳이 배경이 0의 값을 가질 필요는 없다고 봅니다.
이번 셰이더 그래프에서는 명도를 활용해 보고자 합니다. 명도는 색상, 채도와 함께 HSV 색공간을 이루는 요소입니다. 명도는 색의 밝기를 나타냅니다.
물체 셰이더 그래프에서 max를 통해 구했던 빛의 세기는 사실 명도였던 것입니다.
이번 셰이더 그래프에서 핵심적인 아이디어는 기존 색에서 명도를 제한하는 것입니다. N을 기준으로 한다면 0(검은색)에서 N까지의 값은 N으로 고정시키는 것입니다.
물체 셰이더 그래프에서 사용했던 블렌딩 스타일을 선택해 주셔야 합니다.
RGB의 값이 들어오면 HSV로 변환한 뒤 V의 값을 Threshold(제가 정의한 프로퍼티입니다)부터 1까지의 값으로 제한합니다. 이후 다시 RGB로 변환해 줍니다.
가공된 RGB 색상과 MainTex의 색상을 곱해줍니다. 0에서 Threshold까지의 값이 Threshold으로 제한됐기에 아무리 값이 낮아지더라도 Threshold의 경계를 넘어서지 못합니다. 만약 Threshold이 회색의 값이라면 그림자로 인해 검은색이었던 공간은 회색이 곱해지기에 회색빛의 배경이 나올 것입니다.
정답입니다. 배경은 보이지만 캐릭터는 그림자에 가려서 안 보이게 됐습니다. 오른쪽 닌자는 기본 셰이더를 쓰기에 빛의 값이 그대로 적용됐습니다. 여기서 확인할 수 있는 것은 저희가 받는 빛은 0에서 1의 값 그대로라는 것입니다. 같은 영역이라도 오른쪽 닌자는 0의 값을 그대로 받기에 검은색이 나온 것입니다.
만약 그림자 속에 닌자가 아직도 반투명하게 보인다면 Light 2D에 존재하는 Shadows 항목의 Strength를 1로 설정해 주시거나 Global 조명에 영향을 받고 있는지 확인해 보시는 게 좋습니다.
물체에 사용된 셰이더 그래프는 0~1의 값을 사용하기에 0의 값이 아니게 될 경우 반투명하게 보입니다.
실제 적용을 한다면 이런 모습이겠군요.
유니티는 4개의 광원 블렌딩 스타일을 사용할 수 있습니다. 저희는 1개의 블렌딩 스타일에 대해서만 셰이더 그래프로 작성했습니다. 그렇기에 나머지 3개의 블렌딩 스타일은 무시됩니다.
추가로 다른 블렌딩 스타일을 쓰실 거면 셰이더 그래프에 추가적인 작업을 해주셔야 합니다.
또한 앞서 설명드렸듯이 검은색 계열의 색을 회색 계열로 값을 바꾸어서 사용하고 있습니다. 그렇기에 다른 셰이더를 쓰신다면 통일성을 주지 못할 수도 있습니다. (다른 셰이더는 여전히 검은색을 사용할 것이기 때문입니다)
이러한 점은 프로젝트에 맞게 수정하여서 적용해 주시는 것이 옳다고 봅니다.
오랜만에 유니티에 대한 글을 씁니다. 원래는 진행하던 프로젝트인 Square Square Square을 먼저 끝내야겠지만 아직은 장편을 연재하기에는 바쁜 것 같습니다. 그렇기에 여유가 생길 때까지는 단편으로 작성할 수 있는 내용을 위주로 작성하지 않을까 싶습니다. (정 작성할 게 없다면 Square Square Square을 진행할 거 같기도 합니다)