반응형

셰이더는 디바이스를 타는 경우가 많아 코드 작성시 주의 해야할 사항이 많습니다.

얼마전에 공유드렸던 vert_img가 노트4에서만 죽는 거 처럼 안드로이드 기기는 워낙 다양한 GPU를 사용하다보니

로그에 남지 않는 크래쉬 발생이 많고, 또한 어디서는 정상으로 나오던 것들이 어디서는 잘못 렌더 되는 경우가 많습니다.


최근에는 디스토션 셰이더가 아이폰 6에서만 화면이 어둡게 나오는 현상이 발견되어 해당 셰이더를 못쓰는 상황도 발생했습니다..ㅠ


이번에 공유 드릴 내용은 셰이더코드를 어떻게 작성하느냐에 따라 프레임렉을 유발할수 있는 부분에 대해서 나누고자 합니다.


물 셰이더를 제작하면서 버텍스 연산하는 부분을 함수로 따로 뺀뒤 연산하도록 작업이 됐었는데,

갤럭시 시리즈에서만 버텍스 애니메이션시 프레임 렉을 일으키는 문제가 발생했습니다. LG시리즈나 아이폰에서는 이상이 없는데..



아래는 버텍스 연산 내용을 처리한 셰이더 코드 입니다.



fixed getWave(fixed3 s)

{

fixed time = _Time.y * _WaveSpeed;

fixed z = sin(time + s.y * _WaveCycle) * _WavePow;

return z;

}

v2f vert(appdata_base v)

{

v2f o;

v.vertex.z += getWave(v.vertex);


  return o;

 }



-- 프레임 렉을 줄이는 방법--


1. 타임함수의 y로 계산하지 않고 x로 계산하고 *20을 해주면 소수값 연산이 줄어들어 프레임렉을 줄일 수 있습니다.


2. Time을 사용하지 않고 sin을 사용하면 프레임렉을 줄일수 있다. 단, 연산부하가 늘어납니다.


3. 아래처럼 함수로 따로 빼지 않고 버텍스 연산하는데 포함 시키면 프레임렉을 어느정도 완화시킬수 있습니다.


v2f vert(appdata_base v)

{

v2f o;


fixed time = _Time.y * _WaveSpeed;

fixed zzz = sin(time + v.vertex.y) * _WavePow;


v.vertex.z += zzz;


   return o;

}




반응형
반응형

 아래 코드는 라디알 후처리를 처리하는 셰이더 코드입니다...

근데 이 코드가 노트4를 계속 죽이는 문제를 일으켰습니다...




             #pragma vertex vert_img 

             #pragma fragment frag 

             #pragma fragmentoption ARB_precision_hint_fastest 

   

             #include "UnityCG.cginc" 

   

             uniform sampler2D _MainTex; 

             uniform half4 _MainTex_TexelSize; 

             uniform half _BlurStrength; 

             uniform half _BlurWidth; 

             uniform half _imgWidth; 

             uniform half _imgHeight; 

   

             half4 frag (v2f_img i) : COLOR  

             { 

                 half4 color = tex2D(_MainTex, i.uv); 

         

                 // some sample positions 

                 half samples[10]; 

                 samples[0] = -0.08; 

                 samples[1] = -0.05; 

                 samples[2] = -0.03; 

                 samples[3] = -0.02; 

                 samples[4] = -0.01; 

                 samples[5] =  0.01; 

                 samples[6] =  0.02; 

                 samples[7] =  0.03; 

                 samples[8] =  0.05; 

                 samples[9] =  0.08; 

         

                 //vector to the middle of the screen 

                 half2 dir = 0.5 * half2(_imgHeight,_imgWidth) - i.uv; 

         

                 //distance to center 

                 half dist = sqrt(dir.x*dir.x + dir.y*dir.y); 

         

                 //normalize direction 

                 dir = dir/dist; 

         

                 //additional samples towards center of screen 

                 half4 sum = color; 

                 for(int n = 0; n < 10; n++) 

                 { 

                     sum += tex2D(_MainTex, i.uv + dir * samples[n] * _BlurWidth * _imgWidth); 

                 } 

         

                 //eleven samples... 

                 sum *= 1.0/11.0; 

         

                 //weighten blur depending on distance to screen center 

                 /* 

                 half t = dist * _BlurStrength / _imgWidth; 

                 t = clamp(t, 0.0, 1.0); 

                 */  

                 half t = saturate(dist * _BlurStrength); 

         

                 //blend original with blur 

                 return lerp(color, sum, t); 

             } 

             ENDCG 





어느 부분에서 크래쉬를 일으켰을까...

바로 vert_img  ~~~!!!



이 녀석이 노트4에서 앱을 죽이는 역할을 담당했네요...


이 코드를 아래와 같이 바꾸자 노트4 크래쉬가 해결됐습니다~~



             #pragma vertex vert

             #pragma fragment frag 

             #pragma fragmentoption ARB_precision_hint_fastest 

             #pragma exclude_renderers gles3 d3d11_9x xbox360 xboxone ps3 ps4 psp2

   

             #include "UnityCG.cginc" 

   

             uniform sampler2D _MainTex; 


             float4 _MainTex_ST;


             uniform half4 _MainTex_TexelSize; 

             uniform half _BlurStrength; 

             uniform half _BlurWidth; 

             uniform half _imgWidth; 

             uniform half _imgHeight; 



  struct appdata_t {

float4 vertex : POSITION;

float2 texcoord : TEXCOORD;

};

struct v2f {

float4 vertex : SV_POSITION;

float2 texcoord : TEXCOORD;

};


v2f vert (appdata_t v)

{

v2f o;

o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);

o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

return o;

}




             half4 frag (v2f i) : COLOR  

             { 

                 half4 color = tex2D(_MainTex, i.texcoord); 

         

                 // some sample positions 

                 half samples[10]; 

                 samples[0] = -0.08; 

                 samples[1] = -0.05; 

                 samples[2] = -0.03; 

                 samples[3] = -0.02; 

                 samples[4] = -0.01; 

                 samples[5] =  0.01; 

                 samples[6] =  0.02; 

                 samples[7] =  0.03; 

                 samples[8] =  0.05; 

                 samples[9] =  0.08; 

         

                 //vector to the middle of the screen 

                 half2 dir = 0.5 * half2(_imgHeight,_imgWidth) - i.texcoord; 

         

                 //distance to center 

                 half dist = sqrt(dir.x*dir.x + dir.y*dir.y); 

         

                 //normalize direction 

                 dir = dir/dist; 

         

                 //additional samples towards center of screen 

                 half4 sum = color; 

                 for(int n = 0; n < 10; n++) 

                 { 

                     sum += tex2D(_MainTex, i.texcoord + dir * samples[n] * _BlurWidth * _imgWidth); 

                 } 

         

                 //eleven samples... 

                 sum *= 1.0/11.0; 

         

                 //weighten blur depending on distance to screen center 

                 /* 

                 half t = dist * _BlurStrength / _imgWidth; 

                 t = clamp(t, 0.0, 1.0); 

                 */  

                 half t = saturate(dist * _BlurStrength); 

         

                 //blend original with blur 

                 return lerp(color, sum, t); 

             } 

             ENDCG




아.. 이 문제 때문에 정말 힘든 하루였음..ㅠㅠ


노트4에서는 절대 vert_img 가 들어간 셰이더를 사용하면 안됩니다!

반응형
반응형

바닥 아웃라인 셰이더를 만든후 그 위에 올라갈 오브젝트의 소팅에 문제가 있다는 것을 발견했습니다.

 

 

이 때 사용된 렌더큐를 따져보면

 

바닥 렌더 큐(Pink) - Tags {"Queue"="Transparent+100" "IgnoreProjector"="True" "RenderType"="Transparent"}

 

오브젝트 렌더 큐(Blue) - Tags {"RenderType"="Opaque"}

 

큐 순서를 따져보면 Transparnet 가 Opaque보다 소팅이 위에 찍히기에 당연한 결과라 할 수 있습니다.

 

그래서 오브젝트 렌더 큐를 아래와 같이 변경을 해보았습니다.

 

 

Tags {"Queue"="Transparent+200" "IgnoreProjector"="True" "RenderType"="Transparent"}

 

그리고 유니티로 돌아왔지만, 여전히 소팅 문제는 해결되지 않았습니다.

원인을 알수 없어 이짓 저짓 다해보고 서피스 방식이었던 셰이더를 프라그로 바꿔보고 ZTest도 바꿔보고 다 해보았지만

여전히 바닥아래로 오브젝트가 찍히는 것입니다.

 

결국 뒤지다 뒤지다 알게 된 사실이 렌더 큐에 대한 것은 코드를 수정해도 실시간으로 바로 바뀌는게 아님을 알게 되었습니다.

 

셰이더가 생성되는 순서가 먼저이고 그 다음에 큐에 대한 것을 연산하는 것입니다.

그래서 다른 셰이더로 한번 바꾼후 다시 원래 셰이더로 돌아오니 큐가 제대로 찍히는 것입니다.

 

 

 

이게 버그인지 아니면 의도된 것인지 모를 일입니다.. 요상한 유니티..

 

 

 

반응형
반응형

유니티의 아웃라인 셰이더는 구글에 돌아다니는 레퍼런스가 많아 손쉽게 구현이 가능합니다.

하지만, 자세하게 여러가지를 따지다 보면 불편한 사항이나 제약이 많이 뒤따른다는 것을 쉽게 발견할 수 있습니다.

 

 

일반적인 공식으로 알려져있는 아래 수식대로 셰이더를 만들어 아래 오브젝트에 연결해 보면..

 

        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.normal);
        fixed2 offset = TransformViewToProjection(norm.xyz);
     
        o.pos.xy += offset * o.pos.z * _Outline;

        o.pos.z += 0.01 * _Outline;


        o.color = _OutlineColor;
        return o;

 

 

 

3D형태의 메쉬를 가진 오브젝트에는 잘 적용되지만 플랜 상태의 오브젝는 제대로 출려되지 않는 문제가 발생합니다. 아래 그림 참조

 

 

 

이 문제를 해결하기 위해서는 버텍스의 노멀 값 뿐 아니라 사이즈자체도 커지도록 한번 더 계산해 주는 수식을 넣어줘야 합니다.

 

fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.normal); //요 수식에서

 

fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.vertex + v.normal); //버텍스 값이 더해지도록 수식을 추가해 준다.

 

 

이렇게해도 제대로 출력되지 않을땐 아웃라인 패스 부분에 Cull back 을 추가해 줍니다.

 

 

 

그러면 아래 그림처럼 플랜에서도 정상적인 아웃라인을 만들어 지게 됩니다.

 

 

 

 

하지만 이 셰이더를 사용할 때 주의해야할 점이 있는데 바로 배칭이 안된다는 것입니다.

 

스태틱 배칭을 키고나면 버텍스 연산이 다르게 되어 아래그림처럼 엉뚱한 형태로 출력되게 됩니다.

 

 

 

위 셰이더를 사용하고자 할 때는 메터리얼을 따로 만들어 관리하는 것이 최적화에 도움이 될겁니다.

 

 

 

아래는 위 오브젝트의 아웃라인을 구현한 셰이더 코드입니다.

 

{
    Properties {
        _MainColor ("Main Color"Color) = (.5,.5,.5,1)
        _OutlineColor ("Outline Color"Color) = (0,0,0,1)

        _Outline ("Outline width"Range (010)) = 1
        _MainTex ("Base (RGB)"2D) = "white" { }
        _Rim ("Rim"Float) = 0.0
        _scale("Z Position(Only Plane)"FLoat) = 1.0

        //Sorting
        _ZTest ("ZTest Less = 4 / Always = 6 "FLoat) = 1
    }
 
    CGINCLUDE
    #include "UnityCG.cginc"
    #pragma target 3.0

    struct v2f {
        fixed4 pos : SV_POSITION;
        fixed4 color : COLOR;

    };
     
    uniform fixed _Outline;
    uniform fixed4 _OutlineColor;
    uniform fixed _Rim;
    uniform fixed _scale;
     
    v2f vert(appdata_base v) {
        
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        v.vertex.xyz += v.normal.xyz * _scale;
        fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_IT_MV, v.normal + v.vertex * _Outline);
        fixed2 offset = TransformViewToProjection(norm.xyz);
     
        o.pos.xy += (offset * o.pos.z * _Outline) * 0.001;

        o.pos.z += 0.01 * _Outline;


        o.color = _OutlineColor;
        return o;
    }
    ENDCG
 
    SubShader {
        Tags {"Queue"="Geometry+200" "IgnoreProjector"="True" "RenderType"="Opaque"}
         Blend One Zero
//         ZTest NotEqual
//         ZWrite on
        Pass
        {
            ColorMask 0
        }
//
        Pass
        {
            Name "OUTLINE"
            Tags { "LightMode" = "Always" }
            Cull off
            Blend SrcColor OneMinusSrcColor
            ZTest Always
//            ZWrite off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            fixed4 frag( v2f i ) : COLOR
            {
                fixed4 c = i.color;

                return i.color;
            }
            ENDCG
        }         
             
            CGPROGRAM
            #pragma surface surf Lambert

            struct Input {
                fixed2 uv_MainTex;
                fixed3 worldPos;
            };


            sampler2D _MainTex;
            //sampler2D _BumpMap;
            uniform fixed3 _MainColor;
            void surf(Input IN, inout SurfaceOutput o) {
//                IN.worldPOS.z;
                o.Emission = tex2D(_MainTex, IN.uv_MainTex).rgb * _MainColor * _Rim;
            //    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            }
            ENDCG
 
    }

}

반응형

+ Recent posts