본문 바로가기
Unity Boot Camp

Udemy STARTERS (유데미 스타터스) Unity 취업 부트캠프 9주차 - Light, Shader

by 개발하는 디토 2022. 8. 21.

Shader. 물체의 질감이나 표면, 렌더링 되는 시각적인 형태를 담당하는 무언가... 유니티에서 3D 입체를 표현하기 위해 무수히 많은 삼각형을 각각의 방향(normal vector)으로 바라보게 한다는 것을 배웠다.

 

빛 Light

  • 정반사 spectrum : 입사각과 반ㅃ사각이 같은 상태. 빛의 색.
  • 난반사 diffuse : 입사각과 반사각이 같지 않은 상태. 물체의 색.
  • 환경광 directional light : constant한 값. 상수.

빛의 100% = 정반사 + 난반사 (+ 환경광)로 구성되는데 환경광은 상수라서 일단 무시하고 정반사, 난반사만 생각해도 된다. 정반사와 난반사는 반비례 관계로 정반사가 증가하면 난반사가 감소한다. 정반사가 100이고 난반사가 0이라고 가정하면 물질은 거울이 된다. 쏜 빛이 그대로 보이는 것은 거울상이기 때문. 반대로 정반사 0, 난반사 100이면 ambient, 본래 물체의 색만 보여준다고 이해할 수 있다.

빛이 몰리는 부분은 하이라이트, 하얗게 나타남.

 

Light 속성

 

Type

Light Type

  • Directional Light
    각도에 따라서는 차이가 날 수 있는데 위치에 따라서는 빛 차이 없음.
  • Spot Light
    원뿔형. Range에 따라 빛을 비출 범위 결정. Directional Light과 다르게 위치의 영향을 받음. 광원에 가까울수록 더 강한 빛 받을 것. Intensity가 1일 때 1 range 커질수록 광원이 멀어지면서 밝기 약해짐.
    참고) Audio도 빛과 마찬가지. Audio Resource와 Audio Source 사이 거리가 멀어질수록 소리가 작게 들린다. mp3 Force to Mono (왼쪽 오른쪽 합쳐서 양쪽에 같은 소리 들리게끔) Audio Source에서 3D sound setting에서 거리에 따라 소리 값 어떻게 줄일지를 조정할 수 있음. UI의 버튼 클릭음 같은 것은 거리에 상관 없이 사운드 크기 들리도록 해줘야 함.
  • Point Light
    보조 광원. 특정 광원을 그렸을 때 그 Range 범위 안에 있는 공간에 전부 빛을 비춤. 참고로 Directional light은 range 개념이 없음.

 

Color

빛의 색을 지정할 수 있음. 

Mode

실시간으로 계산하지 않고 고정된 물체라면 light map을 구워서 빛에 대한 그림자를 저장해놓고 그 구워놓은 것을 쓸 것인지, 실시간으로 빛 연산할 것인지, 섞어 쓸 것인지 정의할 수 있음.

Intensity

빛의 세기. 숫자 클수록 밝아짐

 

Indirect Multiplier

간접광. 주변 물체에 맞고 반사된 빛을 어떻게 적용할 것인지 정의한다.

Shadow Type

Soft가 무겁고 Hard가 가벼움. Plane 하나 깔고 큐브와 sphere 를 두었을 때 그림자가 생기는 것을 볼 수 있음. 그림자의 가장자리가 부드럽게 처리되면 soft, 선명하게 처리되면 hard. no shadow 아예 그림자 없게 표현.

물체의 MeshRenderer 가보면 Lighting 탭에서 그림자 설정 가능. 물체에 의해 그림자 지게 한 상태에서 그림자가 드리워진 물체의 Receive shadow를 끄면 다른 물체로부터의 그림자가 생기지 않음. Strength: Realtime, mixed 에서 그림자 강도 조절 가능. 그림자의 해상도 설정 가능.

Draw Halo

광에 해당하는 시각적 표현. Directional Light 주변으로 시각적으로 빛이 퍼지는 효과 생김. 0이면 Directional Light 어디 있는지 알 수 없음.

DrawHalo 0이면 빛의 형태 사라짐.

 

Flare

Asset 폴더에서 Create-Lens Flare 생성. 빛을 카메라로 보았을 때 빛이 튀어나오거나 동그란 빛의 원이 생기는 느낌. 1 large 4 small 이라는 텍스처 포맷이 있는데 선정의된 위치에 텍스처 따로따로 넣어주는 방식.Render Mode: Important로 설정하면 무조건 표현해주는 것이고, Not Important로 설정하면 빛이 특정 개수 넘어갔을 때 후순위로 밀려서 표현해주지 않음. Auto는 알아서 생성 순서 등으로 중요도 알아서 정리해서 표현해줌. Culling Mask: 빛을 받을 대상 설정함.

 

  • Area (Light map 구워야만 적용됨)
    Window - Rendering - Lighting Auto Generate 옵션 끄고 Generate Lighting 누를 수 있음. 빛 비출 대상인 오브젝트의 Static 옵션에서 Contribute GI로 설정해주어야 함.

Shape

Disc: 원형, Rectangle: 사각형

 

Environment 환경광 (skybox에 따라)

Skybox Material: 구글링해서 스카이박스 texture png 받아온 다음 이미지를 가지고 Cube map을 만들어야 함. Material 생성한 뒤 shader를 Skybox, 6 sided로 설정한 뒤 각 side에 png를 잘라서 넣으면 됨.

Sun Source: light를 밖으로 빼놓을 경우 Sun 정의 Environment Lighting: 물체의 색은 환경광의 영향을 받은 값임. 그래서 directional light의 빛의 색이 바뀌면 물체의 색도 달라짐.
Environment Reflections: 스카이박스가 기본. 반사광 정의. intensity, bounces 상대방이 비춰지는 횟수 설정. 마주보고 있는 거울 2개는 무한히 서로를 반사하는데 그 반사 횟수를 정해주는 것.

 

 

Legacy Shader

Legacy Shader: 하나하나의 물리 현상을 따로따로 구현해서 종합적인 shader를 하나 만들었음. PBR: Unity 5에서 기본으로 사용하는 Material. 물리 기반 shader. 2D shader, Unlit(빛의 영향을 받지 않는 쉐이더, 주로 UI 사용) 같은 경우 물리 법칙을 거스름. 따라서 Legacy shader로 직접 구현할 때가 있음. 하지만 물리 법칙 영향 받는 일반 쉐이더는 PBR로 만듦.

  • Albedo 원래 물체의 색깔. 고전 쉐이더는 Albedo 값이 없고 Diffuse를 사용해 난반사 자체에 들어가는 색을 지정했었음. 하지만 standard shader가 기본이 되면서 물체 자체의 색을 설정할 수 있게 됨. 물체의 색을 미리 지정해놓고 여러 물리적인 작용이 일어날 때마다 계산해서 보여주는 색을 달리하게 됨.
    • Albedo 옆에 texture 넣어줄 수 있음.
      UV map 텍스쳐의 어떤 지점을 물체의 어떤 지점에 넣어줄지 매핑한 것. 큐브는 모서리를 0,0으로 잡고 텍스처의 왼쪽 상단과 매핑해서 넣어줌. Shader 연산에서 Polygon과 Vertex 연결 연산도 들어감. 어쨌든 modeling에 들어가는 텍스처는 Albedo 옆에 넣어주면 된다~
  • Metalic 웬만하면 0 (비금속) 아니면 1 (금속)로 설정. 여기서 Smoothness에 따라 반사 많이 할지 안 할지 결정. Smoothness가 1이면 반사 많이 함.
    • 비금속인 상태에서 smoothness만 1로 올리면 그렇게 반사가 잘되진 않음.
      도자기 metalic 0, smoothness 1 / 거울 metalic 1, smoothness 1 (엄청 반사됨)
    • Unity Metalic Chart 참고할 것.
    • Metallic 옆에 이미지가 들어가면 반사되는 부분에 그 이미지 느낌이 함께 적용됨. 티가 많이 나지는 않음. Metallic alpha는 메탈릭에서 알파값을 갖고 오고, albedo alpha는 albedo에서 알파값을 가져옴
  • Rendering Mode
    • Opaque 투명 없을 때
    • Cutout : 알파값에 대한 threshold를 정해줌. 0~255인 알파값을 255로 나눠 0부터 1로 치환함. Alpha cutoff 1로 하면 거의 모든 픽셀에 알파값이 적용됨. 0.5로 하면 알파값이 128부터 255인 것까지만 alpha 1 적용됨.
    • Fade Metallic: Albedo 모든 옵션에 투명값을 적용함. Metallic 값을 1로 수정해도 여전히 투명으로 보임.
    • Transparent: Albedo에만 투명값을 줌. 주변광, 환경광에는 적용되지 않음.
  • Normal map
    노말 맵 텍스쳐는 Texture Type을 Normal Map으로 고쳐야 함.
    빛을 쐈을 때 반사가 되는 방향을 표시하기 위해 가지고 있는 normal 값. 점이 3개 이상 모이면 면이 만들어지는데 이 면이 바라보고 있는 방향, 즉 면의 수직방향이 normal 값임. 물체의 표면을 구분하기 위해서는 물체의 표면이 향하는 방향을 알아야 하고, 그 방향을 3차원 축으로 정의한 것이 Normal Vector.
    반사가 된 위치에 가지고 있는 vertex 값(RGB 값이 적용됨) 난반사가 일어났을 때 normal map을 넣어주면 albedo의 색깔 뿐 아니라 vertex가 가지고 있는 RGB에 normal map의 normal (RGB 채널의 혼합)값이 곱해짐.
  • Height Map 깊이맵 3D 매핑에서 사용.
  • Occlusion, Detail Mask 뭔가 가리거나 할 때 사용한다고
  • Emission 자체 발광 원래 물리 기반 PBR shader 사용할 때는 Emission을 사용하지 않음. 본인이 빛을 내는 것은 광원만 가능하므로.
  • Tiling 텍스쳐 몇개 채울 것인지.
  • Offset x, y 얼마만큼 이동해서 타일링 할 것인지
  • Tiling 하려면 WrapMode Repeat으로 설정할 것
  • Secondary map 이중으로 쓰고 싶을 때

Forward: 오브젝트 그리고 빛 연산, 다음 오브젝트 그리고 빛 연산…

Deferred : 모든 오브젝트를 그리고 나서 나중에 빛 연산을 함. 연산을 한번만 하므로 유리. 하지만 shader를 직접 만드는 등의 수고가 필요함.

  • RenderQueue : Shader 간의 render 순서 정해주고 싶을 때 Render Queue 사용. UI 카메라 여러 개 있을 때 렌더 큐를 토해 더 앞에 보여주거나 할 수 있다고 함.

— standard shader를 쓰는 것이 마음 편함. standard 내부에서도 최적화 많이 해놨기 때문에 사용해도 무방. 저사양, 고사양 컴 모두 대응할 수 있음.

— URP는 standard shader 적용 안 됨. URP 용 shader가 따로 있음. Lit 등등.

 

사양과 관련하여

Edit - Project Settings - Graphics 가보면 Tier에 따라 그래픽 사양 적용됨. Tier1, 2, 3 안에 Standard Shader Quality를 low, medium, high 중 선택해서 적용할 수 있음. Shadow cascade 가까운 정도에 따라 그림자 디테일을 표현하는 정도 설정 가능.

// start 문 안에 플랫폼에 따른 그래픽 사양 지정 가능 등등등...
//#if UNITY_STANDALONE // PC 
#if UNITY_ANDROID // Android
	Graphics.activeTier = GraphicsTier.Tier1; //저사양
	Graphics.activeTier = GraphicsTier.Tier2;
	Graphics.activeTier = GraphicsTier.Tier3; //고사양
#endif

 

ShaderLab

유니티가 정의해놓은 Wrapper. 쉐이더 언어. 일반화를 하다보니 사용성에는 제약이 있음. 세부적인 조절, 커스터마이징은 조금 어렵다. Shader Lab 내부를 보면 cg와 GLSL이라는 언어를 사용한다.

* Unity에서 사용할 수 있는 shader 언어는 4가지. ShaderLab, cg (c for graphic), Glsl (OpenGL shader language), Hlsl (High level shader language). GLSL, CG는 범용성도 좋음. URP에서는 HLSL을 사용함. Classic Render Pipeline에서는 cg, GLSL 사용.

* Unity.com에서 Download Archive 가보면 자신의 유니티 버전을 찾아 Built in shader를 받으면 쉐이더를 수정할 수 있음.

Unity shader

  • vert / frag shader: vertex, fragment → Classical Render 쉐이터에서 사용
  • surf shader: PBR 물리 기반 쉐이더에서 사용.
  • ShaderLab 언어 사용해서 shader 파일 만들 수 있음. Shader “이름” → material 창에 shader 고를 때 이름 뜸.
Shader "Udemy/UnlitShader" // 이름 정의. 이 이름 그대로 Material 종류 만들어짐.
{
    // 카메라에서 큐브 정의할 때 Mesh(Vertex, v.Normal, v.Position)
    // 컴퓨터에서는 꼭짓점(위치)만 저장함. 위치만 확정하고, Shader를 처리해야 게임 scene에 나타남. 
    Properties
    {
        // Shader 안에서 쓸 변수를 정의하려면 Properties 안에 정의
        _MainTex ("Texture", 2D) = "white" {} // Float, Range, Int, Color, Vector, 2D
        //_SubTex ("Texture", 2D) = "white" {}  Float, Range, Int, Color, Vector, 2D
        _Colour ("Colour", Color) = (1, 1, 1, 1)
        //_NCount("nCount", Int) = 0 // float 값을 창 안에서 입력은 가능한데 실제로는 int 값으로 적용됨
        //_NCount2("nCount2", Float) = 0
        //_NCount3("nCount3", Range(0,10)) = 0 // 최소 0 ~ 최대 10까지
        //_NVector("UVXY", Vector) = (1,1,1,1)

        _DissolveTexture ("Dissolve Texture", 2D) = "white" {}
        _DissolveCutoff ("Dissolve Cutoff", Range(0, 1)) = 1
    }
    SubShader
    {
        //Tags { "RenderType"="Opaque" }
        Tags { "RenderType"="Transparent" }
        
        // Pass 여러개 만들 수 있음. 하지만 Pass 하나 늘어날 때마다 무거워짐. 이 Shader를 쓰는 material을 가진 모든 객체에 적용되므로.
        // 값을 넘겨줌
        Pass
        {
            // RenderType을 위한 옵션. Blend <source> <desination>
            // https://docs.unity3d.com/Manual/SL-Blend.html

            //Blend SrcAlpha OneMinusSrcAlpha // mask로 덮인 부분을 투명하게 만드는 옵션
            //Blend One OneMinusSrcAlpha; // Premultiplied transparency
            // Texture 구현을 위해 1-alpha값 섞임
            // finalValue = sourceFactor * sourceValue        operation       destinationFactor * destinationValue
            //                     One   *  Tex1.a 알파값       +                    1-alpha  *   Tex2.a

            // alpha 값 표현
            // 2개의 texture 장이 있다고 했을 때
            // 첫번째 texture의 알파값
            // 두번째 texture의 알파값을 1-Tex2.alpha

            //CGPROGRAM ~ ENDCG 안에서는 CG 언어를 사용할 수 있음
            // vertex, fragement 함수 존재하는데 그것을 각각 vert, frag라는 함수 이름에 매핑
            CGPROGRAM 
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc" // 선정이 된 변수, 함수 쓰기 위해 include로 선언. using과 같음.

            struct appdata
            //struct inputStruct //이름은 바꾸어도 상관 없음
            {
                //변수명:키워드; 형태. 사용할 수 있는 키워드 정해져 있음.
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f //이름 vertice to fragement의 약자인 듯?
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            sampler2D _MainTex;
            // color, vector는 4차원 float 배열로 받아야 함
            // fixed4, half4로도 받을 수 있음. 모바일 플랫폼이면 fixed4로 많이 받는다 함.
            float4 _MainTex_ST;
            float4 _Colour;
            //float4 _NVector;
            sampler2D _DissolveTexture;
            float _DissolveCutoff;


            v2f vert (appdata v)
            {
                // vertice shader: uv 조정. 텍스처의 위치 등 조정
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex); // vertex만 게임 scene에 올림.
            	//o.uv = v.uv;
                o.uv = v.uv + _Time; // 텍스처가 흐름

                return o; // v2f 리턴
            }

            fixed4 frag (v2f i) : SV_Target //v2f를 input으로 받음. vert 실행한 결과를 input으로 받는 것.
            {
                // fragment shader: color 조정하는 부분
                // Rasterisation: vertex와 vertex (점과 점) 사이를 vert에서 정의된 uv 값으로 채워줌. 
                // 점과 점 사이를 가상의 점으로 채우다보면 선이 되므로 가상의 점을 더 채운다고 생각하면 됨.

                // tex2D: Texture에 있는 vertex와 uv에 있는 vertex를 일대일 mapping
                fixed4 c = tex2D(_MainTex, i.uv); 
                fixed4 c2 = tex2D(_DissolveTexture, i.uv);
                c2.gb = 0; //red만 살림
                //fixed4 dissolveColour = tex2D(_DissolveTexture, i.uv);
                //c = c - dissolveColour; // 디졸브 텍스쳐에서 흰색인 부분만 검은색으로 남음
                //c.a = c.a - dissolveColour.r; 
	            //clip(dissolveColour.rgb - _DissolveCutoff);
                //c.rgb = dissolveColour.rgb; // alpha 빼고 rgb만 변경
                //c.grb = dissolveColour.rgb; // g,r 채널 숫자 바뀜
                //c = 1 - c; // 색 반전. inverted color. 공포 게임 만들 때 많이 사용한다고..

                // Additive 빛끼리의 합 r,g,b + r,g,b  = 흰색이 될 확률 높음
                // Multiply 빛끼리의 곱 r,g,b (source) * r,g,b (destination) 
                // RGB는 0~1 사이의 값을 가짐. 0~1 * 0~1 => 범위 최소 0, 최대 1. 1보다 작은 값을 곱할수록 수가 감소. 감소연산: 어둡게 할 때 쓰임.
                // Subtractive r,g,b - r,g,b 숫자 작아지므로 어둡게 할 때 사용
                // Mask용. 
                // Divide 가능한데 검은색이 0,0,0이라서 Divide by zero 에러 날 수 있어서 잘 안 씀


                //return fixed4(0, 1, 0, 1); //(R, G, B, A) //green. 보여질 색상(RGBA)을 리턴.
                //return _Colour;
                //return c; // texture 입혀줌


                // C# Lerp(a,b, ratio) : a~b 사이 ratio에 해당하는 값이 나옴
                // c, c2는 color. c가 원본이고 c2가 마스크인 상황.
                // c.uv ~ c2.uv 사이 ratio에 해당하는 지점의 색이 lerp를 통해 나옴
                // 그것을 원본인 c1에서 빼줌. 뒷 파트가 0나오면 리턴값이 1, c1 리턴. 뒷파트가 1 나오면 리턴값이 0. c1 부분은 검은색이 됨. 마스킹된 것. 
                
                //c = c - lerp(c, c2, c2.r); // 이러면 원본에서도 mask 중 빨간 부분이 빠짐
                                                    // dissolve cutoff 값(0 검정 ~ 1 흰색)에서 마스크의 red 값을 빼줌
                c = c - lerp(c,c2,saturate(sign(_DissolveCutoff-c2.r))) * _Colour;
                                       // saturate(sign(_DissolveCutoff-c2.r))값이 1 ==> c2, 마스크 있는 부분 
                                       //                                     값이 0 ==> c1, 원본, 마스크 없는 부분
                                        // 0~1 사이로 정규화하기 위해 2개의 함수 사용
                                        //signum 함수 x=0 일 때 y=0, x>0이면 y=1, x<0 이면 y=-1
                                        //satruate 함수 ( 0 1 사이의 값이 들어오면 0 1 사이 linear한 값 리턴)
                return c;
            }
            ENDCG
        }
    }
}

Shader 언어라서 익숙하지 않아 최대한 자세하게 주석으로 설명을 달았다. vert 부분은 vertice, uv를 정의하는 영역이고, frag (fragment) 부분은 물체의 색상 등을 정의하는 영역이다.

 

 

 

강사님이 알려준 Unity 유용한 오픈소스 모음집

https://github.com/StefanoCecere/awesome-opensource-unity

 

 

후기

알고리즘 테스트와 유니티 개발 테스트가 있었다. 알고리즘은 애초에 별 기대 안 했고, 개발 테스트는 기본은 할 줄 알았는데 생각보다 많은 시행착오를 겪었다. 아직도 유니티 인간이 되려면 먼 것 같다.

shader를 처음 배웠다. 교육 과정 들어오기 전에는 normal map이 뭔지도 몰랐던 나... 이제 uv를 이해한다?! shader를 만들고 다루는 그래픽 아티스트도 있다고 한다. 그래픽 아티스트가 되는 것은 바라지도 않으니 material과 shader 언어에 대해 조금 더 익숙해질 수 있길.

 

 

유데미코리아 바로가기 : https://bit.ly/3b8JGeD

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

 

새로운 가능성의 시작, 유데미 x 웅진씽크빅

글로벌 최신 IT 기술과 실무 교육을 입문부터 심화까지! 프로그래밍, 인공지능, 데이터, 마케팅, 디자인 등 세계 최고의 강의를 경험하세요.

www.udemykorea.com

댓글