Custom Light 2강에서 Diffuse, Specular, Fresnel을 합쳐서 쉐이더 하나를 완성했다.

이번 포스팅에서도 Light효과들을 섞어서 쉐이더를 한번더 구성해볼것이다.

2강과 차이점은 자세한 원리 설명은 이전 포스팅에서 설명 했으니

구현, 순서 위주로 포스팅 하겠다.

 

 

 

1. Custom Light 쉐이더 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
 
        struct Input
        {
            float2 uv_MainTex;
        };
 
        fixed4 _Color;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            return float4(1111);
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

우선 첫 번째 과정은 Unity에서 새 Surface Shader 파일을 만들고 기본 코드에서 Custom Light 코드로 변경했다.

 

2. Diffuse 구현 - Lambert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
 
        struct Input
        {
            float2 uv_MainTex;
        };
 
        fixed4 _Color;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            //Diffuse
            float3 fDiffuseColor;
            float fNDotL = saturate(dot(lightDir, s.Normal));
            fDiffuseColor = s.Albedo * fNDotL * atten;
 
 
 
            //! Final Result
            float4 fFinalColor;
            fFinalColor.rgb = fDiffuseColor;
            fFinalColor.a = s.Alpha;
 
            return fFinalColor;
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

2강에서 소개했던 Lambert 공식을 이용하여 Diffuse를 구현했다.

//Diffuse
float3 fDiffuseColor;
float fNDotL = saturate(dot(lightDir, s.Normal));
fDiffuseColor = s.Albedo * fNDotL * atten;

 

3. Specular 구현, 노말맵추가 - Phong

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "bump" {}
 
        _SpecularColor("Specular Color", Color) = (1,1,1,1)
        _SpecularIntensity("Specular Intensity", Float) = 50
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
        sampler2D _BumpMap;
 
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };
 
        fixed4 _Color;
 
        float4 _SpecularColor;
        float _SpecularIntensity;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
 
            float3 fNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Normal = fNormal;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            //Diffuse
            float3 fDiffuseColor;
            float fNDotL = saturate(dot(lightDir, s.Normal));
            fDiffuseColor = s.Albedo * fNDotL * atten;
 
            //Specular
            float3 fSpecularColor;
            float3 fReflectVector = reflect(-lightDir, s.Normal);
            float fRDotV = saturate(dot(fReflectVector, viewDir));
            fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb;
 
 
            //! Final Result
            float4 fFinalColor;
            fFinalColor.rgb = fDiffuseColor + fSpecularColor;
            fFinalColor.a = s.Alpha;
 
            return fFinalColor;
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

2강에서는 Blinn-Phong으로 구현했지만 이번엔 Phong 공식으로 Specular를 구현했다.

//Specular
float3 fSpecularColor;
float3 fReflectVector = reflect(-lightDir, s.Normal);
float fRDotV = saturate(dot(fReflectVector, viewDir));
fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb;

3-1 Specular Map 추가

Specular 기능을 구현했지만 위 사진과 같이 반사가 일어나서는 안되는 부분에서 반사가 일어나고 있다.

 

이 문제를 해결하기 위해 Specular Map 텍스쳐를 이용하여 해당 텍스처에서 Specular가 나타나는 부분(1)

나타나지 않는 부분(0)으로 표현하여 이 데이터를 이용해 보겠다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "bump" {}
 
        _SpecularMap("Specular Map", 2D) = "white" {}
        _SpecularColor("Specular Color", Color) = (1,1,1,1)
        _SpecularIntensity("Specular Intensity", Float) = 50
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
        sampler2D _BumpMap;
        sampler2D _SpecularMap;
 
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 uv_SpecularMap;
        };
 
        fixed4 _Color;
 
        float4 _SpecularColor;
        float _SpecularIntensity;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
 
            float3 fNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Normal = fNormal;
 
            float4 fSpecular = tex2D(_SpecularMap, IN.uv_SpecularMap);
            o.Gloss = fSpecular.a;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            //Diffuse
            float3 fDiffuseColor;
            float fNDotL = saturate(dot(lightDir, s.Normal));
            fDiffuseColor = s.Albedo * fNDotL * atten;
 
            //Specular
            float3 fSpecularColor;
            float3 fReflectVector = reflect(-lightDir, s.Normal);
            float fRDotV = saturate(dot(fReflectVector, viewDir));
            fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb * s.Gloss;
 
 
            //! Final Result
            float4 fFinalColor;
            fFinalColor.rgb = fDiffuseColor + fSpecularColor;
            fFinalColor.a = s.Alpha;
 
            return fFinalColor;
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

텍스쳐를 이용하여 Specular효과를 제어하게 되었다.

 

 void surf (Input IN, inout SurfaceOutput o)
 {

    float4 fSpecular = tex2D(_SpecularMap, IN.uv_SpecularMap);
     o.Gloss = fSpecular.a;

surf함수에서 해당 Specular맵의 알파채널을 Gloss에 넣고

//Specular
float3 fSpecularColor;
float3 fReflectVector = reflect(-lightDir, s.Normal);
float fRDotV = saturate(dot(fReflectVector, viewDir));
fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb * s.Gloss;

Custom Light 함수에서 최종 계산 변수에서 Gloss값을 곱하였다.

 

4. Fresnel 효과 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "bump" {}
 
        _SpecularMap("Specular Map", 2D) = "white" {}
        _SpecularColor("Specular Color", Color) = (1,1,1,1)
        _SpecularIntensity("Specular Intensity", Float) = 50
 
        _FresnelColor("Fresnel Color", Color) = (1,1,1,1)
        _FresnelIntensity("Fresnel Intensity", Float) = 5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
        sampler2D _BumpMap;
        sampler2D _SpecularMap;
 
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 uv_SpecularMap;
        };
 
        fixed4 _Color;
 
        float4 _SpecularColor;
        float _SpecularIntensity;
 
        float4 _FresnelColor;
        float _FresnelIntensity;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
 
            float3 fNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Normal = fNormal;
 
            float4 fSpecular = tex2D(_SpecularMap, IN.uv_SpecularMap);
            o.Gloss = fSpecular.a;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            //Diffuse
            float3 fDiffuseColor;
            float fNDotL = saturate(dot(lightDir, s.Normal));
            fDiffuseColor = s.Albedo * fNDotL * atten;
 
            //Specular
            float3 fSpecularColor;
            float3 fReflectVector = reflect(-lightDir, s.Normal);
            float fRDotV = saturate(dot(fReflectVector, viewDir));
            fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb * s.Gloss;
 
            //Fresnel
            float3 fFresnelColor;
            float fNDotV = dot(s.Normal, viewDir);
            float fInverseRim = 1 - abs(fNDotV);
            fFresnelColor = pow(fInverseRim, _FresnelIntensity) * _FresnelColor.rgb * s.Albedo * 3.0f;
 
 
            //! Final Result
            float4 fFinalColor;
            fFinalColor.rgb = fDiffuseColor + fSpecularColor + fFresnelColor;
            fFinalColor.a = s.Alpha;
 
            return fFinalColor;
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs
//Fresnel 
float3 fFresnelColor; 
float fNDotV = dot(s.Normal, viewDir); 
float fInverseRim = 1 - abs(fNDotV); 
fFresnelColor = pow(fInverseRim, _FresnelIntensity) * _FresnelColor.rgb * s.Albedo * 3.0f

2강에서 구현한 Fresnel하고 조금 차이가 있다.

 

fFresnelColor = pow(fInverseRim, _FresnelIntensity) * _FresnelColor.rgb * s.Albedo * 3.0f

코드 뒷 부분에 Albedo색을 곱해주는 공식이 추가 됬다.

쉐이더에는 정답이 없고 오직 목표는 이쁘게 만드는 것이기에 기존에 붕떠 보이는 Fresnel효과를

Albedo색을 더해서 자연스럽게 만들었다.

 

5. Second Specular 구현 - Fresnel공식 응용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Shader "Custom/CustomLightShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "bump" {}
 
        _SpecularMap("Specular Map", 2D) = "white" {}
        _SpecularColor("Specular Color", Color) = (1,1,1,1)
        _SpecularIntensity("Specular Intensity", Float) = 50
 
        _FresnelColor("Fresnel Color", Color) = (1,1,1,1)
        _FresnelIntensity("Fresnel Intensity", Float) = 5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
       
        #pragma surface surf _CustomLight noambient
 
        sampler2D _MainTex;
        sampler2D _BumpMap;
        sampler2D _SpecularMap;
 
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float2 uv_SpecularMap;
        };
 
        fixed4 _Color;
 
        float4 _SpecularColor;
        float _SpecularIntensity;
 
        float4 _FresnelColor;
        float _FresnelIntensity;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
 
            float3 fNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Normal = fNormal;
 
            float4 fSpecular = tex2D(_SpecularMap, IN.uv_SpecularMap);
            o.Gloss = fSpecular.a;
        }
 
        float4 Lighting_CustomLight(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            //Diffuse
            float3 fDiffuseColor;
            float fNDotL = saturate(dot(lightDir, s.Normal));
            fDiffuseColor = s.Albedo * fNDotL * atten;
 
            //Specular
            float3 fSpecularColor;
            float3 fReflectVector = reflect(-lightDir, s.Normal);
            float fRDotV = saturate(dot(fReflectVector, viewDir));
            fSpecularColor = pow(fRDotV, _SpecularIntensity) * _SpecularColor.rgb * s.Gloss;
 
            //Fresnel
            float3 fFresnelColor;
            float fNDotV = dot(s.Normal, viewDir);
            float fInverseRim = 1 - abs(fNDotV);
            fFresnelColor = pow(fInverseRim, _FresnelIntensity) * _FresnelColor.rgb * s.Albedo * 3.0f;
 
            //Second Specular
            float3 fSecondSpecularColor;
            float fFresnelSpecPow = pow(saturate(fNDotV), 100.0f);
            fSecondSpecularColor = fFresnelSpecPow * _SpecularColor.rgb * s.Gloss * fNDotL;
 
            //! Final Result
            float4 fFinalColor;
            fFinalColor.rgb =  fDiffuseColor + fSpecularColor + fFresnelColor + fSecondSpecularColor;
            fFinalColor.a = s.Alpha;
 
            return fFinalColor;
        }
 
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

제목은 Second Specular라고 지었지만 사실은 Fresnel 하기 위해 1-하기전 NDotV값을 이용하여

카메라뷰 방향 부분을 Specular와 비슷하게 반사현상을 나타내었다.

앞서 말했지만 쉐이더에는 정답이 없고 오직 목표는 이쁘게 만드는 것이기에 Specular를 더 잘 보이게 만들었다.

//Second Specular
float3 fSecondSpecularColor;
float fFresnelSpecPow = pow(saturate(fNDotV), 100.0f);
fSecondSpecularColor = fFresnelSpecPow * _SpecularColor.rgb * s.Gloss * fNDotL;

 

 

완성


WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,