반응형

텍스쳐 임포트시 모바일 프로젝트에서는 기본적으로 Advanced로 사용하고 mipmap을 끄는게 통상적인 방식입니다.

하지만, 유니티 기본셋팅은 항상 Texture 타입으로 설정되어 mipmap도 무조건 켜지게 됩니다.

아래 코드를 편집하여 프로젝트에 넣어주시면 원하시는 셋팅값으로 텍스쳐 임포트를 설정할수 있습니다.


using UnityEngine;

using UnityEditor;


public class TexturePostProcessor : AssetPostprocessor

{

void OnPostprocessTexture(Texture2D texture)

{

TextureImporter importer = assetImporter as TextureImporter;

importer.textureType = TextureImporterType.Advanced;   // 기본 타입을 Advanced로..

importer.anisoLevel = 1;

importer.filterMode = FilterMode.Bilinear;

importer.mipmapEnabled = false;  // 기본 mipmap 해제

// 필요한 설정값을 이곳에 넣어주시면 됩니다.


Object asset = AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(Texture2D));

if (asset)

{

EditorUtility.SetDirty(asset);

}

else

{

texture.anisoLevel = 1;

texture.filterMode = FilterMode.Bilinear;          

}

}

반응형

반응형

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

얼마전에 공유드렸던 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 가 들어간 셰이더를 사용하면 안됩니다!

반응형
반응형

Prefab_Skin = (GameObject)EditorGUILayout.ObjectField("_Skin Object", Prefab_Skin, typeof(GameObject));

라고 코딩을 하자 아래와 같은 에러 로그가 발생했다.

 

warning CS0618: `UnityEditor.EditorGUILayout.ObjectField(string, UnityEngine.Object, System.Type, params UnityEngine.GUILayoutOption[])' is obsolete: `Check the docs for the usage of the new parameter 'allowSceneObjects'.'

 

에러 로그가 발생하지 않으려면 마지막에 Bool 값을 넣어서 아래와 같이 입력해줘야 에러가 발생하지 않는다.

 

_prefabObj = (GameObject)EditorGUILayout.ObjectField("Target Prefab", _prefabObj, typeof(GameObject), true);

 

 

반응형
반응형

UnityEngineInternal.APIUpdaterRuntimeServices를 사용하면 아래와 같은 Warning Log 가 발생한다.

 

warning CS0618: `UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(UnityEngine.GameObject, string, string)' is obsolete: `AddComponent(string) has been deprecated. Use GameObject.AddComponent<T>() / GameObject.AddComponent(Type) instead.

 

이 로그가 발생하지 않도록 하기 위해서는 툴을 거쳐서 스크립트를 집어넣도록 설정했던것을 해당 스크립트로 바로 불러오도록 값을 수정해 주면 에러로그가 발생하지 않는다.

 

예)

string ParticleStrScript = "ParticleCtrl";

 

UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent (PrefabBipProp, "Assets/Scripts/Editor/Art/CHAR_Prefab_FbxChanger.cs", ParticleStrScript); 

 

위 코드를 사용하면 warning이 발생하게 되며 아래와 같이 수정해주면 로그가 사라진다.

 

-->  PrefabBip.AddComponent (typeof(ParticleCtrl));

 

 

반응형
반응형

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

 

 

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

 

바닥 렌더 큐(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
 
    }

}

반응형
반응형

이번에 기존에 등록된 스크립트를 제거하고 새로운 스크립트를 등록하는 툴을 유니티 스크립트로 제작하게 됐는데 이번 툴을 제작하면서 C#에서의 이름 찾기와 재귀 함수 사용법에 대해서 알게 됐습니다.

 

아래 스크립트는 기존 있던  Animated_Death 컴포넌트를 삭제하고 Animated_Death_Combine 이라는 새로운 컴포넌트를 추가하면서 해당 컴포넌트에 하위 오브젝트들의 이름을 뒤져 필요한 Transform을 이름으로 검색하여 자동으로 넣어주는 툴입니다.

아래 스크립트를 참고하면 여러가지 응용이 가능할거 같습니다.

 

using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;

public class AnimateDeathChange : EditorWindow
{
    GameObject Target = null;
    GameObject pObj = null;

    string strScript = "Animated_Death_Combine";

    string DelScript = "Animated_Death";

    [MenuItem("RK Tools/Art/AnimateDeathChange")]

    //윈도우 생성
    static void InitWindow () 
    {
        AnimateDeathChange window = (AnimateDeathChange)EditorWindow.GetWindow(typeof(AnimateDeathChange));
        window.autoRepaintOnSceneChange = true;

        if (window == null)
        {
            Debug.Log("error: cannot find Dummy");
        }
    }

    //오브젝트 선택창
    void OnGUI()
    { 
        SelectTargetObject ();
        ScriptSetting ();
    }


    //원본소스 선택창
    void SelectTargetObject()
    {
        GUILayout.Space(16f);
        GUI.contentColor = Color.white;
        EditorGUILayout.LabelField("해당 모델에 죽기 스크립트를 추가해 줍니다.");
        EditorGUILayout.LabelField("각각의 항목을 채워주세요.");

        EditorGUILayout.BeginVertical("box");
        Target = (GameObject)EditorGUILayout.ObjectField("Base Model (Project)", Target, typeof(GameObject), false);
        strScript = EditorGUILayout.TextField ( "Add Script Name", strScript);
        DelScript = EditorGUILayout.TextField ( "Del Script Name", DelScript);
        EditorGUILayout.EndVertical();
    }

    //복사 오브젝트는 하이어라키 창에서 실시간 교체 가능
    void OnSelectionChange()
    {        
        if (Selection.gameObjects == null || Selection.gameObjects.Length < 1) {
            Focus();
            Target = null;
            return;
        }

        if (Target != Selection.gameObjects[0])
        {
            Target = Selection.gameObjects[0];
            Focus();           
        }
        else
            Target = Selection.gameObjects[0];
    }

    //실행시 기존 스크     
    void ScriptSetting()
    {
        GUILayout.Space(16f);
        if (GUILayout.Button ("Set Script To Object")) 
        {

            // 새로운 컴포넌트를 넣어주는 명령어 
            UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(Target, "Assets/Scripts/Editor/Art/AnimateDeathChange.cs", strScript);

            SerchObjectsInChild(Target.transform);

            // 기존 컴포넌트를 제거해주는 명령어
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());




            if (null == Target)
                return;
            if (null == pObj)
                return;
            else 
            {
                SerchObjectsInChild(Target.transform);
            }


        }
    }
    //
    //    //원본 오브젝트의 하위 더미를 찾는 재귀 함수
    private void SerchObjectsInChild(Transform tr)
    {
        int iCount = tr.childCount;

        for (int i = 0; i < iCount; ++i) {
            Transform tempTr = tr.GetChild (i);
//            Debug.Log (tempTr);

            // Contains 명령어는 문장을 뒤져 해당 텍스트를 찾는 명령어이다.

            if (tempTr.name.Contains ("_body")) {                
                Target.GetComponent<Animated_Death_Combine>().Model_1 = tempTr.gameObject.GetComponent<Renderer>();
            } 
            else if (tempTr.name.Contains ("_head")|| tempTr.name.Contains ("face")) {
                Target.GetComponent<Animated_Death_Combine>().Model_2 = tempTr.gameObject.GetComponent<Renderer>();
            } 
            else if (tempTr.name.Contains ("_weap")) {                
                Target.GetComponent<Animated_Death_Combine>().Model_3 = tempTr.gameObject.GetComponent<Renderer>();
            }
            else if (tempTr.name.Contains ("_BODY")) {
                Target.GetComponent<Animated_Death_Combine>().Shadow_1 = tempTr.gameObject.GetComponent<Renderer>();
            } else if (tempTr.name.Contains ("_HEAD") || tempTr.name.Contains ("_FACE")) {                
                Target.GetComponent<Animated_Death_Combine>().Shadow_2 = tempTr.gameObject.GetComponent<Renderer>();
            }else if (tempTr.name.Contains ("_WEAP")) {                
                Target.GetComponent<Animated_Death_Combine>().Shadow_3 = tempTr.gameObject.GetComponent<Renderer>();
            }

            if (tempTr.childCount > 0)
            {
                SerchObjectsInChild(tempTr);
            }
        }
    }
}

반응형
반응형
GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());

 

위 구문은 선택된 타겟 오브젝트의 자식을 뒤져 Animated_Death 라는 컴포넌트를 제거해 주는 명령어 입니다.

GameObject.DestroyImmediate (Target.GetComponent<Animated_Death>());  라고 쓰면 자식이 아닌 본인의 컴포넌트를 제거해 주는 거겠죠~

 

이번에 죽기 관련 스크립트를 수정하면서 알게 된 내용이라 메모해둡니다~

반응형
반응형

유니티 스크립트에서는 메터리얼에 담겨 있는 텍스쳐의 다양한 정보를 편집해서 사용할 수 있다.

MainTexture는 maintexture / mainTextureOffset / mainTextureScale  등을 활용하여 크기나 좌표를 편집할 수 있고,

SubTexture들은 String 값을 참조하여 편집할 수 있다.

아래의 코드를 참고하자.

 

m_Renderer.material.mainTextureOffset    = new Vector2(offset.x - ((int)offset.x), offset.y - ((int)offset.y));

m_Renderer.material.SetTextureOffset("_Alpha", offset)//_Alpha 에 담겨 있는 텍스쳐의 좌표를 편집할 수 있다.

m_Renderer.material.mainTextureScale    = m_size;

m_Renderer.material.SetTextureScale("_Alpha", m_size);

반응형

+ Recent posts