이번 포스팅은 Shader Graph에서 Unlit Shader 기반으로 커스텀 라이팅 구현 중 간단한 Lambert구현에 대해 작성합니다.
기본 Lambert 구현
기본적으로 Lambert를 구현하기 위해서는 Normal Vector와 Light Vector를 Dot연산해서 구현합니다.
하지만 Normal Vector 노드는 있지만 Light Vector 노드는 없습니다, 그래서 Custom Function이라는 노드를 이용해 코드를 작성해서 기능을 만들어야합니다.
#if SHADERGRAPH_PREVIEW
lightDir = half3(0.5f, 0.5f, 0.0f);
lightColor = 1;
#else
Light light = GetMainLight();
lightDir = light.direction;
lightColor = light.color;
#endif
Custom Function 노드를 생성후 톱니 버튼을 누른 뒤 위 사진 우측처럼 세팅합니다.
그리고 나머지 NDotL연산을 하면 램버트가 구현됩니다.
Custom Function 외부 파일 불러오기
이전에는 위 Type을 String으로 해서 노드에 직접 코드를 입력헀지만 기본적으로 매우 불편합니다.
그래서 유니티 디렉토리 내부에 .hlsl 확장자 파일을 만들어서 코드를 작성해도 동작하게 됩니다.
하지만 따로 hlsl 파일 생성하는 기능이 없어 메모장이나 아무 파일만들어서 직접 확장자 변경해야합니다.
Shadow 처리
이슈 발견
위 자료에 있는 내용을 참고해서
void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
{
#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = 1;
DistanceAtten = 1;
ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
half4 clipPos = TransformWorldToHClip(WorldPos);
half4 shadowCoord = ComputeScreenPos(clipPos);
#else
half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
Light mainLight = GetMainLight(shadowCoord);
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowAtten = mainLight.shadowAttenuation;
#endif
}
위 코드를 작성하고 자료에 있는 노드대로 구현했지만
작동되지 않습니다.
이슈 해결
URP의 Include 형식 쉐이더들은 Packages/Universal RP/Shaders에 들어 있습니다, 해당 이슈를 추적하기 위해 GetMainLight()함수 부터 추적했습니다.
일단 핵심 원인은 Shadows.hlsl 파일의 위 코드입니다, 쉐이더가 그림자 데이터를 요청하지 않거나 쉐도우가 꺼진 경우에 무조권 1.0을 리턴한다는 내용인데 Unlit Shader가 그림자 데이터를 요청하지 않고 있는 것 같습니다.
그래서 위 조건문을 무시하고 아래 코드를 강제로 끄집어내서 제 CustomLight 함수에 수정을 해봤습니다.
/**
* worldPos : 월드 픽셀 포지션
* lightDir : 메인 라이트 방향 벡터
* lightColor0 : 메인 라이트 컬러
* distanceAtten : 컬링마스크에 의해 컬링되면 1, 아니면 0 (No확실)
* shadowAtten : Cast Shadow 값
**/
void CustomLight_half(float3 worldPos, out half3 lightDir, out half3 lightColor0, out half distanceAtten, out half shadowAtten)
{
#if SHADERGRAPH_PREVIEW //! 쉐이더 그래프 프리뷰에서 보이는 결과
lightDir = half3(0.5, 0.5, 0);
lightColor0 = 1;
distanceAtten = 1;
shadowAtten = 1;
#else
#if SHADOWS_SCREEN //! Screen Space Shadow 사용할때 (No확실)
half4 clipPos = TransformWorldToHClip(worldPos);
half4 shadowCoord = ComputeScreenPos(clipPos);
#else
half4 shadowCoord = TransformWorldToShadowCoord(worldPos); //! 월드 공간좌표를 그림자 공간 좌표로 변환하는 함수 (No확실)
#endif
Light mainLight = GetMainLight(); //! Lighting.hlsl 함수로 라이트 정보 및 쉐도우 데이터 구조체 얻어옴
lightDir = normalize(mainLight.direction); //! 메인 라이트 벡터
lightColor0 = mainLight.color; //! 메인 라이트 칼라
distanceAtten = mainLight.distanceAttenuation; //! 컬링마스크에 의해 컬링되면 1, 아니면 0 (No확실), 라이트맵 상황때는 다름
//shadowAtten = mainLight.shadowAttenuation; //! <ㅗ
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData(); //! 쉐도우 감쇠값
half4 shadowParams = GetMainLightShadowParams();
shadowAtten = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), TransformWorldToShadowCoord(worldPos), shadowSamplingData, shadowParams, false);
#endif
}
Shadow Cast와 Receive가 정상적으로 작동하게 됬습니다.
삽질
해당 #pragma 코드를 추가하면 된다고 하지만 Shader Graph에서 pragma 코드를 넣을 곳을 못찾음,
키워드 기능을 사용해서 포함할 수 있다고 주장해서
_MAIN_LIGHT_SHADOW, _MAIN_LIGHT_SHADOWS_CASCADE, _RECEIVE_SHADOWS_ON/OFF 등 시도해봤지만 에러나고 안됨
https://www.youtube.com/watch?time_continue=619&v=DOLE4nrK97g&feature=emb_title
위 영상에서 Unlit 쉐이더에 그대로 구현을 PT하고 있지만 영어를 잘 못 들어서 놓친것인지 영상속에서는 Shadow Receive가 잘됨
https://forum.unity.com/threads/turn-off-receive-shadows-on-custom-pbr-graph-lwrp.657814/
위 글은 PBR 쉐이더에서 Receive Shadow끄는 법을 알려주고 있는데 지금 상황하고 맞지 않음, 응용해서 _RECEIVE_SHADOWS_ON를 해봤지만 안먹힘
'Unity > TA' 카테고리의 다른 글
URP 커스텀 포스트프로세싱 - 1 (0) | 2022.03.29 |
---|---|
[유니티] 라이트맵이 PC와 안드로이드(모바일)에서 달라보이는 이슈 (0) | 2020.05.22 |
유니티 메쉬 Inspector 정보 설명 (0) | 2020.05.22 |
[Unity URP] 기존 프로젝트 URP로 스위칭하기 (0) | 2020.02.22 |
라이트맵 굽기 (0) | 2020.01.10 |
WRITTEN BY
- CatDarkGame
Technical Artist dhwlgn12@gmail.com