2015년 11월 27일 금요일

Unity 서피스 셰이더에서 discard나 return을 사용할때 주의할 점

사내 프로젝트에서 요상한 문제를 겪었다.

다른 폰에서는 별다른 문제가 없는데, 유독 Galaxy S4(초기모델)에서만 픽셀이 깨지는 현상이 발견된 것이다.

처음에는 셰이더의 Spec 연산시에 음수(-)값이 나오는 것이 아닌가 하는 문제로 좁혀져서 각종 벡터연산에 saturate()를 집어넣는 방식으로 해결하려 했지만 결국 버그가 잡히지 않았다. 게다가 알파채널이 있는 경우에만 발생하는 것도 이상했다.

그러는 가운데, Galaxy S4초기 모델의 특징은 PowerVR이 사용되었다는 것에 착안하고 구글링을 해보니 유사한 현상이 Stackoverflow에 보고된 사례를 접하게 되었다.

내용을 읽어보면 PowerVR의 병렬처리 구조상 if()문 내에서 discard나 return문이 있을 경우에 원치않는 statement가 처리될 수도 있다는 것이다.

예를 들어서 우리의 경우 mask텍스처의 b채널에 alpha정보를 보관하고 있었기에 다음과 같이 처리를 하였다.


inline void surf (Input IN, inout SurfOut o)
{
   fixed4 mask = tex2D(_MaskMap, IN.uv_MainTex);
if( mask.b <= _Cutoff )
discard;

fixed4 main = tex2D(_MainTex, IN.uv_MainTex);
fixed4 n = tex2D(_BumpMap, IN.uv_MainTex);
        .
        .
        .
}

이렇게 코딩하면 오류가 발생한다는 것이다.

이 코드를 바로 잡으려면

inline void surf (Input IN, inout SurfOut o)
{
        fixed4 mask = tex2D(_MaskMap, IN.uv_MainTex);
if( mask.b <= _Cutoff )
        {
discard;
        }
        else
        {
         fixed4 main = tex2D(_MainTex, IN.uv_MainTex);
           fixed4 n = tex2D(_BumpMap, IN.uv_MainTex);
                .
                .
                .
        }
}

이렇게 해야 GPU의 병렬처리시에 오류를 막을 수 있다.

if() {}블록 다음에는 반드시 else {}블록으로 처리를 해야만 GPU가 제대로 지원한다는 것을 명심하자. 

댓글 없음:

댓글 쓰기