Contents

[클라우디아 도피오슬래시]유니티 물리 기반 셰이더 개발 05

   May 2, 2023     9 min read     - Comments

서피스 셰이더


서피스 셰이더는 서피스 라이팅 모델을 계산하는 특수한 유니티 셰이더의 종류 중 하나다.
최고 장점은 상당한 양의 보일러플레이트 코드를 감출 수 있는 것이다.


Pragmas


#pragma surface surf Standerd fullforwardshadows

surf는 서피스 함수 이름이고 Standard는 라이팅 모델이며 fullforwardshadows는 옵션이다.


신규 데이터 구조체


서피스 함수는 Input이라고 하는 데이터 구조체를 취한다.

struct Input{
    float2 uv_MainTex;
};

그리고 inout이라는 타입 퀼리파이어가 붙은 SufaceOutputStandard라는 데이터 구조체를 취한다.

struct SurfaceOutputStandard{
    fixed3 Albedo;  //기본 색상(디퓨즈 혹은 스펙큘러)
    fixed3 Normal;  // 탄젠트 공간 노멀
    half3 Emission; 
    half Metallic;  // 0=비금속성, 1=금속성
    half Smoothness;// 0=거침, 1=부드러움
    half Occlusion; // occlusion (default 1)
    fixed Alpha;    // 투명도 알파
};


서피스 셰이더 편집


두번째 알베도 맵 추가하기


Shader "Custom/SurfaceShaderSecondAlbedo"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _SecondAlbedo("Second Albedo (RGB)", 2D) = "white"{}
        _AlbedoLerp("Albedo Lerp", Range(0,1)) = 0.5
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // 물리 기반 표준 라이팅 모델, 모든 타입의 광원에 그림자 활성화
        #pragma surface surf Standard fullforwardshadows

        // 라이팅 효과가 더 멋져 보이도록 셰이더 모델 3.0 타깃 사용
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _SecondAlbedo;
        half _AlbedoLerp;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // 해당 셰이더에 인스턴스 서포트를 추가한다. 이 셰이더를 활요하는 재질에 `인스터싱 활성화`를 체크해야한다.
        // 인스터싱에 대한 자세한 정보는 https://docs.unity3d.com/Manual/GPUInstancing.html 
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // 각 인스터스별 프로퍼티는 여기에 넣는다.
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // 색상이 스며든 텍스처에는 알베도값을 가져온다.
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            fixed4 secondAlbedo = tex2D(_SecondAlbedo, IN.uv_MainTex);
            o.Albedo = lerp(c, secondAlbedo, _AlbedoLerp) * _Color;
            // 금속성과 부드러움정도는 슬라이더 변수에서 가져온다.
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

두번째 알베도


노멀 맵 추가하기


Shader "Custom/SurfaceShaderNormalMap"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _NormalMap("Normal Map", 2D) = "bump" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows

        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _NormalMap;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
            
        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

노멀 맵


그림자 작동 확인하기


맨 마지막 FallBack 값이 있다. 이는 메쉬의 그림자 렌더링에 사용하는 다른 셰이더의 이름이다.


다른 내장 라이팅 모델 사용


지금까지 표준 라이팅 모델을 사용했다. 대신 BlinnPhong을 사용해보자.

Shader "Custom/SurfaceShaderBlinnPhong"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _SpecColor ("SpecularMAterial Color", Color) = (1,1,1,1)
        _Shininess ("Shiniess", Range (0.03, 1)) = 0.078125
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf BlinnPhong fullforwardshadows

        #pragma target 3.0

        sampler2D _MainTex;
        float _Shininess;

        struct Input
        {
            float2 uv_MainTex;
        };

        fixed4 _Color;

        UNITY_INSTANCING_BUFFER_START(Props)

        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Specular = _Shininess;
            o.Gloss = c.a;
            o.Alpha = 1.0f;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

glossinessmetallic에 대한 개념이 존재하지 않기 때문에 Gloss와 Specular특성을 추가해 줘야한다.

BlinnPhong


커스텀 라이팅 모델


라이팅 모델 함수 시그니처


  • 디퓨즈용 시그니처
    half4 Lighting<Name> (SurfaceOutput s, unityGI gi);
    
  • 뷰 종속 관련 시그니처
    half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, unityGI gi);
    
  • SurfaceOutput 데이터 구조체
    struct SurfaceOutput{
      fixed3 Albedo;
      fixed3 normal;
      fixed3 Emission;    //빛을 스스로 발산하는 객체
      half Specular;
      fixed Gloss;
      fixed Alpha;
    };
    


커스텀 라이팅


Shader "Custom/LightingPhong"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _SpecColor ("SpecularMAterial Color", Color) = (1,1,1,1)
        _Shininess ("Shiniess", Range (0.03, 128)) = 0.078125
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Phong fullforwardshadows

        #pragma target 3.0

        sampler2D _MainTex;
        float _Shininess;
        fixed4 _Color;

        struct Input
        {
            float2 uv_MainTex;
        };

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        inline void LightingPhong_GI(SurfaceOutput s, UnityGIInput data, inout UnityGI gi) {
            gi = UnityGlobalIllumination(data, 1.0, s.Normal);
        }

        inline fixed4 LightingPhong (SurfaceOutput s, half3 viewDir, UnityGI gi) {
            UnityLight light = gi.light;

            float nl = max(0.0f, dot(s.Normal, light.dir));
            float3 diffuseTerm = nl * s.Albedo.rgb * light.color;

            float3 reflectionDirection = reflect(-light.dir, s.Normal);
            float3 specularDot = max(0.0, dot(viewDir, reflectionDirection));
            float3 specular = pow(specularDot, _Shininess);
            float3 specularTerm = specular * _SpecColor.rgb * light.color.rgb;

            float3 finalColor = diffuseTerm.rgb + specularTerm;

            fixed4 c;
            c.rgb = finalColor;
            c.a = s.Alpha;

            #ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
                c.rgb += s.Albedo * gi.indirect.diffuse;
            #endif

            return c;
        }

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = 1.0f;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

커스텀 라이팅