일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Cartoon Rendering
- Specular
- 프로그래밍 기초
- Rim Light
- ColorGradingLutPass
- Private Bytes
- ASW(Application SpaceWarp)
- 3d
- VR
- 게임 수학
- Virtual Byte
- URP로 변경
- Windows Build
- Three(Two) Tone Shading
- 벡터
- 가상 바이트
- Toon Shader
- C언어
- working set
- 메모리 누수
- OculusMotionVectorPass
- Cell Look
- 개인 바이트
- AppSW
- Cell Shader
- 작업 집합
- URP
- Today
- Total
WinCNT
특정 시점(View)에서 SAMPLE_GI의 값이 이상해져서 Bloom이 섬광탄 터진 것처럼 매우 밝아지는 이슈 본문
특정 시점(View)에서 SAMPLE_GI의 값이 이상해져서 Bloom이 섬광탄 터진 것처럼 매우 밝아지는 이슈
WinCNT_SSS 2024. 12. 14. 16:30발생한 이슈
한눈에 알기 쉽게 타이틀에 어느 정도 원인까지 쓰긴 했지만, 최초로 이슈에 대해서 인지한 것은 마치 누군가가 거울로 태양 빛을 반사해 눈에 쏘는 것 마냥 눈뽕(?)이 있다고 느껴졌을 때였다
좀 더 조사해보니 시점(카메라)의 특정 위치와 각도에서 오브젝트의 일부분이 엄청나게 반짝거린 다는 것을 알게 되었다
이슈 상세
화면 크기에도 영향을 받고 위치가 0.01만 틀어져도 이슈가 발생하지 않고 해서 재현이 힘들었다
아래는 Editor에서 어찌저찌 재현한 이미지다
버그 처음 봤을 땐 이거 생각났음ㅎㅎ
원인 추척
섬광탄마냥 눈뽕을 만드는 건 타이틀에도 살짝 적었듯이 바로 Bloom이었다
그렇다고 Bloom이 나쁜 건 아니다 이것때문에 끌 수도 없고
Bloom은 1이 넘어가는 픽셀에 대해서 자신의 역할을 다 했을 뿐이다
그래서 실제로 문제가 되는 부분을 찾기로 했다
어떻게 찾았냐면…그냥 차례로 값들을 return해서 이슈가 발생하는 부분을 특정했다!
(결국 이런 단순한 방법이 제일 빠른 방법인 것 같다)
발생 원인
그래서 바로 원인은 어디었냐면 바로 SAMPLE_GI이었다!
return SAMPLE_GI(IN.lightmapUV, IN.vertexSH, _Normal);
거기에서 또 뭐가 문제인지 찾기 위해 SampleSHPixel의 내부의 값들을 하나씩 확인하거나 했지만 이건 삽질이었다(그리고 구면 조화 함수는 아직도 잘 모르겠기도 하고…)
문제는 그냥 프래그먼트 셰이더에서 법선(Normal)을 정규화(Normalize)하지 않아서 발생하는 것이었다
순간 ‘버텍스 셰이더에서 다 정규화했는데 문제 없어야 하는 거 아닌가?’ 싶었지만 잘 생각해보니 래스터라이저에서 보간될 때 법선의 길이가 1로 유지된다는 보장이 없었다
일단 다음의 코드로 시각적으로 확인을 해봤다
// Fragment Shader
if (length(IN.normalWS) > 1.0000001) // 오차 방지를 위해 1보다 약간 크게 설정
{
return half4(1,0,0,1);
}
else if (length(IN.normalWS) < 0)
{
return half4(0,1,0,1);
}
else
{
return half4(0,0,1,1);
}
아래는 그 결과인데 잘 보면 노멀의 길이가 1이 넘어가는 부분(빨간색)이 있음을 알 수 있다
좀 더 보기 쉽게 노멀의 길이가 1이 넘어가는 부분만 빨간색, 다른 부분은 검정색으로 출력해봤다
하지만 ‘확실히 Normal의 길이가 1이 넘어가긴 하지만 이게 문제가 맞을까?’라고 생각해서 마지막으로 다음도 실험해봤다
if (length(IN.normalWS) > 1.0000001) // 오차 방지를 위해 1보다 약간 크게 설정
{
return half4(SAMPLE_GI(IN.lightmapUV, IN.vertexSH, IN.normalWS), 1);
}
else if (length(IN.normalWS) < 0)
{
return half4(0,0,0,1);
}
else
{
return half4(0,0,0,1);
}
다행히도(?) 이번 이슈의 원인이 맞았다…
해결 방법
원인인 부분을 확실하게 알았으니 해결 방법도 마찬가지
그냥 Normal을 Normalize해주면 된다
우선 적당히 확인해보자
return half4(SAMPLE_GI(IN.lightmapUV, IN.vertexSH, normalize(_Normal)), 1.0);
같은 씬인데 이슈가 해소된 걸 볼 수 있었다
물론 실제로 수정은 Fragment Shader에서 Normal Map 계산이 끝난 부분에서 바로 정규화를 하는 게 좋다
이번에도 URP의 Lit 셰이더에서 사용되고 있는 함수를 가져왔다
half4 frag(Varyings IN) : SV_Target
{
//...생략...
#ifdef _NORMALMAP_ON
half3 _Normal = SampleNormal(IN.texcoord, IN.normalWS, IN.tangentWS, IN.bitangentWS, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap));
#else
half3 _Normal = IN.normalWS;
#endif
_Normal = NormalizeNormalPerPixel(_Normal);
//...생략...
return half4(SAMPLE_GI(IN.lightmapUV, IN.vertexSH, _Normal), 1.0);
//...생략...
}
짜잔~!
사족) SAMPLE_GI(정확히는 SampleSHPixel)의 어디가 문제일까
이슈 해결은 했지만 사실 완전한 원인 파악, 즉 Normal의 길이가 1을 넘는다고 해도 딱히 극단적인 값이 아닌데 SAMPLE_GI(정확히는 SampleSHPixel)에서 극단적인 값이 출력되는가에 대해서가 궁금해졌다
그렇다고 구면 조화 함수에 대해서 아는 게 없으니 이것도 똑같이 찾을 뿐…
일단 SampleSHPixel의 내부를 그대로 옮겨와서 값들을 확인해봤다
// Fragment Shader
real4 SHCoefficients[7];
SHCoefficients[0] = unity_SHAr;
SHCoefficients[1] = unity_SHAg;
SHCoefficients[2] = unity_SHAb;
SHCoefficients[3] = unity_SHBr;
SHCoefficients[4] = unity_SHBg;
SHCoefficients[5] = unity_SHBb;
SHCoefficients[6] = unity_SHC;
float4 shAr = SHCoefficients[0];
float4 shAg = SHCoefficients[1];
float4 shAb = SHCoefficients[2];
float4 shBr = SHCoefficients[3];
float4 shBg = SHCoefficients[4];
float4 shBb = SHCoefficients[5];
float4 shCr = SHCoefficients[6];
// Linear + constant polynomial terms
float3 res = SHEvalLinearL0L1(IN.normalWS, shAr, shAg, shAb);
// Quadratic polynomials
res += SHEvalLinearL2(IN.normalWS, shBr, shBg, shBb, shCr);
return half4(res, 1);
확인해보니 SHEvalLinearL0L1까지는 문제 없는데 SHEvalLinearL2가 이상한 것을 알 수 있었다
// Quadratic polynomials
// res += SHEvalLinearL2(IN.normalWS, shBr, shBg, shBb, shCr);
res = SHEvalLinearL2(IN.normalWS, shBr, shBg, shBb, shCr);
return half4(res, 1);
SHEvalLinearL2는 이런 함수였다
real3 SHEvalLinearL2(real3 N, real4 shBr, real4 shBg, real4 shBb, real4 shC)
{
real3 x2;
// 4 of the quadratic (L2) polynomials
real4 vB = N.xyzz * N.yzzx;
x2.r = dot(shBr, vB);
x2.g = dot(shBg, vB);
x2.b = dot(shBb, vB);
// Final (5th) quadratic (L2) polynomial
real vC = N.x * N.x - N.y * N.y;
real3 x3 = shC.rgb * vC;
return x2 + x3;
}
우선 Normal이 문제인 것을 알았으니 real4 shBr, real4 shBg, real4 shBb, real4 shC 등은 정상적이라고 가정하고 살펴봤다
(혹시 그쪽에 문제가 있다고 해도 필자의 실력으론 모른다…)
우선 다음과 같이 출력해봤다
real4 vB = IN.normalWS.xyzz * IN.normalWS.yzzx;
return vB;
문제가 되고 있던 게 vB의 x와 z였다
위의 식에서 알 수 있듯이 vB는 그냥 Normal의 요소들을 곱하거나 제곱한 값들이다
당연히 노멀의 요소들을 여러 방법으로 출력해봤는데 역시나 해당 이슈가 발생했다
그래서 아예 RenderDoc의 힘을 빌려서 문제가 발생하는 Pixel의 값을 확인해봤다
6만이 넘어가니 섬광탄에 버금가는 Bloom이 생길 수 밖에…
일단 Debug를 눌러서 내부의 값도 확인해봤다
Normal, Tangent, Bitangent 값인 TEXCOORD3~5가 어마무지한 값들이 되어 있었다
즉 구면 조화 함수를 운운하기 전에 애초부터 노멀이 이상하다는 이야기였다
참고로 UV 좌표인 TEXCOORD0도 이상한 값들이 되어 있었는데 이것도 연관이 있을까?
탄젠트 스페이스는 텍스처 스페이스라고도 불리고 탄젠트를 구할 때 UV좌표를 사용하기도 하니…
그래도 이번 이슈와는 상관 없으려나
그리고 SV_POSITION의 w 값도 신경쓰인다
주변의 픽셀들은 대략 4.3이었는데 문제가 발생한 픽셀만 -368이라니
아무튼 프래그먼트 셰이더에서 정규화를 해주면 당연히도 정상적인 값을 확인할 수 있었다
물론 프래그먼트 셰이더로 넘어오는 값은 그대로이다
마지막으로 버텍스 셰이더에서 이미 이상한 값이 되었는지도 대략 확인해봤지만 딱히 이상한 값은 찾지 못 했다
마무리
이번에도 Normal에 관한 이슈였다
사소한 부분을 생략하는 것으로 이런 대참사가 일어나다니 앞으로는 코딩할 때 주의해야겠다
근데 이 이슈 필자는 꽤나 거슬렸는데 다른 사람들은 그다지 신경을 안 쓰는 듯한 기분이 드는 건 왜일까?
참고 자료
이번엔 딱히 없다
'Unity > Unity 개발 중 발생한 이슈 정리' 카테고리의 다른 글
Scale을 늘리면 Normal값이 계속 커지던 이슈 (0) | 2024.07.16 |
---|---|
Nova Shader의 ParticlesUberLit이 VR모드에서 정상적으로 렌더링 되지 않던 이슈 (0) | 2024.04.02 |
Target Android SDK not installed 해결을 위한 Android SDK설치(Command-line tools 사용) (0) | 2024.03.04 |
Directional Light가 없는 Scene에서 Lighting이 이상해지는 이슈(Unity 2021.3.10f1 Personal) (0) | 2024.02.15 |
AppSW(ASW)와 Motion Vector로 인해, Windows용 빌드에서 렌더링이 이상해지는 이슈(VR기기에서는 정상) (0) | 2023.12.20 |