반응형

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

얼마전에 공유드렸던 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
 
    }

}

반응형
반응형

유니티에서 제공하는 안드로이드용 알파 채널 텍스쳐중 디바이스 호환성이 뛰어난 것은 3가지 밖에 없습니다.

RGBA32 BIT / RGBA 16BIT / ETC2 8BIT

 

ETC 2 8Bit는 아직 디바이스 점유율이 안좋아 사용이 불가한 상황입니다.. 현재 시장에 배포된 디바이스중 50프로는 사용 불가..

(점유율 현황 - https://developer.android.com/about/dashboards/index.html --- Open GL Version 이 2.0인 것들은 지원하지 않습니다.)

 

 

결론적으로 RGBA 16 Bit 와 RGBA 32 Bit 두 종류밖에 쓸 수 없다는 건데..

RGBA 32 Bit 는 무압축 포맷이라 메모리 이슈가 빵빵터져 앱이 계속 죽어 나갈 것이고..(특히 아이폰.. 6도 1기가라는..)

RGBA 16 Bit 는 쓰레기 품질에 압축률도 엉망이라 아트를 볼 줄 아는 사람이 한명이라도 있는 프로젝트에서는 거품 물고 반대할 것이 뻔합니다.

 

1024의 알파 채널이 있는 텍스쳐를 사용하게 될 경우

RGBA 32 Bit의 용량은 4MB

RGBA 16 Bit의 용량은 2MB

 

사이즈가 작은 캐주얼 게임이라면 별 상관없을거 같긴한데.. 대규모  RPG같은 알파 텍스쳐 사용량이 100개 이상 넘어가는 프로젝트라면

너무 큰 클라이언트 용량으로 출시가 힘든 상황이 올수도 있습니다. 특히 중국쪽은 더더욱..

 

 

이런 문제들을 해결하기 위해서 Etc1 4Bit 2장을 이용하면 간단히 해결이 됩니다. 한장은 오파큐텍스쳐로 쓰고 한장은 알파텍스쳐로 쓰면 되지요..

 

1024 텍스쳐의 Etc1 4bit의 용량은 고작 512 KB~~ 2장을 써도 1024 KB~~  쓰레기 품질의 RGBA 16Bit 한장을 쓸때보다도 1/2 용량이라는 어마어마한 이득을 취하면서도 괜찮은 품질을 얻을 수 있습니다.

 

이 방법을 쓰기 위해서는 셰이더를 새로 짜고 텍스쳐 분리 작업을 하고 여러가지 해야할 일거리가 여기저기 터지지만 메모리 이슈로 게임을 출시 못하는 거보다는 낫지 않을까?

 

이방법을 쓸 때 유의 사항.

1. 1드로우였던게 2드로우가 되므로 드로우콜에 유의하자.

2. 알파 채널이 필요한 경우도 반드시 생기므로 알파 채널에 대한 연산 부분도 포함하자.

 

 

아래는 etc1 2장으로 처리할 수 있는 간단한 알파 블렌드 셰이더 코드 입니다.

 

 

Properties    {
        _TintColor ("Tint Color"Color) = (0.5,0.5,0.5,0.5)
        _MainTex ("MainTex"2D) = "white" {}
        _Alpha ("Alpha"2D) = "white" {}
    }

    SubShader {

        Tags {"IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent"}
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

        uniform sampler2D _MainTex; 
        uniform fixed4 _MainTex_ST;

        uniform sampler2D _Alpha; 
        uniform fixed4 _Alpha_ST;

        uniform fixed4 _TintColor;

        struct v2f 
        {
            fixed4 pos : SV_POSITION;
            fixed2 uv : TEXCOORD0;
            fixed2 uv1 : TEXCOORD1;
            fixed4 color : COLOR;
        };


        v2f vert (appdata_full v) 
        {
            v2f o;

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

            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            o.uv1 = TRANSFORM_TEX(v.texcoord, _Alpha);

            return o;
        }

        fixed4 frag(v2f i) : COLOR {
                 fixed4 _MainTex_var =  tex2D(_MainTex,i.uv);
                fixed4 _Alpha_var = tex2D(_Alpha,i.uv1);

                  fixed3 emissive = _MainTex_var.rgb * i.color.rgb;
                  fixed node_9936 = _Alpha_var.r;     

                fixed3 finalColor = emissive * (_TintColor * 2);

                return fixed4(finalColor, (i.color.a * _MainTex_var.a * node_9936));
            }
            ENDCG
        }
    }//SubShader

반응형
반응형

셰이더 코드에서 완전히 빛 연산을 안하도록 만들었는데도

Point Light 연산이 들어가는 것을 보고 경악..

이유는 Albedo 때문이었다는..

Albedo가 아닌 Emmision을 사용하면 해결된다..

이상한 유니티..

반응형
반응형

간단한 서피스 커스텀 셰이더를 만들었는데 알파 값을 0으로 아무리 해도 알파가 안빠지는 현상이 계속됐다.

여러가지 테스트를 해보니 SurfaceOutputCustom 에 Normal 과 Emission 값을 넣어주지 않으면 알파 값이 계속 빠지지 않는 것이다..

#pragme에 keepalpha 문을 넣어주는 것은 당연하고..

 

서피스 셰이더 제작후 알파값이 빠지지 않으면 아래의 세 구문을 꼭 넣어주자.

 

Blend One OneMinusDstAlpha

 

#pragma surface surf keepalpha

 

struct SurfaceOutputCustom
{
      fixed3 Albedo;
      fixed3 Normal;
      fixed3 Emission;
      fixed Alpha;
};

 

반응형
반응형

유니티 서피스 셰이더를 UnLit으로 사용할 때 캐릭터가 하얗게 타는 현상이 발생했다.

Scene안에 라이트가 두개 이상 있을경우 UnLit 서피스 셰이더임에도 캐릭터가 하얗게 타는 것이다..(프라그 셰이더는 발생하지 않음)

해당 문제를 해결하기 위해서는 noforwardadd를 꼭 써줘야 한다.

 

 

#pragma surface surf NoLighting noambient noforwardadd

 

그런데 이렇게 하고나서도 또 타는 현상이 발생했는데, 이 역시 라이트가 두개 있을때 두 라이트가 같은 Layer Culling을 사용하면서

그 사용된 Culling Object가 또 하얗게 타는 것이다... 레이어를 수정해서 해결하긴 했는데.. 뭔가 이상하고 찜찜한 기분..

 

 

아래는 라이트에 영향받지 않는 저 사양 서피스 셰이더 코드이다. 딱 림컬러까지만 처리~

 

 

    SubShader
    {
        LOD 200
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        Lighting off

            CGPROGRAM
            
            #pragma surface surf NoLighting noambient noforwardadd

            //================================================================
            // VARIABLES
            
            fixed4 _Color;
            sampler2D _MainTex;
  
            uniform fixed _IsRim;
            uniform fixed4 _RimColor;
            uniform fixed _RimPower;
   

            struct Input
            {
                half2 uv_MainTex;
                half3 viewDir;
            };
            

            half4 LightingNoLighting (SurfaceOutput s, half3 lightDir, half atten)
            {
               return half4(s.Albedo, s.Alpha);
            }
            
            
            //================================================================
            // SURFACE FUNCTION
            
            void surf (Input IN, inout SurfaceOutput o)
            {
                fixed4 mainTex = tex2D(_MainTex, IN.uv_MainTex);
                fixed rim = (1.0 - saturate(dot(normalize(IN.viewDir), o.Normal)));

                o.Albedo = mainTex.rgb; * Color.rgb;

                o.Alpha = mainTex.a * _Color.a;

                o.Emission = _RimColor.rgb * pow(rim, 1.5 - _RimPower) * _IsRim;

            }
            
            ENDCG

    }

반응형

+ Recent posts