본문 바로가기
vfxai 잡학지식 vfxai 잡학지식

렌더 EXR의 NaN·Inf 픽셀이 블러 뒤 검은 구멍으로 번질 때

vfx 잡학지식 읽는 시간 약 7분 조회 2

렌더 EXR를 블러했더니 작은 검은 점이 넓은 구멍처럼 번진 적 있나요? 화면에 검게 보인다고 원본 픽셀 값도 0이라고 단정할 수는 없습니다. 먼저 NaN 픽셀과 Inf를 찾아야 블러 뒤 번지는 원인을 좁힐 수 있습니다.

이 글에서는 NaN 픽셀 하나가 간단한 평균 블러를 거치며 어떻게 커지는지 숫자로 확인하고, Nuke에서 이상값을 표시하고 무해화하는 순서를 살펴봅니다.

1. 검은 점이 곧 0은 아닙니다

EXR의 float 채널은 화면의 검정과 다른 값을 보관할 수 있습니다. 그중 NaN은 숫자로 표현할 수 없는 연산 결과를, Inf는 무한대 값을 뜻합니다. 뷰어가 이를 검정이나 다른 표시색으로 보여 줄 수 있으므로 화면색만 보고 픽셀 값을 판단하면 안 됩니다.

32bit Float 이미지가 넓은 밝기 범위를 어떻게 담는지는 32bit Float 이미지 설명에서, VFX에서 EXR을 쓰는 이유는 EXR과 PNG 비교에서 확인할 수 있습니다. 이번 문제는 파일 확장자보다 float 채널 안에 잘못된 값이 들어왔는지가 핵심입니다.

2. NaN 픽셀은 블러 커널에서 이웃으로 전파될 수 있습니다

3×3 평균 블러는 주변 9개 픽셀에 각각 1/9의 가중치를 곱해 더한다고 가정해 보겠습니다. 한 픽셀만 NaN이어도 계산에는 NaN × 1/9가 들어갑니다. 그 값을 건너뛰지 않고 합산하면 결과도 NaN입니다.

실제 수치 예시는 9×9 배열의 바탕을 모두 1.0으로 채우고 중앙 한 칸만 NaN으로 둔 뒤, 같은 3×3 평균 블러를 두 번 적용했습니다. 첫 입력의 NaN은 1개였고, 한 번 뒤에는 3×3 범위의 9개, 두 번 뒤에는 5×5 범위의 25개 픽셀로 늘었습니다. 퍼진 영역 밖의 값은 1.0으로 남았습니다.

NaN 픽셀 전파를 보여 주는 3×3 평균 블러 도식: 한 번에 1개에서 9개, 두 번에 25개로 증가
9×9 float32 배열 중앙의 NaN 한 개에 3×3 단순 평균 블러를 적용해 NaN 출력이 1개에서 9개와 25개로 늘어나는 계산 예시.

이 도식은 NaN을 제외하지 않고 커널 값을 그대로 누산하는 단순한 모델입니다. 실제 Blur, Defocus, VectorBlur나 렌더러의 텍스처 필터는 NaN을 무시하거나 다른 값으로 대체할 수 있어 결과가 다를 수 있습니다. 다만 필터가 NaN을 그대로 계산에 넣으면 한 픽셀이 더 넓은 오염 영역으로 바뀔 수 있습니다.

3. 렌더러에서는 잘못된 연산과 입력을 함께 확인합니다

NaN·Inf가 생기는 원인은 하나로 고정되지 않습니다. Pixar RenderMan의 디버깅 안내는 0으로 나누는 연산에서 Inf가, 음수의 제곱근 같은 유효하지 않은 연산에서 NaN이 나올 수 있다고 설명합니다. NaN이 들어 있는 텍스처를 멀리서 필터링하면 문제가 더 넓게 드러날 수 있다고도 보여 줍니다.

따라서 우선 원본 EXR과 렌더 로그에서 위치를 찾고, 해당 픽셀에 기여하는 셰이더·텍스처·AOV를 분리해 봅니다. Arnold 포럼에 올라온 한 로그 사례에는 NaN·Inf 샘플의 픽셀 좌표와 RGBA 값이 함께 기록됐습니다. 좌표가 찍히면 그 지점을 기준으로 관련 패스와 소스를 좁힐 수 있습니다.

특정 렌더러 버전의 버그도 별도로 확인해야 합니다. 예를 들어 Chaos의 V-Ray 7 Build 7.00.04 변경 기록에는 GPU에서 GeomScatter 인스턴스 VRayLight를 렌더할 때 발생하던 NaN 픽셀 수정이 적혀 있습니다. 이는 해당 조합의 사례이지, 모든 검은 픽셀이 같은 버그라는 뜻은 아닙니다.

4. Nuke에서 먼저 표시하고, 그다음 정리합니다

Blur 앞의 원본 EXR을 먼저 검사합니다. Foundry의 Nuke 17.1v2 문서에서 Expression의 지원 함수 목록에는 isnan, isinf, isfinite가 없습니다. 위치 확인에는 Pixel Analyzer를 사용할 수 있습니다. Full Frame 모드에서 전체 프레임 처리를 켜면 현재 Viewer에 보이는 영역 밖까지 확인하고, 의심 픽셀을 Viewer에서 선택해 swatch의 ! 표시로 NaN·Inf 여부를 확인합니다. 아래 Expression 식은 특수 검사 함수가 아닌 비교식을 이용하므로 실제 EXR에서 사용 중인 Nuke 버전으로 먼저 확인하세요.

목적Expression 식결과
이상값 위치 표시R 출력에 ((r-r)!=(r-r)) || ((g-g)!=(g-g)) || ((b-b)!=(b-b)) || ((a-a)!=(a-a))RGBA 중 하나라도 NaN·Inf면 1, 모두 유한값이면 0
R 채널 무해화((r-r)!=(r-r))?0:r유효한 값은 유지하고 NaN·Inf는 0으로 바꿈
G 채널 무해화((g-g)!=(g-g))?0:g유효한 값은 유지하고 NaN·Inf는 0으로 바꿈
B·A 채널 무해화((b-b)!=(b-b))?0:b, ((a-a)!=(a-a))?0:a각 채널에 같은 조건을 적용

이 식은 각 값에서 자기 자신을 빼어 NaN·Inf를 NaN으로 만든 뒤, NaN은 자기 자신과 같지 않다는 부동소수점 비교를 이용합니다. 마스크를 R 출력에 넣고 Viewer에서 R 채널을 보면 이상값이 밝게 표시되고, 채널별 치환식으로 해당 채널의 NaN·Inf를 0으로 바꿀 수 있습니다. 이 비교식은 Foundry 문서의 예제가 아니므로, 실제 샘플에서 먼저 시험한 뒤 적용합니다. 다른 레이어나 렌더 패스도 사용한다면 해당 채널에도 검사식을 적용해야 합니다.

0으로 바꾸면 잘못된 값의 전파는 막지만 해당 채널은 0이 되어 색이 어두워지거나, 알파 채널이면 투명해질 수 있습니다. 최종 이미지에서 구멍까지 복구하려면 원인이 된 셰이더나 텍스처를 고쳐 다시 렌더하거나, 마스크를 기준으로 유효한 주변 픽셀을 복원해야 합니다. 무해화는 누락된 원래 색을 되살리는 작업이 아닙니다.

검은 구멍은 검은색을 덮기 전에 NaN부터 찾습니다

블러 뒤 커진 얼룩을 바로 페인트로 덮으면 다음 프레임이나 다른 패스에서 다시 나타날 수 있습니다. 먼저 블러 앞의 원본 EXR을 검사하고, 마스크와 로그 좌표로 원인을 좁히십시오.

검은 구멍은 검은색을 덮을 문제가 아니라, 블러 전에 NaN·Inf를 찾아 고칠 문제입니다.

vfx 잡학지식

함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.