본문으로 바로가기

Fake FixedLight Environment

category Technical Report/Unity Shader 2018. 8. 14. 19:11
반응형




모처럼 Shader 코드 통으로 올리는거기도 하고 굳이 여기까지 와서 찾아보시는 분들 도움좀 되시라고 요번 코드에는 주절주절 잡담을 좀 덧붙여 볼까 한다. 이 Shader code는 Surface shader code로 작성되어있으며, 배경에서 Lightmap을 사용한다는 전제하에 작성된 코드이다.


fake light를 사용하므로 굳이 directional lightmapping을 사용할 필요가 없다.




Left : Standard Shader, Right : Fake FixedLight




 Properties {
      
       [Header(Fake FixedLight for Environment)]
       [Space(10)]
        _TintColor("TintColor", color) = (1,1,1,1)       
        _Bright   ("Brightness", Range(0,2)) = 0.4
      
       [Space(10)]
        _MainTex  ("Albedo", 2D) = "white" {}

       [NoScaleOffset][Normal]
       _BumpTex  ("NormalMap", 2D) = "white" {}
       _Normalinten("Normal Intensity", Float) = 1
                       
       [Space(20)]
       [NoScaleOffset]
       _MetallicGlossMap("Metallic(R), Occlusion(G), Smoothness(A)", 2D) = "white" {}
      
       [Space(20)]
       [Header(Reflection probe Roughness(G))]             
       _RoughInten  ("Roughness Intensity", Range(0, 2)) = 0.5         
       _MainBias("Smoothness values", Range(0, 10)) = 0

//일단 Shader code 사용할때 CustomUI를 사용하지 않는 이유는 개발도중에 변경될 여지가 많고 분기는 모두 shader_feature로 처리하기 때문에(Runtime에서 바꾸려면 제일 싼 비용은 material 여러개 들고 있는게 싸기도 하고.. shader variants 늘어나는거 비해서 UBER 타입의 관리이슈가 줄어드는게 더 효율적이란 생각때문. shader variants까지 극단적으로 쥐어짤 상황이면 그때 고쳐도 뭐 안늦고... 게다가 toggle로 처리하는건 결국 코드에서 계산한걸 0,1 multiply로 그리냐 안그리냐이기 때문에 어차피 코드에선 계산하게 된다.


       [Space(20)]
       [Header(View Dir Light(A))]      
       _LightInten("Light Intensity", Range(0,5)) = 0.5
       _LightGloss("Light Gloss", Range(1, 255)) = 1   
      

       [Space(20)]
       [Header(Occlusion Ambient(G))]      
       [MaterialToggle(_Ocu_ON)] _Ocu("Occlusion Toggle", float) = 0      


       [Header(Rim Light by GroundColor(R))]
       [MaterialToggle(_Rim_ON)] _Rim("Rim light Toggle", float) = 0      
       _RimInten("Rimlight Intensity", Range(0,10)) = 3
       _RimPower("Rimlight Power", Range(0.1, 5.0)) = 0.1


        }


// Shader Properties에서 분기는 오클루젼 맵과 Rim light 적용에 대한 부분 두가지만 분리시켰다. AO맵을 사용하는 경우가 프로젝트별로 아직은 상이한게 이유이고 Rim 의 경우 Metallic object의 경우 필요에 따라 껐다 켰다 하는게 나을것으로 보기 때문. 오히려 Rim lighting이 없어야 할 경우라면 굳이 코드에서 계산할 이유가 없기도 하고.


   
    SubShader {
   
      Tags {"RenderType" = "Opaque"}
       
        LOD 200
                       
        CGPROGRAM
      
        //not supported deffered render cause optimizing shader variants
        #pragma surface surf Lambert interpolateview exclude_path:deferred nolppv noambient //halfasview
noforwardadd


// 일단 Shader variants를 줄이기 위해 deferred render를 막았다. halfasview의 경우 라이팅 벡터를 단순화시켜 계산을 가볍게 한다고 하는데 NdotL 계산은 다 관여를 하는듯... Rim lighting도 fake lighting에도 영향을 준다. 역시 결과보고 알아서 선택. noforwardadd는 추가로 라이팅을 사용할 경우를 생각해 더하지 않았다. 추가 라이팅을 사용하지 않을경우는 compiler directives에 추가하는게 맞다.


        #pragma target 3.0   

        #pragma shader_feature _Ocu_ON
        #pragma shader_feature _Rim_ON

       
        // Unlit Lighting model

        /**
        inline half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten) {
       
       
        return fixed4(s.Albedo, s.Alpha);
        }
        **/

//여기에서 굳이 Lambert 모델을 쓰고 아래에 Unlit을 쓴 이유는 ShadowMask를 사용하려면 Lighting model을 define된걸 써야 하니까..(따로 cginc 계산에 define 된걸 가져와도 되지만 이건 아직 못찾아보고 있다....). Unlit을 사용한다면 당연히 subtractive 모드 고정이 되지만... 그리고 굳이 여기에서 fake light를 쓰니까 blinnphong 보다 가벼운걸 쓰는게 나을거란 판단이고...

               
        fixed4  _TintColor;
        half    _Bright;  

        sampler2D   _MainTex;
        sampler2D   _MetallicGlossMap;       
               
        half    _LightInten;
        half    _LightGloss;      

        sampler2D   _BumpTex;
        float   _Normalinten;
        half _MainBias;



        //Rim light Toggle
       
        #if _Rim_ON

        half    _RimInten;
        half    _RimPower;

        #endif

        half    _RoughInten;       
       
                   
        struct Input {
       
            half2  uv_MainTex;                   
            float3 viewDir;
            float3 worldRefl;
                                           
        INTERNAL_DATA
           
        };

// struct Input에서 uv는 Albedo껄 Normal, Metallic 모두 같은걸 쓴다. 이럴경우 Albedo의 Tiling과 offset을 변경하면 다 같이 바뀌기 때문. 그래서 [NoScaleOffset] 로 나머지 텍스쳐에서 메뉴를 숨겼다.


        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader
        UNITY_INSTANCING_BUFFER_START(Props)
    

        // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

       
        void surf (Input IN, inout SurfaceOutput o) {
           
           
            half4 c  = tex2D(_MainTex, IN.uv_MainTex).rgba;           
            half4 mg  = tex2D(_MetallicGlossMap, IN.uv_MainTex).rgba;                       
            half3 nm = UnpackNormal(tex2D(_BumpTex, IN.uv_MainTex)) * float3(1, _Normalinten, 1);

            o.Normal = normalize(nm) ;                          
            half face = saturate(dot(normalize(IN.viewDir), o.Normal));                       
                       
            half4 rfprobe = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, WorldReflectionVector(IN, o.Normal),  mg.a + _MainBias) * unity_SpecCube0_HDR.r;

// Reflection Probe 사용에서 텍스쳐에 _MainBias 값에 따라 적용되는 Mipmap값이 증가한다.(10일경우 제일 낮은 밉맵 레벨이 적용된다)

           
            o.Albedo =  c.rgb * _Bright * _TintColor;  

            #if _Ocu_ON
                   
            o.Albedo *=  mg.g;

            #endif       

           
            o.Emission = _LightColor0 * (pow(face, _LightGloss * mg.a ) * _LightInten) ;


//fake light의 color를 directional light의 컬러값을 그대로 사용

                                  
            o.Emission *= mg.a * rfprobe * _RoughInten;

            #if _Rim_ON

            half3 rim = 1.0 - (pow(face, _RimPower));

            //offside Light color value
            //o.Emission += mg.r * _RimInten * rim * (1.0 - _LightColor0);
           
            //Light color value, if you want to Light color into rim light use below code
            o.Emission += mg.r * _RimInten * rim *  (_LightColor0 + 0.1);
           
            #endif

           
//Rim light color는 Directional light의 color값에 0.1 만큼 증감된 값을 사용하거나 주석처리된 보색을 사용할수 있다. 취향껏 선택

                                          
        }
        ENDCG
        }
       
    Fallback "VertexLit"
    //if using Unlit change to Fallback off
}

반응형