이것저것

[UNITY] 라이팅 3장 - Phong, Blinn Phong 본문

[유니티] Unity3D/Unity Shader

[UNITY] 라이팅 3장 - Phong, Blinn Phong

Patch_JA 2019. 5. 3. 23:15
728x90

 

저번 포스팅 마지막에서 Rim Light를 이용한 홀로그램을 구현했었다.

이번장에서는 Rim을 기반으로 CustomLight나 Specular 등

여러 가지를 같이 적용시켜서 어떻게 보이는지 확인해보려고 한다.

실습 위주이기 때문에 코드 분석을 해보면서 하면 좋을듯 하다.

 

[저번 포스팅 링크]2019/04/26 - [쉐이더] - 라이팅 2장

 

 

 

시작하기 앞서

Specular 공식에  여러 공식들이 있는데 그중에서도

Phong과 Blinn Phong이 있다.

 

R : Reflection Vector(반사광)

V : View Vector(카메라)

N : Normal Vector(법선)

H : Half Vector(반각)

L : Light Vector

 

 

 

 

 

Phong

빛 방향과 카메라 방향의 정반사율에 대한 0~1 값을 구하는 것이다.

즉 R과 V를 dot 연산하는데 1에 가까우면 밝게 specular가 맺히고

0에 가까우면 보다 약한 specular가 맺히게 된다.

 

Blinn Phong

Phong에서는 R과 V를 dot 연산했지만 Blinn에서는 H와 N를 dot 연산한다.

여기서 H(Half Vector)는 V와 L의 중간인 합을 말한다.

이렇게 사용하는 것이 연산면에서 엄청 가볍다고 한다.

 

 

Rim

저번 포스팅에서 다루었던 Rim을 활용한 홀로그램과 비슷하게

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
Shader "Custom/0_Lim"
{
    Properties
    {
        _BumpTex("Normal Textrue", 2D) = "bump" {}
    }
        SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
 
        CGPROGRAM
        #pragma surface surf Lambert alpha:blend        
 
        sampler2D _BumpTex;
        
        struct Input
        {
            float2 uv_BumpTex;
            float3 viewDir;
        };
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
            o.Emission = float3(1, 0, 0);
            o.Alpha = (1 - abs(dot(o.Normal, IN.viewDir)));
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 

 

전에 작성했던 코드랑 굳이 다른 점이라면 노말 맵 외 텍스쳐가 없고,

alpha:fade가 아닌 alpha:blend를 사용하였는데 지금 셰이더에서는

둘이 정확히 무슨 차이인지 모르겠다. 유니티 도큐먼트를 참고하자면

 

alpha:fade는 전통적인 페이드 투명도라 하고,

alpha:blend는 알파 블렌딩을 해서 혼합해준다는 것 같다.

 

 

Lim + Cumstom Light

이번에는 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
43
44
45
46
47
48
49
50
51
Shader "Custom/1_Lim+CustomLight"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpTex("NormalMap", 2D) = "bump" {}
 
        _fRimPow("Rim Power", float) = 5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
        #pragma surface surf _CustomA noambient noshadow
        #pragma target 3.0
 
        sampler2D _MainTex, _BumpTex;
 
        struct Input
        {
            float2 uv_MainTex, uv_BumpTex;
            float3 viewDir;
        };
 
        float _fRimPow;
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
                        
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
            o.Emission = pow(1 - saturate(dot(o.Normal, IN.viewDir)), _fRimPow);
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomA(SurfaceOutput s, float3 lightDir, float atten)
        {
            float4 fDiff_Result;
            float fDiff_Dot = saturate(dot(s.Normal, lightDir));                        
            fDiff_Result.rgb = s.Albedo * _LightColor0.rgb * fDiff_Dot * atten;
            fDiff_Result.a = s.Alpha;
 
            return fDiff_Result;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 

 

_CustomA라는 Lighting 함수를 만들어서 Lambert공식을 사용하여

Diffuse 코드를 작성하였다.

 

 

Lim + Cumstom Light + Specular

이번에는 Specular를 적용시켜보려고 한다.

_CustomA 함수에서 특별시 추가되는 부분이 있는데 viewDir 변수가 추가된다.

또한 Phong공식과 Blinn Phong공식 두 가지를 비교해보겠다.

추가적으로 noambient와 noshadow가 켜져 있다면 이젠 꺼주자.

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
Shader "Custom/2_Lim+CustomLight+Specular"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpTex("NormalMap", 2D) = "bump" {}
 
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularValue("Specular Value", float) = 50
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
 
        CGPROGRAM
        #pragma surface surf _CustomA // noambient noshadow
        #pragma target 3.0
 
        sampler2D _MainTex, _BumpTex;
 
        struct Input
        {
            float2 uv_MainTex, uv_BumpTex; 
            float3 viewDir;
        };
 
        float4 _SpecularColor;
        float _SpecularValue;
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
                        
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomA(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            float4 fDiff_Result;
            float fDiff_Dot = saturate(dot(s.Normal, lightDir));
            fDiff_Result.rgb = s.Albedo * _LightColor0.rgb * fDiff_Dot * atten;
            fDiff_Result.a = s.Alpha;
 
           float3 fSpec_Blinn;
            float3 fSpec_Half = normalize(lightDir + viewDir);
            float fSpec_Dot = saturate(dot(s.Normal, fSpec_Half));
            fSpec_Blinn = pow(fSpec_Dot, _SpecularValue);
 
            float3 fSpec_Phong;
            float3 fReflecV = reflect(-lightDir, s.Normal);
            float fReflec_Dot = saturate(dot(fReflecV, viewDir));
            fSpec_Phong = pow(fReflec_Dot, _SpecularValue);            
 
            float4 fResult;
            fResult.rgb = fDiff_Result + fSpec_Blinn ; // fSpec_Phong
            fResult.a = s.Alpha;
 
            return fResult;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 

 

< (좌)BlinnPhong , (우)Phong >

그러나 자세히 보면 머리 부분에 반사가 되고 있다. 이를 해결하기 위해서는

SpecularMap 텍스쳐를 사용하여 나타나지 않게 할 수 있다.

 

 

Lim + Cumstom Light + SpecularMap

 

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
Shader "Custom/2_Lim+CustomLight+SpecularMap"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpTex("NormalMap", 2D) = "bump" {}
        
_SpecTex("SpecularMap"2D= "white"{}
        _SpecularValue("Specular Value", float) = 50
 
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
 
            CGPROGRAM
            #pragma surface surf _CustomA // noambient noshadow
            #pragma target 3.0
 
            sampler2D _MainTex, _BumpTex, _SpecTex;
 
        struct Input
        {
            float2 uv_MainTex, uv_BumpTex, uv_SpecTex;
            float3 viewDir;
        };
 
        float _SpecularValue;
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 fSpecMap = tex2D(_SpecTex, IN.uv_SpecTex);
                        
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
            o.Gloss = fSpecMap.a;
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomA(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            float4 fDiff_Result;
            float fDiff_Dot = saturate(dot(s.Normal, lightDir));
            fDiff_Result.rgb = s.Albedo * _LightColor0.rgb * fDiff_Dot * atten;
            fDiff_Result.a = s.Alpha;
            
            float3 fSpec_Blinn;
            float3 fSpec_Half = normalize(lightDir + viewDir);
            float fSpec_Dot = saturate(dot(s.Normal, fSpec_Half));
            fSpec_Blinn = pow(fSpec_Dot, _SpecularValue) * s.Gloss;
            
            float4 fResult;
            fResult.rgb = fDiff_Result + fSpec_Blinn;
            fResult.a = s.Alpha;
 
            return fResult;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 

 

surf함수에서 Gloss부분에 Specular map의 알파채널을 넣고

_CustomA함수에서 BlinnPhong최종 결과에 Gloss를 곱해주었다.

실행 결과를 확인해보면 더 이상 머리 부분에 빛이 반사되지 않게 되었다.

 

모두 합치기

위에서 사용했던 Diffuse, Rim, Specular 들을 모두 적용시켜보자.

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
Shader "Custom/3_Shader_All"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpTex("NormalMap", 2D) = "bump" {}
        _SpecTex("SpecularMap", 2D) = "white" {}
 
        _SpecularColor("FakeSpecular Color", Color) = (1,1,1,1)
        _SpecularPow("FakeSpecular Pow", float) = 50
 
        _RimColor("Rim Color", Color) = (1,1,1,1)
        _RimPow("Rim Pow", float) = 30
 
        _FakeSpecColor("FakeSpecular Color", Color) = (1,1,1,1)
        _FakeSpecPow("FakeSpecular Pow", float) = 30
 
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
 
            CGPROGRAM
            #pragma surface surf _CustomA
            #pragma target 3.0
 
            sampler2D _MainTex, _BumpTex, _SpecTex;
 
        struct Input
        {
            float2 uv_MainTex, uv_BumpTex, uv_SpecTex;
            float3 viewDir;
        };
 
        float4 _RimColor, _SpecularColor, _FakeSpecColor;
        float _SpecularPow, _RimPow, _FakeSpecPow;
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 fSpecMap = tex2D(_SpecTex, IN.uv_SpecTex);
                        
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
            o.Gloss = fSpecMap.a;
            o.Alpha = c.a;
        }
 
        float4 Lighting_CustomA(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            // Diffuse
            float4 fDiff_Result;
            float fDiff_Dot = saturate(dot(s.Normal, lightDir));
            fDiff_Result.rgb = s.Albedo * _LightColor0.rgb * fDiff_Dot * atten;
            fDiff_Result.a = s.Alpha;
            
            // Specular
            float3 fSpec_Blinn;
            float3 fSpec_Half = normalize(lightDir + viewDir);
            float fSpec_Dot = saturate(dot(s.Normal, fSpec_Half));
            fSpec_Dot = pow(fSpec_Dot, _SpecularPow);
            fSpec_Blinn = fSpec_Dot * _SpecularColor * s.Gloss;    
            
            //Fake Specular
            float3 fSpec_Fake;
            float fSpecF = saturate(fSpec_Half);
            fSpecF = pow(fSpecF, _FakeSpecPow);
            fSpec_Fake = fSpecF * _FakeSpecColor * s.Gloss;
 
            // Rim
            float3 fRim_Result;
            float fRim_Dot = abs(dot(viewDir, s.Normal));
            fRim_Dot = pow(1 - fRim_Dot, _RimPow);
            fRim_Result = fRim_Dot * _RimColor;
 
            // Result
            float4 fResult;
            fResult.rgb = saturate(fDiff_Result + fSpec_Blinn + fSpec_Fake + fRim_Result);
            fResult.a = s.Alpha;
 
            return fResult;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 

 

 

 

 

Comments