http://forum.unity3d.com/threads/spine-2d-skeletal-animation.169758/page-2


http://www.strandedsoft.com/2d-animations-using-spine-2d/


http://www.reddit.com/r/Unity2D/comments/1sikn4/is_spine_animation_viable_for_unity_or_would_i_be/


http://forum.unity3d.com/threads/spine-2d-skeletal-animation.169758/page-2

1. 스크립트에 새 C# 스크립트 만들기 > PlayerMovement를 만든다.


2. 스크립트 내용을 다음과 같이 작성

using UnityEngine;

using System.Collections;


public class PlayerMovement : MonoBehaviour {


public float speed;

void Update () {


if(Input.GetKey(KeyCode.D)){

transform.Translate (Vector2.right * speed);

}

if(Input.GetKey(KeyCode.A)){

transform.Translate (-Vector2.right * speed);

}

if(Input.GetKey(KeyCode.W)){

transform.Translate (Vector2.up * speed);

}

if(Input.GetKey(KeyCode.S)){

transform.Translate (-Vector2.up * speed);

}

}

}


3. 씬에 필요한 프리팹을 넣고 그 프리팹의 Inspector에서 Add Component 버튼 > Scripts > 아까 만든 PlayerMovement 선택


4. Speed 값을 조정. 보통은 0.3 정도?


5. 플레이 ▶ 버튼을 눌러 실행시키면 해당 프리팹이 키보드 WASD 키로 상하좌우 이동이 가능하게 된다.

'프로그래밍 > Unity' 카테고리의 다른 글

Unity - 게임 제작용 테크닉 모음  (0) 2019.07.15
[Unity] Spine 오브젝트를 Mask하기  (1) 2018.05.06
유니티 셰이더 강좌 #6  (1) 2014.08.15
유니티 셰이더 강좌 #5  (0) 2014.08.15
유니티 셰이더 강좌 #4  (0) 2014.08.15

출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-6


---


 안녕하세요, 흑기사입니다.
 

 (개인휴가, 명절 연휴, 컨퍼런스가 연이어 발생하는 바람에 부득이하게 오랜 기간동안 글을 포스팅하지 못하게 되었네요…;)


 이전까지 유니티에서 셰이더를 제작하는 방법 중에서 fixed function shader를 작성하는 법을 알아보았습니다. 물론 동시에 ShaderLab의 기본을 익히는 시간도 가졌습니다. 고정 파이프 라인을 사용하는 fixed function shader의 특징을 다시금 짚어보자면:


     -버텍스 당 라이팅 연산이 이루어짐.
     -커스터마이징 불가.
     -하지만 구형 디바이스에서도 동작하는 장점이 있음.

 이 fixed function shader은 잘 사용하면 의외로 많은 쓸모가 있는 셰이더 작성 방식입니다. 따라서 많은 여러가지 예제를 접해보면 도움이 많이 됩니다. 이러한 예제들은 다음에 기회가 된다면 알아보는 시간을 가지도록 하겠습니다.  

 이번 시간에는 유니티에서 셰이더를 작성하는 또 다른 방법인 vertex/fragment program에 대해서 알아보겠습니다.

 앞서 고정 파이프 라인을 사용하는 fixed function shader의 특징을 짚어본 이유가 바로 이번에 다룰 셰이더는 프로그래머블 파이프라인을 이용하는 방식으로서 서로 대조적인 방식이기 때문입니다. 프로그래머블 파이프라인하면 가장 먼저 떠오르는 것은 '프로그래머블' 하다는 것입니다. 즉, 프로그래머가 그래픽 파이프라인에서의 버텍스 레벨과 프래그먼트 레벨(픽셀 레벨)에 각각 개입하여 자기 마음대로 커스텀한 함수들을 제작할 수 있다는 것입니다.
 사실 이것은 이미 예전부터 게임개발을 하셨던 분들에게는 아주 익숙한 셰이더 작성법입니다. vertex/fragment program 라는 타이틀보다는 'vertex&pixel shader' 라고 부르는 것에 더 친숙할 수도 있지만, 그냥 명칭의 차이일 뿐이며 동일한 것으로 생각하면 됩니다. 
 이러한 셰이더를 작성하기 위해서는 우리는 별도의 셰이더 언어를 사용하여야 합니다. 그런데 셰이더 언어는 한가지가 아닙니다. 예를 들어 OpenGL에 포함된GLSL, DirectX에 포함된 HLSL, NVIDIA에서 개발한 Cg 등이 있습니다. 이처럼 그래픽API를 어떤 것을 사용하느냐에 따라서 다른 셰이딩 언어를 사용해야 하는 경우가 발생할 수도 있습니다. (Cg의 경우는 OpenGL과 DirectX 모두에 호환됩니다.)

 자, 그러면 이러한 vertex/fragment program을 유니티에서 작성하기 위해서는 어떻게 해야 할지 알아보겠습니다. 그런데 그 전에 먼저 언급해야 할 것이 있습니다. 
 vertex/fragment program 자체의 제작 방식은 매우 일반적으로 언급되는 프로그래머블 커스텀 셰이더 작성법입니다.  이 글에서는 단지 유니티 엔진에서 이 셰이더 방식을 '적용'하려면 어떻게 해야 하느냐에 대한 것일 뿐,  vertex/fragment program의 근본적인 개념과 작성법을 설명하지는 않을 것입니다. 따라서 이에 관련한 프로그래머블 파이프 라인의 개념이나 셰이딩 언어의 문법 등에 대해서는 (혹시 필요한 분들은) 직접 학습이나 리서치하시길 권장합니다. 

 그럼 이제 정말 유니티에서 vertex/fragment program 를 어떻게 작성하는지 알아보겠습니다. 
 (여기서 우리는 vertex/fragment program의 제작을 위해서 Cg를 사용하도록 합니다. 그 이유는 나중에 다시 언급하도록 하겠습니다.)
 사실 우리가 실제로 필요한 것은 바로 버텍스 프로그램(버텍스 셰이더)와 프래그먼트 프로그램(픽셀 셰이더), 이렇게 두 개의 셰이더 함수를 Cg로 작성하는 것뿐입니다. 이 얘긴 결국 유니티 셰이더의 나머지 부분은 앞서 익혔던 ShaderLab 문법을 이용하여 그대로 작성할 수 있으며, 단지 버텍스/프래그먼트 셰이더 함수에 해당하는 부분만 Cg로 작성해서 끼워넣기만 하면 목표달성이라는 뜻입니다. 글로 표현하려니 뭔가 이상하군요.. 그냥 실제 코드를 보자면:

Shader "MyCustomShader" { 
     SubShader { 
       Pass { 
           // ...여러가지 pass관련 설정… 
            
           CGPROGRAM 
              // Cg code ... 
           ENDCG 


           // ...그밖의 pass관련 설정… 
       } 
    } 
}

 음, 이미 우리가 알고 있는 셰이더의 모습과 많은 차이는 없군요(다행입니다...).차이점이라면 CGPROGRAM과 ENDCG라는 키워드가 있는 부분이 새롭게 등장했다는 것입니다. 아마 당연히 이 부분이 vertex/fragment program에 대한 것이라고 느껴지실 것입니다. 

 즉, 우리는 CGPROGRAM 와 ENDCG 키워드 사이에서 vertex/fragment program을 Cg언어로 작성할 것입니다. 이렇게 작성된 Cg조각은 유니티 엔진에 의해서 각각의 플랫폼들에 대해 적절히 동작하도록 자동컴파일됩니다. 이전에 익혔던 fixed function shader의 외형과 비교하자면, 단지 CGPROGRAM~ENDCG 블럭이 추가되었다는 것 외에는 크게 변한 점이 없습니다. 절대 복잡하지 않다는 점을 다시 한번 강조합니다. 
 

 이 블럭부분을 한번 자세히 들어가 볼까요.

 CGPROGRAM ~ ENDCG 블럭에는 순수 Cg코드 외에 여러 컴파일 지시자를 포함할 수 있습니다. 거의 기본적으로 포함되는 지시자는 바로 #pragma vertex 과 #pragma fragment 라는 지시자입니다:


     CGPROGRAM 


           #pragma vertex vertfunc 
           #pragma fragment fragfunc 

           // 여기에 Cg code
     ENDCG


 이것을 해석해보자면, vertfunc 라는 이름을 가진 함수가 버텍스 프로그램 (셰이더 함수)이며, fragfunc 라는 이름을 가진 함수는 프래그먼트 프로그램 (셰이더 함수)라는 것을 가리키는 것입니다. 그리고 이 vertfuc와 fragfunc 함수들을 Cg코드로 작성될 것을 의미합니다. 아주 간단합니다.


 그밖의 유용한 컴파일 지시자들 중에는 #pragma target, #pragma only_renderers, #pragma exclude_renderers등이 있습니다.


 #pragma target 은 어떤 버전의 셰이더 모델로 컴파일할 지를 지정할 때 쓰입니다. 

 유니티는 기본적으로 셰이더 2.0 버전에 맞추어 컴파일합니다. 만약 다른 버전으로 컴파일을 원한다면 아래의 예와 같이 지정할 수 있습니다:


     #pragma target 3.0 


이것은 셰이더 3.0 버전으로 컴파일을 하라는 뜻이겠죠.
 

 그리고 유니티는 지원가능한 렌더러들(d3d9, OpenGL 등등)에 대해서 모든 셰이더들을 컴파일하게 되는데요.  #pragma only_renderers와 #pragma exclude_renderes를 이용하여 특정 렌더러들에 대해서만 컴파일을 하거나, 특정 렌더러들에 대해서는 컴파일하지 않도록 할 수 있습니다. 현재 지원되는 렌더러들은 d3d9, d3d11, opengl, gles, xbox360, ps3, flash 입니다. 

 한가지 예를 들면: 


    #pragma exclude_renderers xbox360 ps3

이러면 xbox360과 ps3 렌더러에 대해서는 해당 셰이더에 대한 컴파일을 하지 않도록 합니다.


 그 밖에도 셰이더를 작성하다보면 여러 컴파일 지시자들을 사용하게 되는데요, 이들에 대해서는 그것들이 등장할 때마다 알아보도록 하겠습니다.

 그럼 오늘 언급한 내용을 모두 포함한 셰이더의 모습을 살펴보겠습니다:


Shader "MyCustomShader" { 
     SubShader { 
        Pass { 
           // ... 
            
          CGPROGRAM 
                #pragma vertex vertfunc 
                #pragma fragment fragfunc  
                #pragma target 3.0 
                #pragma exclude_renderers xbox360 ps3 
      
                 // add Cg code
          ENDCG 

          // ...
       } 
    } 
}


오늘은 여기까지입니다~
다음에는 유니티에서의 실제 간단한 vertex/fragment program의  예를 알아보도록 하겠습니다. 

감사합니다~


'프로그래밍 > Unity' 카테고리의 다른 글

[Unity] Spine 오브젝트를 Mask하기  (1) 2018.05.06
캐릭터 이동시키기(WASD)  (0) 2014.12.13
유니티 셰이더 강좌 #5  (0) 2014.08.15
유니티 셰이더 강좌 #4  (0) 2014.08.15
유니티 셰이더 강좌 #3  (0) 2014.08.15

출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-5


---


 안녕하세요, 흑기사입니다.

 이번 시간에는 오브젝트를 반투명으로 그려볼까 합니다. 지난 시간 마지막에 작성한 셰이더 코드를 약간 수정하여서 반투명 구(sphere)를 한번 그려보도록 하겠습니다.

 자, 우리가 오브젝트를 반투명으로 만들기 위해서 가장 먼저 해야 할 것은, 바로 블렌딩에 대한 디바이스의 렌더 상태를 변경해주는 것입니다. 그러니까 이 말은 앞으로 곧 렌더링할 오브젝트의 결과색상값과 이미 버퍼에 있는 색상값(배경색상)을 어떻게 섞을 지를 명시해줘야 할 필요가 있다는 뜻입니다. 만약 명시하지 않으면(지금까지와 같이), 블렌딩은 비활성화(디폴트)되어 있으므로 결국 오브젝트 렌더링 시에 결과 색상값을 그냥 버퍼에 덮어쓰므로 반투명을 표현할 수 없게 됩니다. 따라서 우리는 이것을 위해서 Blend 라는 ShaderLab 커맨드를 사용해서 렌더 상태를 블렌딩이 활성화되도록 지정해줘야 합니다:

 Blend SrcFactor DstFactor 

 위의 코드는 Blend 커맨드의 가장 기본적인 구문입니다. (다른  형식의 구문도 있지만, 여기서는 설명생략)
 이 구문을 해석하자면,
 (이제 나올 결과 색상값 x SrcFactor) + (이미 스크린(버퍼)에  존재하는 색상값 x DstFactor)
 이렇게 색상을 섞어서 최종 색상값을 결정하겠다는 뜻입니다. SrcFactor와 DstFactor 자리에는 우리가 직접 속성을 지정해주어야 하는데요. 사용할 수 있는 속성들은 아래와 같습니다:

 One, Zero,  
 SrcColor, SrcAlpha,  
 DstColor, DstAlpha,   
 OneMinusSrcColor, OneMinusSrcAlpha,  
 OneMinusDstColor, OneMinusDstAlpha 

 결국 우리는 이것들을 사용하여 블렌딩 연산을 지정하게 되는 것입니다. 뭔가 많아 보이고 복잡해 보이는데요... 사실 속성의 이름들을 하나하나 살펴보면 그 의미는 직관적으로 쉽게 알 수 있습니다. 어떤 세부 설명이 필요하다면, 유니티 메뉴얼을 통해 자세한 내용을 참조하실 수 있습니다. (일단 이번 글에서는 스킵해도 상관없습니다.)
 이제 당장 우리가 필요한 속성들만 취해서 구문을 작성해 보겠습니다. 우리는 아주 기본적인 알파 블렌딩을 하는 것이 목표이므로, SrcFactor에는 오브젝트 알파값을 지정하고 DstFactor에는 (1-오브젝트 알파값)을 지정하여야 합니다. 
 따라서 현재 오브젝트 알파값을 의미하는 SrcAlpha 와  (1-오브젝트 알파값)을 의미하는 OneMinusSrcAlpha를 속성 팩터로 사용하여 블렌드 구문을 작성합니다:

 Blend SrcAlpha OneMinusSrcAlpha 

 이 경우에 만약 오브젝트의 알파값이 0.6 이라고 한다면, SrcAlpha = 0.6 이고 OneMinusSrcAlpha = 0.4 이므로 오브젝트 색상과 배경 색상은 60% 대 40% 비율로 섞여서 결국 오브젝트가 반투명하게 보이는 효과를 나타내는 것입니다. 

 블렌딩 설정은 쉽게 끝났군요. 그 다음은... 
 아, 오브젝트 알파값을 지정해줘야 겠군요. 우리가 직접 오브젝트의 알파값을 정해주면 재밌을 것 같습니다.
 에디터에서 알파값을 지정할 수 있도록 하면 되겠군요. 이건 알파값을 지정할 수 있게 프로퍼티를 추가해주면 되겠죠. 
 이전에 Main Color 라는 프로퍼티를 추가했다가, 라이팅 연산을 없애면서 이 프로퍼티도 함께 삭제했었는데요. 알파값 지정을 위해서 다시 이 프로퍼티를 살리도록 합니다:

Shader "2Textures SemiTrans" { 
    Properties { 
        _Color ("Main Color", COLOR) = (1,1,1,1) 
        _MainTex("Texture"2D) = "white" {} 
        _SubTex("Texture"2D) = "white" {} 
    } 
    SubShader { 
        Pass { 
                 Blend SrcAlpha OneMinusSrcAlpha 
                
                 SetTexture [_MainTex] { 
                        Combine texture 
                 } 
                                                                       
                 SetTexture [_SubTex] { 
                        Combine texture lerp(texture) previous
                 } 
        } 
    } 
} 


자, 아직 코드가 완성되지 않았습니다. 
프로퍼티로 다시 추가한 _Color의 알파값을 사용하는 곳이 아직 없군요... 
이제 우리는 두번째 SetTexture 블럭을 다음과 같이 수정하려 합니다.

SetTexture [_SubTex] { 
          ConstantColor[_Color] 
          Combine texture lerp(texture) previous, constant 
}

 윽, 당황하지 마시기 바랍니다. 갑자기 뭔가 복잡한 코드가 추가되어 혼란스러울 수 있지만, 막상 내용은 별 것 아닙니다. 
 우선 Combine 커맨드를 주목하시기 바랍니다. 콤마(,)가 추가되었군요. 이건 대체 뭘까요? 
 Combine 커맨드에서 우리는 콤마(,)를 이용하여 색상값 연산과 알파값 연산을 분리할 수 있습니다. 분리한다는 말은 결국.. 콤마를 사용하지 않았던 이전에는 색상과 알파 연산이 동일하게 적용되었다는 얘기입니다. 그렇습니다... 만약 연산을 따로 콤마(,)를 사용하여 구분하지 않는다면, 색상(rgb)과 알파(a) 모두 하나의 Combine 연산을 통해서 계산합니다. (지금까지 그래왔겠군요.)
 그런데 우리는 임의로 직접 어떤 알파값을 지정해주길 필요가 있기 때문에, 일단 알파에 대한 연산을 따로 구분하기 위해 콤마(,)를 넣은 것입니다. 그 다음 알파연산 만을 위한 구문을 따로 작성했습니다. 위의 예제의 경우, 별다른 연산이 없이 constant 만 있는 걸 보니, 그냥 constant 라는 것을 알파값으로 사용한다는 의미겠군요. 여기서 그럼 constant는 또 뭘까요? constant는 단어 뜻 그대로 그냥 어떤 상수값을 말합니다. 그게 어떤 상수값인지는ConstantColor 라는 커맨드를 사용하여 지정합니다. 그러니까 ConstantColor (1,1,1,1) 이런 식으로 지정해주면, constant 값은 결국 (1,1,1,1)이 되는 셈이지요. 그런데 우리는 하나 더 나아가서 프로퍼티 _Color값을 알파값으로 이용하려 하므로 ConstantColor [_Color] 라고 지정한 것입니다. 결과적으로 constant는 _Color값이 되는 것이고, 마침내 우리는 constant의 알파값을 최종 알파값으로 사용하도록 지정했습니다.

지금까지 내용을 정리한 코드는 다음과 같습니다:

Shader "2Textures SemiTrans" { 
    Properties { 
        _Color ("Main Color", COLOR) = (1,1,1,1) 
        _MainTex("Texture"2D) = "white" {} 
        _SubTex("Texture"2D) = "white" {} 
    } 
    SubShader { 
        Pass { 
                 Blend SrcAlpha OneMinusSrcAlpha 
                
                 SetTexture [_MainTex] { 
                          Combine texture 
                 } 
                                                                       
                 SetTexture [_SubTex] { 
                          ConstantColor[_Color] 
                          Combine texture lerp(texture) previous, constant 
                 }
        } 
    } 
} 

그 결과는 이렇습니다:


 반투명이 제대로 적용했는지를 확실히 구문하기 위해서 더 먼 뒷쪽에 다른 두 오브젝트를 추가했는데요. 반투명 처리가 잘 적용되어 보입니다. 인스펙터 창에서 Main Color의 알파값을 조절해보면 구(sphere)의 반투명 정도가 변하는 게 느껴지는군요... 잘 구현된 것 같습니다!

 어라.... 근데 좀 들여다 보니까 약간 이상한 부분이 바로 눈에 들어옵니다!
 뒤쪽에 배치된 노란색 큐브를 주목해보면, 초록색 캡슐 오브젝트와는 달리 구(sphere)와 시야에서 겹치는 부분의 색상이 제대로 나타나지 않는다는 것을 쉽게 알 수 있습니다. 왜 이런 일이 생겼을까요? 
 이미 어느 정도는 벌써 눈치채셨으리라 생각이 듭니다. 우리는 지금 반투명 오브젝트를 그리는 중이고, 반투명 오브젝트는 일반적인 불투명한 오브젝트와는 다르게 그리는 순서에 큰 영향을 받지요. 그런데 위의 경우에 반투명 구  (sphere)를 일반적인 불투명한 오브젝트와 함께 그렸기 때문에 문제가 발생하게 되었던 것입니다. 어떤 오브젝트가 먼저 그려질 지 우리가 알 수 없기 때문입니다. 위의 결과로 볼 때에 노란색 큐브가 구(sphere)보다 늦게 렌더링 되어버려서 이슈가 생겨 버렸군요.
 이걸 해결하기 위해서는 모든 불투명 오브젝트들이 렌더된 한 후에 반투명 오브젝트를 렌더링되도록 하면 깔끔하게 정리가 됩니다. 
 이를 위해서 우리는 렌더링 순서 지정을 위해서 Queue 태그라는 것을 사용할 것입니다. 
 
 Queue 태그는 렌더링 순서를 결정하는 데에 사용하는 태그입니다.
유니티에서는 렌더링 순서를 구분하는 가장 일반적인 케이스를 고려하여, 다음과 같이 5가지의 큐를 미리 정의해 놓았으며, 아래의 큐 순서대로 렌더링을 하게 됩니다.

1) Background 
2) Geometry (디폴트) 
3) AlphaTest
4) Transparent
5) Overlay

 우리가 셰이더에 아무런 Queue 태그를 지정하지 않으면, 유니티는 해당 셰이더를 디폴트로 Geometry 큐로 지정합니다. 그런데 만약 필요하다면, 우리가 해당 셰이더에 5개 중 하나의 큐를 직접 지정해 줄 수 있습니다. 그렇게 되면 결국 렌더링 순서를 제어할 수 있게 되겠죠.
 지금 우리가 작성하는 셰이더의 경우, 반투명 오브젝트를 렌더링해야 하기 때문에 Geometry 큐보다 늦게 렌더링되는 큐로 지정해줘야 할 필요가 있습니다. 마침 적당한 큐가 눈에 띄는군요. 바로 Transparent 큐입니다. 이름대로 이 큐(queue)는 반투명 렌더링되는 경우를 위해 미리 정의되어 있는 큐(queue)입니다. 따라서 우리는 다음과 같이 ShaderLab의 Tags 구문을 이용하여 Queue 태그를 새로 지정할 수 있습니다:

 Tags { "Queue" = "Transparent" } 

이렇게 추가하면 우리는 반투명 구(sphere)를 다른 불투명한 오브젝트보다 더 늦게 렌더링하게 됩니다. 또한 Transparent 큐 안에서도 오브젝트들이 거리에 따라 내부적으로 정렬되므로, 더 이상 렌더링 순서에 따른 문제는 발생하지 않게 됩니다. 이렇듯 우리는 큐를 지정하여 렌더링 순서를 원하는대로 제어할 수 있습니다!

자, 그럼 다시 우리가 작성하던 코드로 돌아오면:

Shader "2Textures SemiTrans" { 
    Properties { 
        _Color ("Main Color", COLOR) = (1,1,1,1) 
        _MainTex("Texture"2D) = "white" {} 
        _SubTex("Texture"2D) = "white" {} 
    } 
    SubShader { 
  Tags { "Queue" = "Transparent}
        Pass { 
                 Blend SrcAlpha OneMinusSrcAlpha 
                
                 SetTexture [_MainTex] { 
                          Combine texture 
                 } 
                                                                       
                 SetTexture [_SubTex] { 
                          ConstantColor[_Color] 
                          Combine texture lerp(texture) previous, constant 
                 }
        } 
    } 
} 

 여기서 잠깐 하나만 더~.
우리는 Queue 태그를 지정할 때에 SubShader의 Tags를 사용했다는 것을 주의하시기 바랍니다. 이 언급을 하는 이유는 바로 ShaderLab 구문의 Tags는 SubShader 뿐만 아니라 Pass의 Tags도 있기 때문입니다. 그러니까 ShaderLab에는 SubShader Tags와 Pass Tags가 각각 존재하기 때문에 이 둘을 혼동하지 않도록 합니다. (Pass 태그는 나중에 배울 기회가 있을 것입니다.)

 그럼 이제 결과를 한번 보겠습니다:


이제 제대로 그려지는군요. 보기 좋습니다!

끝으로 Queue 태그에 대해서 하나만 더 이야기하고 마치겠습니다. 
앞서  유니티에는 미리 정의된 렌더링 큐(queue)가 5개 있다고 설명드렸고, 필요하면 다른 큐로 지정할 수 있다고 하였습니다. 그런데 이 렌더링 큐는 5개만 존재하는 것은 아니고, 필요에 따라서 직접 정의할 수 있습니다. 이 큐들은 유니티 내부에서 정수 인덱스로 표현됩니다. Background 는 1000, Geometry 는 2000, AlphaTest 는 2450, Transparent 는 3000 그리고 Overlay 는 4000 입니다. 그리고 큐(queue) 인덱스값 순서가 곧 렌더링 순서와 동일합니다. 따라서 우리가 만약 필요하다면 적당한 큐 인덱스를 정의하여 어떤 필요한 타이밍에 렌더링이 가능하도록 할 수 있습니다.
 예를 들면:

 Tags { "Queue" = "Geometry+1" }

 이렇게 정의하면 오브젝트는 2001번 렌더 큐에 속하게 되는 것이고, Geometry 큐(2000)와 AlphaTest 큐(2450) 사이에서 렌더링되는 것입니다.

 오늘은 여기까지 입니다.
 감사합니다~


'프로그래밍 > Unity' 카테고리의 다른 글

캐릭터 이동시키기(WASD)  (0) 2014.12.13
유니티 셰이더 강좌 #6  (1) 2014.08.15
유니티 셰이더 강좌 #4  (0) 2014.08.15
유니티 셰이더 강좌 #3  (0) 2014.08.15
유니티 셰이더 강좌 #2  (0) 2014.08.15
출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-4

---

 안녕하세요, 흑기사 입니다. 
 지난 시간에는 텍스처를 적용하여 fixed function shader를 어떻게 작성할 수 있는지를 알아보는 시간을 가졌습니다. 이번 시간에는 텍스처 활용에 대해서 좀 더 알아볼까 합니다.
 
 아! 갑자기 오브젝트에 텍스처 두 장을 입혀보고 싶은 생각이 드는군요. 별다른 이유는 없고, 그냥 문득 그런 생각이 떠올랐습니다; 그럼 한번 시작해볼까요?

 우리가 해야 할 일은 매우 명확합니다. 일단 텍스처를 위한 프로퍼티가 하나 더 추가하고, 텍스처를 적용하여 연산하기 위해 기존 SetTexture 외에 또 다른 SetTexture 커맨드를 추가해 주면 끝입니다.

 따라서 새롭게 작성된 셰이더 코드는:

Shader "2Textures" { 
    Properties { 
         _MainTex("Texture"2D) = "white" {} 
         _SubTex("Texture"2D) = "white" {} 
    } 
    SubShader { 
           Pass { 
               SetTexture [_MainTex] { 
                 Combine texture DOUBLE 
               } 
                                                 
               SetTexture [_SubTex] { 
                 Combine texture * previous 
               } 
           } 
    } 
}

어라... 근데 뭔가 예전 코드에 비해 갑자기 허전해졌습니다!
네, 그렇습니다... 이번에는 라이팅 연산은 제외하고 순수하게 텍스처만 합성하여 오브젝트에 적용해볼까 합니다. 따라서 재질과 라이팅에 관한 코드는 과감하게 삭제하였습니다. 혼란스러워 하지 마시길 바랍니다.(오히려 코드는 더 간단해졌습니다!)

 코드를 한번 살펴보도록 하겠습니다. 진한 글씨로 표시된 부분을 주목할 필요가 있군요. 일단 라이팅 연산을 배제했기 때문에 기존에 SetTexture 연산을 정의할 때 사용하던 primary 가 없어졌군요. 이전 결과값이 없으므로, 그냥 첫번째 텍스처 색상값만 두 배 연산하도록 했습니다. 그리고 다음에 등장하는 두번째 SetTexture 커맨드를 보시면 뭔가 새로운 것이 등장했습니다. 바로 previous 라는 녀석인데, 이것은 바로 직전의 SetTexture에서의 연산를 통해서 나온 결과값을 의미합니다. 따라서 지금 코드에서는 previous 는 앞서 _MainTex 색상값을 두배한 값을 가리키게 됩니다.

이번에는 인스펙터창을 살펴볼까요? 
우리는 여기서 서로 다른 2장의 텍스처를 설정하게 됩니다:

그래서 적용 결과는:

적절히 두 이미지가 합성되어서 나오는군요...  

음, 근데 이번에는 생각보다 그렇게 재밌지는 않았군요. 그럼 다른 것을 하나 더 해보겠습니다.

 이번에는 벽돌 텍스처 이미지 대신, 아래의 캐릭터 이미지를 덧붙여 보겠습니다. 

 이전과는 상황이 좀 다릅니다. 이번에는 검정부분은 적용되어선 안되고, 캐릭터 부분은 적용하되 이전 텍스처 이미지와 섞이지 않도록 합니다. 쉽게 다시 말하면, 캐릭터 모양의 스티커를 떼어서 구(sphere)에 붙인다고 생각하면 되겠습니다.


 만약 앞서 작성한 셰이더 코드를 그대로 사용하면 어떤 결과가 나올까요?
 결과는 이렇게 됩니다:

당연하겠지만.. 원치 않은 결과가 나왔습니다. 단순히 텍스처 색상값을 곱했기 때문에 캐릭터는 이전 텍스처 이미지와 섞여버렸고, 출력하고 싶지 않은 검정부분은 그대로 그려질 수 밖에 없군요. 이거 뭔가 다른 조치가 필요하다는 것이 느껴집니다.


우선 우리는 원하는 것을 얻기 위해서 캐릭터 텍스처의 알파값을 사용하기로 합니다. 사실 지금 사용하는 캐릭터 텍스처 이미지의 알파값을 확인해보면 다음과 같이 제작되었습니다:

그러니까 캐릭터 부분은 알파값이 1 이고, 다른 검정색으로 칠해진 부분은 알파값이 0 이었군요. 음, 그렇다면 해당 알파값의 비중만큼 해당 캐릭터 텍스처의 색상을 반영하도록 한다면 원하는 결과를 얻을 수 있을 것 같습니다. (즉, 알파값이 0인 경우에는 캐릭터 텍스처 색상값을 적용하지 않게 되겠죠.)


이것을 위해 마지막 SetTexture 커맨드의 블럭의 코드를 다음과 같이 수정하겠습니다:

 SetTexture [_SubTex] { 
       Combine texture lerp(texture) previous 
 } 

lerp() 라는 것이 등장했군요. 음, 뭔가 어려운 듯이 보이지만 알고보면 간단한 구문입니다:
 
 A lerp (B)  C
 
A와 C를 보간하여 결과값을 만들게 되는데, 그 기준값으로 B의 알파값를 사용하겠다는 것을 의미합니다. 즉, B의 알파는 0~1의 범위를 가지며 B = 1일 경우엔 결과는 A가 되며, B = 0일 경우에 결과는 C가 됩니다. 따라서 이번 예제에서는 캐릭터 텍스처의 알파값을 이용하여, 적절히 두 텍스처의 색상값을 취하여 원하는 결과를 얻어낼 수 있는 것입니다.

결국 최종 코드와 결과는:

Shader "2Textures" { 
    Properties { 
         _MainTex("Texture"2D) = "white" {} 
         _SubTex("Texture"2D) = "white" {} 
    } 
    SubShader { 
           Pass { 
               SetTexture [_MainTex] { 
                 Combine texture
               } 
                                                 
               SetTexture [_SubTex] { 
                 Combine texture lerp(texture) previous 
               } 
           } 
    } 
}

이번 시간은 여기까지입니다.

감사합니다~



'프로그래밍 > Unity' 카테고리의 다른 글

유니티 셰이더 강좌 #6  (1) 2014.08.15
유니티 셰이더 강좌 #5  (0) 2014.08.15
유니티 셰이더 강좌 #3  (0) 2014.08.15
유니티 셰이더 강좌 #2  (0) 2014.08.15
유니티 셰이더 강좌 #1  (0) 2014.08.15

출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-3


---


지난 시간에는 마지막에 fixed function shader 에 대해서 짧게 언급한 후 마무리 했었는데요..
 조금만 더 이야기 해보겠습니다.
 앞서 이미 얘기한 대로 fixed function shader는 이름에서도 느껴지듯이 고정기능 파이프 라인을 사용합니다. 이미 아실테지만, 원래 셰이더를 작성한다고 하면 당연히 프로그래머블 파이프라인을 떠올리는 것이 상식이죠. 즉, GPU의 버텍스 레벨과 픽섹 레벨 단계를 우리가 마음껏 코딩(버텍스셰이더, 픽셀셰이더)하여 원하는대로 멋대로 구현하는 것에 진정한 의미가 있습니다. 
 그런데 지금 현 시점에서 우리는 클래식한 고정기능 파이프 라인을 이용한 셰이더(fixed function shader)를 작성하고 있는 중입니다.
 이전 마지막 코드를 살펴볼까요,
Shader "VertexLit White" {  
    SubShader {  
        Pass {  
            Material {  
                Diffuse (1,1,1,1)  
                Ambient (1,1,1,1)  
            }  
            Lighting On  
        }  
    }  
}
 다시 들여다보니 정말 프로그래밍을 했다고 할 수 있는 부분은 보이지 않네요. 
 전부 그냥 무언가 제공되는 것에 대한 '설정'만을 했을 뿐입니다. 재질의 값을 설정하고 라이팅 기능을 그냥 켰을 뿐이고...음.. 고정기능 파이프라인을 사용한 것이 확실하군요... 역시 제대로 셰이더 코드를 작성했고 내세우기는 어려워 보이네요.
 게다가 고정기능 파이프라인에서는 버텍스당 라이팅(per-vertex lighting)을 사용합니다. 즉, 라이팅 연산이 버텍스 레벨에서 이루어진다는 것이죠. 따라서 픽셀 레벨에서의 라이팅 연산에 비해서는 연산 부하는 적겠지만, 당연히 그만큼 품질이 아주 떨어집니다. 더욱 슬픈 건 무엇인가를 개선하거나 수정하기 위한 프로그래머블한 방법은 없다는 것입니다. 고정기능 파이프라인이기 때문이지요!... 결국 할 수 있는 게 별로 없는 셰이더군요.    
그렇다면 fixed function shader를 사용하는 것은 진짜 쓸모없는 일일까요? (그럼 지금까지 뭘 한거죠.. ;)

 결론부터 말하자면 그렇지는 않습니다. 다행히 쓸모가 있습니다. 
 fixed function shader는 다양한 하드웨어에 대응에 꼭 필요합니다. 이미 예전에 언급한 적 있지만 우리는 다양한 플랫폼과 다양한 디바이스 환경에 대해 대응해야 합니다. 그러기 위해서 fixed function shader와 같은 저레벨 셰이더(?) 작성이 필요한 것이죠. 특히 오래된, 후진 그래픽 하드웨어 사양에 대한 fallback 셰이더로써 사용되는 데 아주 유용합니다. 유니티 빌트-인 셰이더들을 살펴보시면 아시겠지만, 최종적인 fallback은 fixed function shader를 두고 있습니다. 따라서 어떠한 디바이스에서도 대응할 수 있게 끔 구성해 놓은 것이죠. 
 그리고 품질을 대한 것은 논외로 하더라도 간단한 기능의 셰이더들은 fixed function shader로 제작하는 것이 더 수월할 수 있습니다. fixed function shader는 ShaderLab으로만 구성되기 때문에 쉽고 간결하게 깔끔히 작성할 수 있기 때문이죠. 그래서 알아두면 손해보지는 않을 거라고 생각이 됩니다.
 
지금까지 fixed function shader에 대한 이야기를 잠깐 해보았는데요...
앞으로 당분간은 ShaderLab만을 이용하여 제작할 수 있는 이 fixed function shader 에 대하여 계속 집중해보겠습니다.

 그럼 돌아가서 마지막에 작성한 셰이더를 다시 살펴봅니다.

Shader "VertexLit White" {  
    SubShader {  
        Pass {  
            Material {  
                Diffuse (1,1,1,1)  
                Ambient (1,1,1,1)  
            }  
            Lighting On  
        }  
    }  
}
 아, 우리가 이전에 구(sphere)에 적용했던 기억이 납니다. 조명에 영향받은 흰색 공을 화면에 띄웠었죠...
 근데 문득, 구(sphere)를 빨간색으로 해보면 더 멋질 것 같은 생각이 듭니다.
 그러려면 간단히 코드에서 재질의 Diffuse, Ambient 색상값을 변경하면 해결되겠군요:

Material {  
      Diffuse (1,0,0,1)  
      Ambient (1,0,0,1)  
}

아주 간단합니다.
그런데 이렇게 하면 재질 색상이 셰이더 항상 고정이 되버리네요. 여러 색상을 원할 경우, 그 색상 수만큼 셰이더를 만들고 각 오브젝트에 적용해야 한다는 얘기가 되네요. 이건 매우 비효율적일 수 밖에 없군요.
따라서 우리는 색상값을 고정시키지 말고 외부(유니티 에디터)에서 그 값을 읽어와서 셰이더에서 사용할 수 있도록 하는 것이 올바른 방법인 듯 합니다.  이것을 위해서 유니티는 Properties 구문을 제공합니다. Properties 안에 셰이더 외부로부터 개발자에 의해 설정될 수 있는 파라미터들을 정의하게 됩니다. 
 그러면 우선 코드에 Properties 구문을 추가해보겠습니다:

Shader "VertexLit Simple" { 
    Properties { 
        _MyColor ("Main Color", COLOR) = (0,0,1,1
    } 
    SubShader { 
        Pass { 
            Material { 
                Diffuse [_MyColor] 
                Ambient [_MyColor] 
            } 
            Lighting On 
        } 
    } 

위의 코드를 보면 새롭게 Properties 블럭이 추가된 것을 보실 수 있습니다. 1개의 파라미터 변수가 추가되었군요.
이 부분만 살펴보자면:

_MyColor ("Main Color", COLOR) = (0,0,1,1)

 _MyColor는 속성 변수의 이름을 나타내고, "Main Color"는 에디터 상에서 보여질 라벨(이름)입니다. COLOR는 변수가 색상타입임을 의미하며, 에디터 상에서 디폴트 값은 파란색(0,0,1,1)으로 지정함을 의미합니다.

 이렇게 Properties 블럭이 추가됨에 따라서 에디터에는 어떤 변화가 일어났는지 한번 보겠습니다.

재질 인스펙터 창에 뭔가 한 줄 추가된 것이 보이네요. "Main Color"라는 색상을 설정할 수 있는 인터페이스가 생겼고, 디폴트 색으로 현재 파란색이 설정되었습니다. 앞서 Properties 블럭에 추가한 내용이 반영되었다는 것을 알 수 있습니다. 예상한대로 에디터의 "Main Color" 값을 변경하면,  셰이더의 _MyColor 값에 그 값이 적용됩니다.

 그럼 이제 이 값을 사용하려면, 셰이더에서는 어떻게 작성해야 할까요?
 셰이더 내부에서는 [ 프로퍼티 이름 ] 이라고 작성하여서 그 값을 사용할 수 있습니다.
 그냥 위 코드의 일부분을 보면 알 수 있습니다:

 Material { 
           Diffuse [_MyColor] 
           Ambient [_MyColor] 
          }

 재질의 Diffuse와 Ambient 색상을 (r,g,b,a) 에서 [_MyColor]로 수정하였군요. 즉,  _MyColor 값을 참조하여  색상값을 지정하겠다는 것이죠.
 자, 드디어 에디터로부터 언제든 원하는 색상으로 오브젝트의 색상을 지정할 수 있게 되었습니다.
 물론 여기에서 사용한 COLOR 타입 프로퍼티 외에도 다양한 타입의 프로퍼티들이 존재하며, 셰이더에서는 그것들을 사용할 수 있습니다. 나머지 프로퍼티 타입들은 앞으로 계속 등장할테니 그때마다 익히면 되겠습니다.


 이제 조금 더 욕심이 생기는군요. 이젠 단순히 재질의 색상을 정하는 것 만이 아니라, 이제 표면에 텍스처 이미지를 입혀보려 합니다. 이것은 ShaderLab의 SetTexture를 이용하면 됩니다. 
 그전에 먼저 우리는 사용할 텍스처를 에디터에서 설정할 필요가 있습니다. 그러기 위해서 방금 전에 배웠던 프로퍼티를 활용하면 됩니다. Properties 블럭 안에 다음과 같이 프로퍼티 변수를 하나 추가하겠습니다.

_MainTex ("Base Texture"2D) = "white" {}

 벌써 새로운 프로퍼티 타입이 등장했군요. 텍스처를 위한 2D 타입입니다. 디폴트 값으로 "white"가 지정되어 있는게 보입니다. 이것은 만약 개발자가 에디터를 통해 텍스처 이미지를 설정하지 않았을 때, 이 값을 어떻게 취급할 지를 결정합니다. "white"니까 흰 색상값으로 취급하겠군요. 이외에도 "black", "gray", "bump" 를 설정할 수 있으며, "" 도 가능합니다.
 { } 안에는 별도의 옵션이 들어갑니다. 이 옵션에 관해서는 이번에는 다루지 않고, 다음 번에 기회가 될 때 다루도록 하겠습니다.

에디터는 다음과 같이 변화가 생겼습니다:

Texture 프로퍼티가 추가된 것을 확인할 수 있군요. (텍스처는 임의로 맨홀 뚜껑 이미지를 세팅한 상태입니다.)

 그럼 이제 에디터에서 설정한 텍스처 이미지를 사용하기 위해서, 앞서 잠깐 언급된 SetTexture 라는 커맨드를 사용해보겠습니다. 한 가지 유의할 점은 우리가 텍스처를 적용하는 것은 색상을 결정하는 최종단계에서 이루어지므로, 코드의 마지막 부분에 들어가야 한다는 것입니다. 어쨋든:

SetTexture[_MainTex]

이렇게 추가하면, 우리가 에디터에서 설정한 _MainTex 를 가져와 적용하게 됩니다.

그럼 코드는:
Shader "VertexLit Simple" { 
    Properties { 
        _MyColor ("Main Color", COLOR) = (1,1,1,1
        _MainTex ("Base Texture"2D) = "white" {} 
    } 
    SubShader { 
        Pass { 
            Material { 
                Diffuse [_MyColor] 
                Ambient [_MyColor] 
            } 
            Lighting On 

            SetTexture [_MainTex]  
        } 
    } 

적용한 결과는:


그런데 뭔가 이상하게 보입니다. 라이팅 연산이 반영되지 않은 것도 같고...
아, 그러고보니 그냥 밋밋하게 최종 렌더링 색상으로 텍스처 이미지 색상 만이 적용되어 버렸네요.

Pass의 끝부분에 단순히 SetTexture [프로퍼티] 만 작성해놓았더니, 이전에 재질과 빛이 라이팅 연산을 통해 나온 결과 색상값은 그냥 무시해버리고, 현재 텍스처로 덮어씌워지는 현상이 발생했군요.

사실 우리는 텍스처를 설정하면서, 이전에 계산된 결과 색상값과 현재 텍스처가 어떻게 결합할 지를 결정해야 합니다. 그래야 적절히 믹스되어서 그럴듯한 색상으로 최종값이 결정되는 것이지요. 일단 다음과 같이 다시 코드를 추가해보겠습니다.

SetTexture [_MainTex] { Combine texture * primary } 

SetTexture 커맨드에 새로운 블럭을 추가되었습니다. 블럭 안에는 어떻게 색상을 결합할 지를 정하는 코드가 들어가며, Combine 커맨드로 설정하게 됩니다. texture는 현재 설정한 텍스처의 색상을 의미하고,  primary는 이전 라이팅연산되어 나온 색상을 의미합니다. 그러므로 이전 결과값과 현재 텍스처를 곱하겠다는 얘기로 해석되는군요. 
근데 여기서 조금 더 코드를 추가해볼까요.. 조명 강도를 좀 높이기 위해서 결과값을 두 배로 만들어주게끔 DOUBLE 을 추가합니다.DOUBLE은 결과값을 x2 해주는 키워드입니다.

SetTexture [_MainTex] { Combine texture * primary DOUBLE }


그럼 이제 최종 코드는:
Shader "VertexLit Simple" { 
    Properties { 
        _MyColor ("Main Color", COLOR) = (0,0,1,1
        _MainTex ("Base Texture"2D) = "white" {} 
    } 
    SubShader { 
        Pass { 
            Material { 
                Diffuse [_MyColor] 
                Ambient [_MyColor] 
            } 
            Lighting On 

            SetTexture [_MainTex] { 
                    Combine texture * primary DOUBLE 
            } 
        } 
    } 

실제 적용해본 결과입니다:


자, 이번에는 여기까지입니다.
다음 시간에는 더더욱 그럴 듯한 셰이더를 작성해보도록 하겠습니다.
감사합니다~


'프로그래밍 > Unity' 카테고리의 다른 글

유니티 셰이더 강좌 #5  (0) 2014.08.15
유니티 셰이더 강좌 #4  (0) 2014.08.15
유니티 셰이더 강좌 #2  (0) 2014.08.15
유니티 셰이더 강좌 #1  (0) 2014.08.15
Unity 2D 스프라이트 - 버그 및 유의사항  (0) 2014.03.25

출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88


---


안녕하세요, 유니티 셰이더의 기초 두 번째 시작하겠습니다. 
이전 글의 마지막에 소개된 셰이더 코드부터 다시 한번 살펴보겠습니다.

Shader "Solid White" { 
    SubShader { 
        Pass {  
            Color (1,1,1,1)  
        } 
    } 
}

음, 오브젝트를 흰색으로 렌더링하는 셰이더였군요(사실 틀렸습니다. 이전 글에서는 오브젝트를 빨간색으로 렌더링하는 셰이더였죠;). 이 셰이더를 유니티에서 제공하는 구(sphere)에 적용해보도록 하겠습니다:

 구(sphere)가 그냥 흰색 동그라미가 그려졌습니다. 입체감도 없고 딱히 특별한 감흥도 없으며, 이게 도대체 뭔가 싶습니다. 

 사실 우리가 원하는 건 월드에 존재하는 빛과 물체 표면과 반응하여 뭔가 그럴 듯하게 물체가 보여지는 것입니다.
 그럼 이제부터는 빛과 오브젝트 표면이 반응하여 그려지는 약간은 그럴 듯한 셰이더를 제작해봐야 겠군요. 먼저, 씬(scene) 상에는 이미 directional light 1개가 존재하고 있다고 가정하겠습니다. 
 우리가 셰이더 코드에서 해야 할 것은 딱 두 가지입니다. 
 바로 재질(material)과 라이팅(lighting)를 설정하는 것입니다.

 먼저 물체 표면의 재질(material)을 정의해 보도록 합니다. 
 재질은 다음과 같이 ShaderLab의 Material 구문을 이용하여 정의할 수 있습니다.

Material { 
      Diffuse (1,1,1,1
      Ambient (1,1,1,1
}

 음, 매우 간단합니다. 재질의 Diffuse 색상과 Ambient 색상 모두 흰색으로 지정하라는 구문입니다.

 이제 물체의 재질은 정의했으니, 이제 월드에 존재하는 빛(여기서는 directional light)과 방금 정의한 재질이 '어떻게' 반응할 지를 설정해줘야 합니다. 즉, 조명에 대하여 어떻게 연산할 지를 설정해줘야 한다는 얘기입니다.  
 결론적으로 말하면, 우리는 라이팅 연산을 위해서 '고정 기능 파이프 라인'을 이용할 것입니다. 이는 개발자가 GPU에서 이미 정해져 있는 연산(기능)을 호출하여 오브젝트 렌더링을 수행하는 것을 의미합니다. 그러니까 조명에 대한 연산은 우리가 따로 별도의 신경쓰지 않고, GPU 레벨에서 이미 만들어져 있는 연산을 가져다 쓰기만 하면 되는 것입니다. 
그렇다면 우리가 해야 하는 일이 무엇일까요? 그냥 고정 기능 파이프 라인을 통해 라이팅 연산을 하겠다고 셰이더에 명시하는 작업을 하면 됩니다. 그 외에는 다른 어떤 작업도 필요치 않습니다.
 그냥 ShaderLab의 Lighting (On|Off) 커맨드를 이용하여 라이팅를 켜주면(On) 끝입니다:

 Lighting On

이 코드 한줄만 작성하면 우리는 해당 셰이더에서 고정 기능 파이프라인의 표준 라이팅을 적용한다는 의미입니다. 
최종 정리하면 세이더 코드는:

Shader "VertexLit White" { 
    SubShader { 
        Pass { 
            Material { 
                Diffuse (1,1,1,1
                Ambient (1,1,1,1
            } 
            Lighting On 
        } 
    } 
}

위와 같이 완성할 수 있습니다. 
실제 적용된 화면을 보면 아래와 같습니다.

        
       
 정말 단일 색상으로 물체를 그렸을 때와는 매우 느낌이 다릅니다. 무엇보다도 입체감이 느껴지는게 아주 좋군요. 월드에 있는 directional light 의 방향을 변화시켰을 때, 표면이 다르게 그려지는 걸 보니 잘 동작하는 것 같습니다.

 자, 지금까지 우리는 ShaderLab 만을 이용하여 재질을 정의하고 여기 라이팅을 적용하여 물체를 그리는 셰이더 작성을 완료했습니다. 이것을 다시 말하자면, 방금 우리는 유니티에서 셰이더를 작성하는 방법 중 하나인 Fixed Function Shader를 얼떨결에 작성하게 된 것입니다. 
 Fixed Function Shader 는 고정 기능 파이프라인을 이용하며, 지금과 같이 ShaderLab 만으로 작성이 가능한 것이 특징입니다. 그래서 아주 간단하고 작성이 쉽습니다.  이 Fixed Function Shader에 대해서는 다음 시간에 좀 더 자세히 이야기하는 시간을 가져보도록 하겠습니다.

 감사합니다.


출처 : http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-1


---


안녕하세요, 흑기사입니다.
다들 유니티로 게임 만드시느라 밤낮없이 매우 바쁘실텐데요.. 
너무 바쁜 나머지, 아직 유니티 셰이더 쪽은 살펴보지 못하신 분들께 조금이나마 도움이 되었으면 하는 마음으로..
유니티 셰이더에 대한 '기초'적인 내용들에 대해서 한번 정리해보았습니다. 
대부분의 내용은 유니티 메뉴얼을 참조해서 작성했음을 미리 말씀드립니다. 

 그럼 시작하겠습니다.

 유니티 셰이더에서 가장 먼저 알아야 할 것은 ShaderLab 입니다.
ShaderLab는 한마디로 유니티의 셰이더 스크립트 언어입니다. 보통 셰이더 언어라고 하면 Cg, HLSL, GLSL 에 더욱 익숙하실 텐데요. 유니티에서는 우선 기본적으로 ShaderLab을 기반으로 작성해야 합니다. 그럼 왜 하필 유니티에서는 ShaderLab이라는 것을 만들어 사용하고 있는 걸까요? (배워야 할 언어가 늘어날 수록 개발자에게 짜증을 유발합니다..)
 그건 유니티가 멀티플랫폼 엔진이니만큼 다양한 플랫폼, 다양한 디바이스들에 대응해야 하기 때문입니다. 어떤 특정 환경이나 상황에서도 초연하게 대응할 수 있는 공통의 인터페이스가 요구되므로, 이러한 셰이더 스크립트 언어를 사용하는 것입니다.  
 ShaderLab 문법은 처음에 확실하게 알고 가는 게 좋습니다. 유니티에서 셰이더를 작성하려면 어쨋든 ShaderLab의 영역은 벗어날 수가 없기 때문입니다. 

그럼 유니티 셰이더 파일의 기본 구조와 함께, 대표적인 ShaderLab 구문에 대해서 살펴보겠습니다.

Shader "MyCustomShader" { 
     SubShader { 
          // ...  body  ... // 
     } 
 위의 코드는 유니티 셰이더 파일의 가장 간단한 구조입니다. 하나의 Shader 블럭이 있고, 그 안에 SubShader가 있는 형태로 구성됩니다. "MyCustomShader"는 해당 셰이더의 이름입니다. 그리고 SubShader내의 body 라고 표기된 부분이 있는데, 여기에 실제 셰이더 코드(구현부)가 위치하게 됩니다. 즉, 우리는 어떤 셰이더를 작성하고 싶다면, 그 방법이 어떤 형태든지 간에 저 body 부분에 실제 코드를 작성하게 되는 겁니다. 
 여기서 한가지 의문이 들 수 있습니다. 그럼 그냥 간단하게 Shader{} 안에 body에 해당하는 셰이더 코드를 작성하면 될 것을, 왜 하필 귀찮게 또 SubShader{}라는 것을 만들고 거기에 코드를 작성해야 할까요?
 사실 Shader 블럭은 위의 예제처럼 하나의 SubShader 만이 아니라 여러 개의 SubShader로 구성될 수 있습니다. 아래와 같은 형태로 말입니다:

Shader "MyCustomShader" { 
     SubShader { 
          // ...  body  ... // 
     } 
     SubShader { 
          // ...  body  ... // 
     }       
     SubShader { 
          // ...  body  ... // 
     }

// ...other subshaders//
}
 이렇게 말이죠...
 결국 하나의 셰이더에서 실제 구현은 n개로 구성할 수 있다는 얘기입니다. 이렇게 여러 개의 구현을 할 수 있게 끔 구성한 이유는 그래픽 하드웨어(성능)의 다양성 때문입니다. 그래픽 하드웨어는 매우 다양하고, 그 성능도 제각각입니다. 그렇기 때문에 어떤 셰이더가 특정 하드웨어에서는 지원되지 않아 동작하지 않을 수 있습니다. 이런 상황을 막고 어떤 그래픽 하드웨어 환경이든 상관없이 동작하게 하기 위해서 여러 개로 다양한 구현을 할 수 있게 끔 인터페이스를 제공하는 것입니다. 어떤 셰이더에 대한 렌더링이 이루어 질 때, 유니티는 그 SubShader들의 리스트를 살펴본 뒤에 그 중에 현재 하드웨어가 지원 가능한 것들 중 가장 첫 번째 것을 사용하게 됩니다.

 코드에서 짚어보자면:
Shader "MyCustomShader" {  
      SubShader{  
          // A급 디바이스용 (최상의 품질로 구현) 
     }  
      SubShader{  
          // B급 디바이스용  
     }  
      SubShader{  
          // C급 디바이스용  
     }  
      SubShader{  
          // 그 밖의 초저사양 디바이스들 (매우 후진 품질로 구현)  
     }  

     Fallback "Diffuse"  
}
 대략 이런 식의 의도를 가지고 셰이더를 작성하게 되는 것입니다.

 그런데 바로 위의 예제 코드를 다시 잘 살펴보면, 맨 아래쪽에 Fallback이라는 새로운 커맨드가 슬쩍 추가된 것을 볼 수 있습니다. 이 Fallback은 무슨 역할을 할까요? 
 만약 현재 셰이더의 모든 SubShader들이 현재 사용자의 그래픽 하드웨어에 맞지 않아 동작하지 않는 경우에 유니티 엔진은 Fallback을 통해서 지정된 이름의 셰이더를 찾아, 그 셰이더에서 현재 디바이스에서 동작 가능한 SubShader를 다시 한번 찾게 됩니다. 어찌보면 후보선수 같은 개념입니다. 원래 셰이더가 맞지 않을 때, 다른 대안 셰이더를 지정해 놓은 것이죠. 이런 식으로 각 셰이더들이 fallback셰이더들을 지정해 놓는다면, 최소한 아예 렌더링이 되지 않거나 하는 경우는 방지하게 되며 따라서 낮은 품질이라도 렌더링 자체는 보장할 수 있게 됩니다. 
 위의 예제에서는 "Diffuse"라는 이름의 셰이더를 Fallback 셰이더로 지정했습니다. 그래서 유니티가 "MyCustomShader"에서 적당한 SubShader를 찾지 못한 경우, "Diffuse"셰이더에서 SubShader를 찾는 작업을 반복하게 되는 것입니다.

 마지막으로 Pass구문에 대해서 알아보겠습니다. 이 Pass는 흔히 우리가 셰이더에서 익히 알고 있는 그 Pass와 동일합니다. 즉, 오브젝트의 렌더링의 단위를 의미하며, SubShader 안의 Pass의 수만큼 오브젝트가 렌더링됩니다. 그러므로 유니티에서는 Pass 구문을 통해서 경우에 따라서는 셰이더를 멀티 패스로 구현할 수 있습니다.

다시 정리해보면 일반적인 셰이더 구성은:
Shader "MyCustomShader" { 
     SubShader{ 
          Pass{ //  ...  // } 
          Pass{ //  ...  // } 
     } 

     SubShader{ 
          Pass{ //  ...  // } 
     } 

     //  ...other subshaders  // 

     Fallback "OtherShader" 
}
 대략 dl 정도가 간단한 유니티 셰이더의 기본 형태가 되겠습니다. 
(사실 나중에 소개될 서피스 셰이더(surface shader)의 경우엔 Pass 구문은 필요하지 않지만, 우선 저렇게 알고 있는 게 편함.)


이제 마지막으로 실제로 화면에 뭔가를 보여주는 초간단한 셰이더 코드 하나 작성해보겠습니다.
Shader "Solid Red" { 
    SubShader { 
        Pass {  
            Color (1,0,0,1)  
        } 
    } 
 굉장히 심플합니다. "Solid Red" 라는 이름을 가진 셰이더이군요. 코드에는 특별한 것은 없고, Color라는 새로운 커맨드가 등장했습니다. Color 커맨드는 오브젝트에 단일 RGBA색상을 지정해주는 커맨드입니다. 따라서 위의 셰이더는 조건없이 그냥 오브젝트 전체를 그냥 빨간색으로 렌더링합니다. 하지만 뭔가 보여주는 셰이더치고는 너무 단순하고 쓸모없어 보이는군요.
 
다음 시간에는 ShaderLab을 이용하여, 약간 더 그럴듯한 셰이더를 제작하는 방법에 대해 알아보겠습니다.
감사합니다.



* Animation 윈도우(타임라인)에서 회전값을 변화시킬 때, 위쪽방향 중심(0도)을 기준으로 값이 커지면 그만큼 왼쪽으로 회전하게 된다. 오른쪽으로 회전시키고 싶을 경우 - 값을 입력하는데, 값 입력 후 엔터를 치면 - 값이 아니라 왼쪽 값으로 계산해버리는 경우가 있다. 


 즉 위 그림처럼 오른쪽의 -25도로 돌리고 싶어서 -25를 입력하면 335도가 되어버려, 0도 -> -25도로 오른쪽으로 살짝 도는 것이 아니라 0도 -> 335도로 왼쪽으로 크게 돌아가 버리는 현상이다. 이건 커브를 조절해 맞추던지(더럽게 귀찮다), 값 입력시 엔터를 치지 말고 TAB 키로 완료하면 제대로 입력되는 듯.


* 타임라인의 Dope Sheet에서 지정한 키프레임의 키값은 복사가 될때가 있고 안될때가 있다. 안될때는 커브로 하거나 숫자입력으로 값을 복사해 붙여넣기를 해줘야 한다.


* 스프라이트 에디터에서 실제 이미지 하나에 모듈을 여러개 지정했을 경우(같은 그림을 각각 다른 모듈로 여러번 사용하는 경우), 나중에 수정 등을 위해 뒤쪽에 가려져 있는 모듈을 선택하기가 매우 곤란하다. Project 폴더의 텍스처에서 해당 그림을 선택해도 여전히 앞에 가려진 모듈밖에 나오지 않는다. 드럽게 짜증난다. 이걸 해결하려면 앞에 가려진 모듈을 잠시 치워놓고 작업하던지 해야 하는데, 치워놓을때 모듈이 완전히 여백(투명한 부분)에 자리잡지 않게 해야 한다. 모듈이 완전히 빈 곳으로 간 상태에서 다른 창을 보거나 열거나 다른 프로그램을 돌리거나 했을 경우, Apply 버튼을 누르지 않았다면 빈 곳에 치워놓은 모듈은 없어져 버린다. 즉 이걸 해결하려면 모듈을 완전한 여백이 아닌 다른 그림에 걸쳐놓던지 해야 한다.


* 매터리얼 생성시, 원하는 텍스처를 적용하려고 해도 Inspector 창에 Shader 부분까지만 나오고 아무리 해도 세부 파라미터 부분이 안 나오는 경우가 있다. 이때는 텍스처 파일을 끌어다가 Shader 아래의 빈 곳에 가져가면 파라미터 영역이 갑자기 생겨난다 -_-;



차후 또 발견시 업데이트 예정

* 텍스처 관련


 1. 아래의 Project 창에 보이는 Assets 폴더는 실제 하드디스크 상에 있는 프로젝트 폴더(디폴트값으로 저장했다면 New Unity Project 뭐시기 폴더)에 있는 Assets 폴더이다. 




     즉 Unity 상에서 이 Assets 폴더안의 파일을 지우거나 하면 실제 프로젝트 폴더 안에서도 지워져버린다. 주의.


 2. 새로 스프라이트를 만들기 위해 이미지 리소스(텍스처)를 만들었으면 이 Assets의 Textures 폴더 안에 넣어주기만 하면 바로 인식된다.



* 파츠 자르기


 1. 텍스처가 불러와진 상태에서 메뉴 > Window > Sprite Editor 를 선택해 스프라이트 에디터를 띄운다.





   그리고 아래의 Assets의 Textures 폴더에서 원하는 텍스처를 선택해 준다. 



(옆에 붙은 ▷ 아이콘을 클릭하면 그 텍스처에 설정된 파츠들이 보이게 되는데 현재는 자르기를 하지 않았으므로 텍스처 전체가 통짜로 나오게 된다.)


 2. 스프라이트 에디터 창에 리소스 이미지가 뜬다.



 3. Inspector 창(안 보일 경우 메뉴 > Window > Inspector 선택해서 띄운다)에서 Sprite Mode 를 Multiple 로 바꾸고 오른쪽 아래의 Apply 버튼을 누른다.



 4. 스프라이트 에디터 창의 Slice 버튼을 누르고, 나오는 메뉴 아래의 Slice 버튼을 클릭한다.



 5. 파츠들이 알아서 적당하게 잘라진다.



 6. 잘라진 모양을 확인한 뒤 오른쪽 위의 Apply 버튼을 누른다.


 7. 이제 다시 아래의 Textures에 선택된 텍스처 옆에 붙은 ▷ 아이콘을 클릭하면 이번엔 잘라진 파츠들이 보일 것이다.




 8. 만약 스프라이트가 파츠를 조합하는 2D 스켈레톤 방식이 아니라 통짜 이미지를 바꿔가며 보여주는 방식의 스프라이트라면, 위 7번 단계에서 잘라진 스프라이트들을 Shift + 클릭으로 복수 선택한 후 Scene 창으로 바로 끌어다 놓으면 한방에 *.anim 파일을 만들면서 스프라이트가 생성된다. Sprite Editor 창에서 기준점(Pivot) 설정도 해두었다면 이미지의 위치도 중심점에 맞추어서 자동 정렬되어 생성되므로 편리하다. 다만 이미지 1장으로 이뤄진 스프라이트를 만들 때는 1장만 끌어다 놓으면 *.anim 파일을 만드는 부분이 나오지 않으므로, 사용자가 직접 Animation 창에서 [Create New Clip]을 선택해 *.anim 파일을 만들어 주어야 한다. 



* 스프라이트 생성


 1. Hierarchy 창에서 Create 버튼을 클릭하고 Sprite 를 선택한다. 



     아래에 New Sprite 라는 이름의 새 스프라이트가 만들어질 것이다. 이 이름을 클릭하고 F2를 누르면 이름을 변경할 수 있다.


 2. 아까 자른 파츠 중 원하는 파츠를 드래그해 Scene 창에 갖다놓는다. 그러면 파츠가 삽입될 것이다.



 2. 들어간 파츠를 Hierarchy 창에서 클릭해주면 Inspector 창에 Sprite Renderer 항목이 나타나는데, 여기에서 Color 옆의 색깔 창을 클릭해주면 색상 관련 설정창이 뜬다. 아래의 RGBA 값 중 A값은 알파값으로 투명도를 나타낸다. 파츠의 투명도를 조절해야 할 경우는 여기에서 조정해 주면 된다. 참고로 Hierarchy 창에서 원하는 파츠들을 여러개 복수 선택한 뒤 같은 방식으로 한번에 투명도를 일괄 조정할 수도 있다. 다만 이것은 포토샵의 레이어 그룹처럼 모아진 종속관계를 그대로 유지하면서 투명도가 조절되는 것이 아니고 그냥 각각의 투명도를 한번에 조절해주는 것 뿐이므로, 서로 겹쳐진 파츠들은 투명해지면서 가려졌던 윤곽이 드러나 보이게 된다. 주의.

 

 3. 파츠의 기준점(Anchor Point)는 Sprite Editor 창에서 원하는 파츠를 선택한 뒤, 오른쪽 아래의 서브윈도우에 있는 Pivot을 눌러 설정한다. 



    중앙으로 맞추고 싶으면 Center인 채로 두면 되고, 다르게 설정하고 싶을 경우 Custom을 선택해 직접 파츠 내에서 원하는 위치에 설정한다. 설정한 뒤에는 오른쪽 위의 Apply 버튼을 누르는 것을 잊지 말것!


 4. 파츠는 Hierarchy 창에 그냥 마구잡이로 들어가게 되므로, 들어간 뒤에는 아까 만든 새 스프라이트에 끌어서 집어넣어 준다. 



    파츠는 다른 파츠의 아래에 들어갈 수 있는데 이것은 종속관계(일종의 본)을 설정하게 된다. 이렇게 다른 파츠 아래에 들어가는 것을 제외하고는 여기에서의 상하 위치는 실제 스프라이트의 앞뒤 가려짐 순서와 관계가 없다. 



    앞뒤 가려짐 순서는 원하는 파츠를 선택한 뒤 Inspector 창에서 Sprite Renderer > Order in Layer 에 숫자값을 입력해서 설정한다.


 5. Scene 창에 놓인 파츠를 드래그해 조합하고 앞뒤 순서를 맞추면서 스프라이트를 만들어나간다.



* 애니메이션


 1. 애니메이션 윈도우를 띄운다(메뉴 > Window > Animation. Animator가 아님에 주의!)


 2. Hierarchy 창에서 원하는 스프라이트를 선택한다. 어떤 스프라이트의 어떤 파츠를 선택하느냐에 따라 만들어지는 애니메이션이 적용되는 부분이 다르므로 잘 선택하고 만들어야 한다.


 3. 먼저 Project 창에서 Animations 폴더에 스프라이트를 저장할 폴더를 만들고 지정한다. 이 폴더를 제대로 지정해두지 않으면 애니메이션 동작이 제대로 저장되지 않거나 하므로 반드시 폴더를 잘 지정해둘 것.


 4. 녹화버튼과 재생버튼 아래의 빈 부분을 클릭하면 Create New Clip 이라는 것이 나온다. 



    이것을 선택하면 하나의 큰 동작(대기상태, 걷기, 달리기, 공격 등등)을 만들 수 있다. 


 5. 동작을 만들면 빈 부분에 만든 동작의 이름이 나와서 해당 동작을 편집중이라는 것을 알려주며, 위에서 지정한 폴더 안에 동작 파일(*.anim)이 저장된다. 아래의 Add Curve 버튼을 누른다.


 6. 누르면 Transform, Sprite Renderer, Animator 등의 3가지 조정항목과, 아까 생성한 스프라이트의 파츠 중 종속관계가 가장 위인 파츠들의 목록이 보인다. 여기서 움직이고 싶은 파츠를 선택해, 왼쪽의 ▶ 아이콘을 누르면 하부 메뉴가 펼쳐진다. 

 


 예를 들어 팔과 손에 쥔 무기 파츠가 있다고 할 때, 팔을 움직이면 무기도 움직이는 것은 당연하므로 무기 파츠는 팔 파츠의 자식 파츠로 설정되는 것이 보통이다. 이때 팔을 움직이고 싶다면, 팔 파츠의 하부 메뉴에서 Transform의 Position 옆에 있는 + 아이콘을 눌러준다. 팔 파츠의 위치를 움직이는 타임라인이 생성된다.


 7. 추가된 타임라인을 선택한 뒤 타임라인 바의 시간이 표시되는 영역에서 원하는 적당한 길이의 위치에 클릭을 해 주면 빨간 세로선이 생긴다. 이 상태에서 왼쪽 위의 녹화 버튼이 붉게 표시되는 걸 볼 수 있는데 이것은 해당 타임라인이 편집 중이라는 의미이다. 세로선이 생긴 상태에서 ◇+ 아이콘을 클릭하면 세로선이 위치한 자리에 키프레임이 추가된다. 


 8. 키프레임을 선택한 상태에서, Scene 창의 파츠를 원하는대로 수정해준다(위치변경, 회전, 크기변경 등등등). 


 9. 녹화버튼을 눌러서 편집을 마치고, 옆의 재생(▶) 버튼을 누르면 파츠가 부드럽게 움직일 것이다.


 10. 파츠의 변형중 회전은 0~360도의 각도값을 이용해 움직이게 되는데, 타임라인의 키프레임 모드(Dope Sheet)에서는 버그가 있어 - 값을 인식하지 못한다. 



    이것은 일단 키프레임을 설정해 움직임을 만든 뒤, Animation 창의 아래쪽에 있는 Curves 버튼을 눌러 보간 패턴을 - 값으로 계산해 적용시켜주면 된다. 0도에서 오른쪽으로 5도 더 돌아가므로 -5 정도로 낮추어 준다. 이 때도 옆의 수치값을 보면 수직방향(0도 = 360도)에서 오른쪽으로 5도 회전시키는 값이 360도에서 5도 빠진 355도로 적용되어버리고 있다. 회전의 보간에는 주의하자. 더 간단한 해결방법은 수치값을 입력한 뒤 엔터를 치지 말고 tab 키를 눌러 입력을 종료하는 것. 


  11. Hierarchy 창에 만들어진 스프라이트의 트리에서 원하는 부분을 선택하면, 보통은 Scene 창에 해당 부분의 핸들이 나와서 편리하게 보면서 조작과 변형을 할 수 있다. 그런데 가끔 해당 부분을 선택했는데도 Scene 창에 핸들이나 조절바가 전혀 안나올 때가 있다. 이 때는 선택이 된 상태에서 Inspector 창을 확인하자. 아마 Animator가 추가되어 있을 텐데, 이 Animator를 제거해주면 정상적으로 핸들이 나오고 조절이 가능해진다.



* 스프라이트의 동작관계설정 및 Prefab 저장


 1. Scene 창 옆에 있는 Animator 탭을 클릭하면 각 동작들 간의 관계설정이 가능하다. 


 2. 완전히 스프라이트를 저장하기 위해서는 몇 가지 귀찮은 작업이 필요한데 가장 중요한 것은 Prefab을 저장하는 것이다. Project 창의 Assets에 있는 Prefabs 폴더에 우클릭 후 Create > Prefab을 설정하면 아무것도 들어있지 않은 새 Prefab 파일이 만들어진다. Hierarchy 창에 만들어진 스프라이트의 최상위 폴더(해당 스프라이트)를 드래그해 방금 만든 빈 Prefab 파일 안에 넣으면 스프라이트가 제대로 보존된다.



* 만든 스프라이트를 저장할 때


 1. 메인 메뉴에 있는 Save Scene 이나 Save Project 는 캐릭터 스프라이트만을 별도로 저장하지는 않는다. 캐릭터 스프라이트만을 별도로 저장하고 싶다면 메뉴 > Assets > Export Package 를 선택해서 스프라이트만을 따로 저장해야 한다.


 2. Project 창에서 Animations 폴더에 스프라이트를 저장할 폴더를 만들고 지정한다. 



* 만들어진 스프라이트를 재생하고 확인해볼 때


 1. 메뉴 > Assets > Import Package > Custom Package 로 원하는 스프라이트를 불러온다


 2. 아래의 Assets 에서 Prefabs에 등록된 스프라이트를 메인 윈도우의 Scene으로 끌어다 놓는다. 스프라이트가 보이게 된다


 3. 메뉴 > Window > Animation 을 선택해 애니메이션 타임라인창을 띄운다


 4. 타임라인창의 왼쪽 위 플레이(▶) 버튼을 클릭하면 애니메이션이 재생된다. 한번 더 클릭하면 멈춘다.







+ Recent posts