Shaders: ShaderLab & 고정함수 쉐이더

이 지침서는 사용자가 자신만의 쉐이더를 생성하여 사용자 게임을 더 멋있게 만들 수 있는 방법을 가르쳐줍니다!

유니티는 ShaderLab이라고 불리는 강력한 쉐이딩과 머티리얼 언어를 갖추고 있습니다. 스타일 상으로 이는 CgFX 와 Direct3D Effects (.FX) 언어와 비슷하여 - ''Material''을 디스플레이 하는데 필요한 모든 것을 표현하여 줍니다.

쉐이더(Shader)는 유니티의 Material Inspector와 여러 종류의 그래픽 하드웨어 성능을 타깃으로 구현한 다수 쉐이더의 구현(SubShaders)에서 보여주는 속성으로, 그 각각은 그리고 완전한 그래픽 하드웨어 랜더링 상태, 고정 함수 파이프라인 설정 혹은 사용할 버텍스(vertex)/프래그먼트(fragment) 을 설명합니다. Vertex와 fragment 프로그램은 고수준의 Cg/HLSL 프로그램 언어입니다.

이 지침서는 고정 함수(fixed function)과 프로그램 가능한(programmable) 파이프라인을 둘 다 사용하는데 쉐이더를 어떻게 작성할 지를 알려줍니다. 저희는 이 지침서의 독자가OpenGL과 Direct3D 랜더링 상태, 고정 함수와 프로그램 가능한 파이프라인에 대한 기초적인 이해가 있고 Cg, HLSL 혹은 GLSL 프로그램 언어에 대해서도 일부 알고 있다고 가정합니다. 일부 쉐이더 지침서와 문서는NVIDIAAMD 개발자 사이트에서도 찾을 수 있습니다.

시작하기

새로운 쉐이더를 생성하려면, 메뉴 바에서 Assets→Create→Shader을 선택하든지 혹은 현존하는 쉐이더를 복제하여 거기서 시작하면 됩니다. 새로운 쉐이더는 Project View에서 그것을 더블클릭 하여 편집할 수 있습니다.

가장 기본적인 쉐이더부터 시작해봅니다:

Shader "Tutorial/Basic" {
    Properties {
        _Color ("Main Color", Color) = (1,0.5,0.5,1)
    }
    SubShader {
        Pass {
            Material {
                Diffuse [_Color]
            }
            Lighting On
        }
    }
} 

위 간단한 쉐이더는 가장 기본적으로 가능한 것을 보여줍니다. Main Color라 불리는 컬러 속성을 설정하고 장미색조의 (red=100% green=50% blue=50% alpha=100%) 디폴트 값을 할당합니다. 그리고 Pass를 호출하여 해당 오브젝트를 랜더링 하고, Pass에서는 diffuse 머티리얼 컴포넌트를 _Color속성으로 설정하고 pre-vertex lighting을 켭니다.

이 쉐이더를 시험하려면, 머티리얼을 하나 생성하고, 드롭다운 메뉴에서(Tutorial→Basic)해당 쉐이더를 선택한 다음 그 머티리얼을 어느 오브젝트에 할당합니다. 머티리얼 인스펙터에 있는 색깔을 수정하고 변화를 감상하십시오. 이제 좀 더 복잡한 것으로 시도해 봅니다!

기초적 버텍스 라이팅(Basic Vertex Lighting)

만일 사용자가 기존의 복잡한 쉐이더를 열면, 언뜻 전체를 이해하기가 어려울 수 있습니다. 일단 사용자가 첫 발을 뗄 수 있도록 유니티에 내장되어 출품되는 VertexLit이라는 쉐이더를 해부해 보겠습니다. 이 쉐이더는 표준적인 버텍스당 (per-vertex) 라이팅을 실행하기 위해 고정 함수 파이프라인(fixed function pipeline)을 사용합니다.

Shader "VertexLit" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }
 
    SubShader {
        Pass {
            Material {
                Diffuse [_Color]
                Ambient [_Color]
                Shininess [_Shininess]
                Specular [_SpecColor]
                Emission [_Emission]
            }
            Lighting On
            SeparateSpecular On
            SetTexture [_MainTex] {
                constantColor [_Color]
                Combine texture * primary DOUBLE, texture * constant
            }
        }
    }
} 

} 모든 쉐이더들은 Shader라는 키워드에서 시작해서 해당 쉐이더의 이름을 나타내는 문자열로 이어집니다. 이 이름은Inspector에 나타나는 이름입니다. 이 쉐이더의 모든 코드는 이 후 중괄호{ } 사이에 위치 되어야 합니다. (이것을 블록(block)이라 부릅니다).

*이름은 짧고 의미 있어야 하지만 _.shader_ 파일 이름과 동일해야 하는 것은 아닙니다. *유니티에서 쉐이더를 서브메뉴에 두려면, 빗금(slash, /)를 사용합니다. 예를 들면, MyShaders/TestMyShaders라 불리는 서브메뉴 안에 Test를 보여주고 혹은 MyShaders→Test로 표시합니다.

쉐이더는 Properties블록으로 구성되어 있고 그 뒤에는 SubShader블록이 따라옵니다. 각각의 블록은 다음 섹션에 설명되어 있습니다.

속성

쉐이더 블록의 시작 부분에서 사용자는 Material Inspector. 에서 아티스트가 편집 할 수 있는 어떤 속성이든 설정할 수 있습니다. VertexLit의 예에서 해당 속성은 이렇게 됩니다:

해당 속성들은 Properties 록 내에서 별개의 라인들로 리스트 되어 있습니다. 각각의 속성은 내부 이름(Color, MainTex)으로 시작하고, 괄호 뒤에는 인스펙터에서 보여주는 이름과 속성의 타입이 나옵니다:

사용 가능한 타입의 종류는 Properties Reference을 참조하십시오. 디폴트 값은 속성의 타입에 따라 다릅니다. 컬러의 사례에서는, 디폴트 값은 네 콤포넌트 벡터이어야 합니다.

이제 속성 설정은 끝났고, 실제 쉐이더를 작성 할 준비가 되었습니다.

쉐이더 본문(The Shader Body)

본격적으로 시작하기 전에, 쉐이더의 기본 구조를 정해 봅니다.

서로 다른 그래픽 하드웨어는 다른 성능을 가지고 있습니다. 예를 들어, 어떤 그래픽 카드는 프래그먼트 프로그램을 지원 하지만 어떤 하드웨어는 지원하지 않습니다; 어떤 것은 pass 당 네 텍스처를 펼칠 수 있지만 어떤 것은 두 개나 하나만 가능한 것 등입니다. 사용자의 하드웨어가 무엇이든 최대한 효율적으로 사용할 수 있도록, 모든 SubShaders를 점검하여 해당 하드웨어가 지원하는 첫 번째 것을 사용합니다.

Shader "Structure Example" {
    Properties { /* ...shader properties... }
    SubShader {
    	// ...subshader that uses vertex/fragment programs...
    }
    SubShader {
    	// ...subshader that uses four textures per pass...
    }
    SubShader {
    	// ...subshader that uses two textures per pass...
    }
    SubShader {
    	// ...subshader that might look ugly but runs on anything :)
    }
} 

이 시스템은 유니티가 모든 현존하는 하드웨어를 지원하고 각각의 품질을 최상으로 보장하는 것을 가능케 합니다. 하지만 이는 또한 결과적으로 긴 쉐이더를 만들게 됩니다. 각 SubShaders 블록 내에서 사용자는 모든 pass가 공유하는 랜더링 상태를 설정하고, 랜더링 pass 자체를 정합니다. 사용 가능한 명령어의 전체 리스트는 SubShader Reference에서 찾을 수 있습니다.

Passes

각 subshader는 pass의 모음입니다. 각 pass에는 object geometry가 랜더링 되므로 적어도 하나 이상의 pass가 존재해야 합니다. 저희 VertexLit 쉐이더에는 단 하나의 pass만 있습니다:

// ...snip...
Pass {
    Material {
        Diffuse [_Color]
        Ambient [_Color]
        Shininess [_Shininess]
        Specular [_SpecColor]
        Emission [_Emission]
    }
    Lighting On
    SeparateSpecular On
    SetTexture [_MainTex] {
        constantColor [_Color]
        Combine texture * primary DOUBLE, texture * constant
    }
}
// ...snip... 

하나의 pass 설정된 어떤 명령이든 해당 geometry를 특정한 방법으로 랜더링 하기 위한 그래픽 하드웨어를 설정합니다. 위 사례의 Material 블록은 우리의 속성 값을 고정 함수 라이팅 머티리얼 설정과 바인딩 합니다. Lighting On이라는 명령은 표준 버텍스 라이팅을 켜고, SeparateSpecular On은 스페큘러 하이라이트(specular highlight)에 별개의 색깔 사용을 허용합니다.

이 모든 명령들이 지금까지는 고정 함수 OpenGL/Direct3D 하드웨어 모델과 직접적으로 연관됩니다. 여기에 관해 더 자세한 정보를 보려면OpenGL red book을 참조하십시오.

다음 명령, SetTexture는 아주 중요한 명령입니다. 이 명령들은 사용자자 쓰고자 하는 텍스처와 어떻게 그것을 조합하고 합치고 우리의 랜더링에 적용할 지를 설정합니다. SetTexture명령 다음에는 우리가 사용하고자 하는 속성 이름이 따라옵니다(_MainTex 여기). 그 다음은 combiner block이 오는데 이는 해당 텍스처를 어떻게 적용되는지를 정합니다.

이 블록에서 Color of the Material, _Color이라 이름하는 상수 컬러 값을 정합니다. 이 상수 컬러는 아래에서 사용될 것 입니다.

다음 명령에서는 해당 텍스처를 어떻게 컬러 값과 조합 할 것인지를 명시합니다. 이는 Command 명령어를 사용하여 해당 텍스처를 다른 것과 혹은 한 색깔과 어떻게 섞을 것인지를 명시합니다:

  //Combine// ''ColorPart'', ''AlphaPart''

여기서 ColorPartAlphaPart는 각각 색깔 조합(RGB)과 알파(A) 콤포넌트를 설정합니다. 만일 AlphaPart가 생략된다면, ColorPart와 같은 조합을 사용합니다.

위VertexLit 의 사례에서:

  //Combine// texture * primary DOUBLE_,_ texture * constant

여기서 texture는 현재의 텍스처 (여기서는_MainTex)로부터 오는 컬러입니다. 이것은 primary 버텍스 컬러와 곱해집니다(*). Primary 컬러는 버텍스 라이팅 컬러이며, 위의 머티리얼 값에서 계산되어 집니다. 마지막으로, 그 결과를 2배로 곱해 빛의 강도를 강화합니다(DOUBLE). The alpha value알파값(코머 뒤에)은 textureconstant값(위constantColor에 설정)과 곱한 것입니다. 또 다른 자주 사용되어지는 combiner 모드는 previous 라 불립니다(이 쉐이더에는 미 사용). 이것은 모든 이전 SetTexture 단계의 결과이며, 여러 텍스처와 그리고/혹은 컬러가 서로 합성하는데 사용되어 집니다.

요약

저희 회사의 VertexLit 쉐이더는 표준 버텍스 라이팅을 설정하고 텍스처 combiner를 설정하여 랜더링 후 빛의 강도가 배가 시킵니다. 더 많은 pass를 쉐이더에 넣으면 차례로 하나씩 랜더링 될 것 입니다. 하지만 지금으로써는 이미 원하는 효과를 얻었으므로 불필요합니다. 우리는 단 하나의 SubShader만 필요하며 고급 사양을 사용하지 않습니다 – 바로 이 쉐이더는 유니티가 지원하는 모드 그래픽 카드에서 작동할 것 입니다. 쉐이더는 우리가 생각할 수 있는 가장 기본적은 쉐이더 입니다. 우리는 어떠한 일부 하드웨어에 특정적인 오퍼레이션을 사용하지도 않았고, ShaderLab 과 Cg가 제공해야 하는 특별하거나 멋진 명령을 더 사용하지도 않았습니다.

다음 장next chapter에서는 계속해서 Cg 언어를 사용하여 맞춤형 버텍스 & 프로그램을 작성하는 방법을 설명하겠습니다.

역링크