Generic Singleton 패턴 (유니티) 소스코드 입니다.

 - 유니티 app이 종료될 때, 무작위 순서로 오브젝트들이 destroy 됩니다. 
   대체적으로 싱글턴 객체는 app이 종료될 때에만 destroy 되게끔 코딩하는데, 
   OnApplicationQuit에서 싱글턴 객체가 이미 destroy 된 이후에 다른 스크립트에서의 싱글턴 객체 참조를 막기 위해 appIsQuitting 변수를 추가했고
   get부분의 안정성을 위해 lock 키워드로 감싸주었습니다.

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
// Singleton<T>.cs
using UnityEngine;
using System.Collections;
 
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _singleton;
 
    private static object _lock = new object();
 
    private static bool appIsQuitting = false;
 
    public static T singleton
    {
        get
        {
            if (appIsQuitting)
            {
                Debug.LogError("[Singleton<" + typeof(T).ToString() + ">] : " +
                                "already destroyed on application quit");
 
                return null;
            }
 
            lock (_lock)
            {
                if (_singleton == null)
                {
                    _singleton = FindObjectOfType<T>();
 
                    if (FindObjectsOfType(typeof(T)).Length > 1)
                    {
                        Debug.LogError("[Singleton<" + typeof(T).ToString() + ">] : " +
                                        "singleton instance is duplicated");
 
                        return _singleton;
                    }
 
                    if (_singleton == null)
                    {
                        GameObject go = new GameObject();
                        _singleton = go.AddComponent<T>();
                        _singleton.name = typeof(T).ToString();
 
                        DontDestroyOnLoad(_singleton);
                    }
                }
 
                return _singleton;
            }
        }
    }
 
    public virtual void OnApplicationQuit ()
    {
        appIsQuitting = true;
 
        _singleton = null;
    }
 
}
 
 
cs

Singleton 클래스를 'T myClass = new T();' 와 같이 생성자 호출을 통해 생성하는 것을 막으려면
아래처럼 생성자에 protected 지시자를 붙여 주세요.

1
2
3
4
5
6
7
8
// Example - GameManager.cs
public class GameManager : Singleton<GameManager> 
{
    protected GameManager ()
    {
        // guarentee this object will be always a singleton only - can not use the constructor
    }
}
cs

사용 예시

1
2
3
4
5
6
7
8
9
10
11
// Example - Main.cs
using UnityEngine;
using System.Collections;
 
public class Main : MonoBehaviour 
{
    void Awake ()
    {
        Debug.Log(GameManager.singleton.GetType().ToString());
    }
}
cs



'02.Development > Design Pattern' 카테고리의 다른 글

[DesignPattern] Observer Pattern (C#)  (0) 2016.03.15


JNI (Java Native Interface) 란?






기본 개념

- JNI는 Java에서 native영역(C/C++)으로 들어가 호출 또는 native(C/C++)에서 Java로 호출하는 interface.

- Java와 Java 이외의 언어로 만들어진 어플리케이션이나 라이브러리가 상호작용할 수 있도록 연결시켜주는 인터페이스.

- JVM(Java Virtual Machine)이 네이티브 메소드(Native Method)를 적재(locate)하고 수행(invoke)할 수 있도록 함.

- JNI가 JVM 내에 포함됨으로써 JVM이 호스트 운영체제상의 입출력, 그래픽스, 네트워킹, 그리고 스레드와 같은 기능들을 작동하기 위한 

로컬시스템호출(local system calls)을 수행할 수 있도록 함.


* 쉽게 말해, JNI는 Java와 다른 언어를 연동하는 솔루션이다. (C/C++로 작성한 API를 Java에서 호출하게 해줌)

* 네이티브 메소드(Native Method)란 자바에서 특화된 메소드로써, 다른 언어로 구현되어 있으며 native 라는지시자(예약어)를 가지는 메소드를 말함.

* NDK(Native Development Kit)는 Java의 JNI 기술을 이용하여 안드로이드 App(Java)과 안드로이드 Library(C/C++) 계층을 연결해주는 역할을 함.


참조 : http://viiiin.tistory.com/11


네이티브 메소드를 이용하는 몇 가지 공통적인 이유

- 이미 다른 언어(c/c++)로 상당히 크고 중요한 코드를 작성했고, 자바로 동일한 코드를 재작성하기를 원치 않으며, 단지 기존 코드를 재사용하고 싶을 때 JNI를 사용한다.

- 시스템 디바이스에 접근할 필요가 있거나 자바의 성능을 넘어선 플랫폼 특정적인 작업을 수행할 경우에 JNI를 사용한다.

- 자바로 구현하기엔 어플리케이션이 매우 느릴 수 있으며, C++에서 time-critical 코드를 구현함으로써 성능의 향상을 가져올 수 있을 경우에 JNI를 사용한다.



JNI의 부정적인 측면

- 이식성(Portability)을 잃게 된다. 많은 자바 라이브러리가 네이티브 메소드를 사용하고 있는데, 새로 추가된 네이티브 메소드가 모든 플랫폼에서 호출 가능한 동일한 C++코드를 가진다면, 그 코드는 자바에서 가장 먼저 구현되어질 수 있다.

- 안정성(safety)을 잃게 된다. 네이티브 메소드는 자바 메소드처럼 동일한 보호를 보장받지 못한다. 일단 C++코드에 들어가게 되면, 자바에서의 모든 것이 백지화되고 메모리 추적, 포인터의 사용, 그리고 배열 바운딩에 의한 C++ 버그들이 발생할 수 있다.

- 네이티브 메소드의 구현은 .dll 및 .so 와 같은 동적 라이브러리들 내에서 이루어진다. 네이티브 메소드를 사용하는 자바 코드는 반드시 동적 라이브러리들을 호출하는데, 이는 자바 시큐리티 매니저에 반대되는 연산이다.

- 타이핑 오류와 같은 컴파일 오류에 종종 직면하게 되므로 코드 자체가 방해물이 될 수 있다. 



JNI를 사용하는 기본 절차

- native 예약어를 표시함으로써 자바로 구현되지 않는 특정 메소드들을 가지는 자바 클래스를 선언한다.

- JNI 프로토콜을 사용하여 네이티브 메소드를 구현하는 C/C++ 함수를 작성한다.

- C/C++ 함수를 컴파일하고 동적 라이브러리를 삽입한다.

- JVM이 동적 라이브러리를 호출한다. 네이티브 메소드에 대한 호출은 동적 라이브러리 내에서 구현된 호출에 의해 다루어진다.



디바이스의 시스템 언어 정보를 가져오는 간단한 방법!

Application.systemLanguage



다국어 지원(Localization) 할 때 유용하게 사용하세요!


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
    public enum SystemLanguage
    {
        Afrikaans = 0,
        Arabic = 1,
        Basque = 2,
        Belarusian = 3,
        Bulgarian = 4,
        Catalan = 5,
        Chinese = 6,
        Czech = 7,
        Danish = 8,
        Dutch = 9,
        English = 10,
        Estonian = 11,
        Faroese = 12,
        Finnish = 13,
        French = 14,
        German = 15,
        Greek = 16,
        Hebrew = 17,
        Hungarian = 18,
        Hugarian = 18,
        Icelandic = 19,
        Indonesian = 20,
        Italian = 21,
        Japanese = 22,
        Korean = 23,
        Latvian = 24,
        Lithuanian = 25,
        Norwegian = 26,
        Polish = 27,
        Portuguese = 28,
        Romanian = 29,
        Russian = 30,
        SerboCroatian = 31,
        Slovak = 32,
        Slovenian = 33,
        Spanish = 34,
        Swedish = 35,
        Thai = 36,
        Turkish = 37,
        Ukrainian = 38,
        Vietnamese = 39,
        Unknown = 40,
    }
    
 
    // 예시
 
    SystemLanguage sl = Application.systemLanguage;
    
    switch (sl)
    {
        // to do ...
    }
    
    // 또는
        
    switch (sl.ToString())
    {
        // to do ...
    }
cs


돈 표기처럼, 
숫자 문자열에 컴마를 찍어서 출력하는 방법은 정말 간단합니다. 

ToString("n0") 


하세요!


1
2
3
4
5
6
7
8
9
10
public static string InsertComma (int n)
{
    return n.ToString("n0");
}
 
int n = 1234567;
 
Console.WriteLine(string.Format("{0}", InsertComma(n)));
 
// 출력결과 : 1,234,567
cs



'02.Development > C#' 카테고리의 다른 글

[C#] Convert C# to Java, Java to C# - UTC  (0) 2016.07.07
[C#] List, Array 셔플  (0) 2016.02.04


ObjectPoolSample.unitypackage


모바일 프로젝트의 필수요소인 오브젝트 풀,

구글링하여 찾은 C# 소스를 기반으로 유니티에서 재사용 가능하도록 제작해보았습니다.

이를 활용한, 총에서 불렛을 발사하는 예제도 함께 첨부했습니다.


( ObjectPool<T>.cs, PoolableObject.cs, ObjectPoolManager.cs, Gun.cs, Bullet.cs )



풀링할 객체는 모두 PoolableObject 클래스를 상속받아서 사용하시면 됩니다.


ObjectPool<T>.cs의

 - objStack : 비활성화 된 object들을 담고있는 Stack (풀)

 - objList : 활성화 된 object들을 담고있는 List (참조용)


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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// ObjectPool<T>.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class ObjectPool<T> where T : PoolableObject
{
    private int allocateCount;
 
    public delegate T Initializer ();
    private Initializer initializer;
 
    private Stack<T> objStack;
    public List<T> objList;
 
    public ObjectPool ()
    {
        // default constructor
    }
 
 
    public ObjectPool (int ac, Initializer fn)
    {
        this.allocateCount = ac;
        this.initializer = fn;
        this.objStack = new Stack<T>();
        this.objList = new List<T>();
    }
 
    public void Allocate ()
    {
        for (int index = 0; index < this.allocateCount; ++index)
        {
            this.objStack.Push(this.initializer());
        }
    }
 
    public T PopObject ()
    {
        if (this.objStack.Count <= 0)
        {
            Allocate();
        }
 
        T obj = this.objStack.Pop();
        this.objList.Add(obj);
 
        obj.gameObject.SetActive(true);
 
        return obj;
    }
 
    public void PushObject (T obj)
    {
        obj.gameObject.SetActive(false);
 
        this.objList.Remove(obj);
        this.objStack.Push(obj);
    }
 
    public void Dispose ()
    {
        if (this.objStack == null || this.objList == null)
            return;
 
        this.objList.ForEach(obj => this.objStack.Push(obj));
 
        while (this.objStack.Count > 0)
        {
            GameObject.Destroy(this.objStack.Pop());
        }
 
        this.objList.Clear();
        this.objStack.Clear();
    }
    
 
 
// PoolableObject.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class PoolableObject : MonoBehaviour 
{
    protected ObjectPool<PoolableObject> pPool;
 
    public virtual void Create (ObjectPool<PoolableObject> pool)
    {
        pPool = pool;
 
        gameObject.SetActive(false);
    }
    
    public virtual void Dispose ()
    {
        pPool.PushObject(this);
    }
 
    public virtual void _OnEnableContents ()
    {
        // to do ...
    }
    
    public virtual void _OnDisableContents ()
    {
        // to do ...
    }
 
 
 
// ObjectPoolManager.cs
using UnityEngine;
using System.Collections;
 
public class ObjectPoolManager : MonoBehaviour 
{
    private static ObjectPoolManager singleton;
    public static ObjectPoolManager GetInstance () { return singleton; }
 
    public ObjectPool<PoolableObject> bulletPool = new ObjectPool<PoolableObject>();
    
    public Bullet bulletPrefab;
 
    void Awake ()
    {
        if (singleton != null && singleton != this)
        {
            Destroy(gameObject);
        }
        else 
        {
            singleton = this;
        }
    }
 
    void Start ()
    {
        bulletPool = new ObjectPool<PoolableObject>(5, () => 
        {
            Bullet bullet = Instantiate(bulletPrefab);
            bullet.Create(bulletPool);
            return bullet;
        });
        
        bulletPool.Allocate();
    }
 
    void OnDestroy ()
    {
        bulletPool.Dispose();
        singleton = null;
    }
 
 
 
// Gun.cs
using UnityEngine;
using System.Collections;
 
public class Gun : MonoBehaviour 
{
    [HideInInspector] public Transform tm;
    private Vector3 bulletSpawnPoint;
 
    void Awake ()
    {
        tm = gameObject.GetComponent<Transform>();
        bulletSpawnPoint = transform.FindChild("bulletSpawnPoint").transform.position;
    }
 
    void Update ()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Bullet bullet = ObjectPoolManager.GetInstance().bulletPool.PopObject() as Bullet;
            bullet.Fire(bulletSpawnPoint);
        }
    }
 
 
 
// Bullet.cs
using UnityEngine;
using System.Collections;
 
public class Bullet : PoolableObject
{
    [HideInInspector] public Transform tm;
    [HideInInspector] public Rigidbody2D rb2D;
 
    private float force;
 
    public override void Create (ObjectPool<PoolableObject> pool)
    {
        base.Create (pool);
    }
 
    public override void Dispose ()
    {
        base.Dispose ();
    }
 
    public override void _OnEnableContents ()
    {
        base._OnEnableContents ();
 
        rb2D.Sleep();
    }
 
    public override void _OnDisableContents ()
    {
        base._OnDisableContents ();
    }
 
    void Awake ()
    {
        tm = gameObject.GetComponent<Transform>();
        rb2D = gameObject.GetComponent<Rigidbody2D>();
 
        force = 800.0f;
    }
 
    void OnEnable ()
    {
        _OnEnableContents();
    }
 
    void OnDisable ()
    {
        _OnDisableContents();
    }
 
    void Update ()
    {
        if (tm.position.x > 10.0f)
        {
            Dispose();
        }
    }
 
    public void Fire (Vector3 spawnPoint)
    {
        tm.position = spawnPoint;
 
        rb2D.AddForce(Vector3.right * force);
    }
 
cs


Noraml Extrusion Shader

폴리곤의 노멀벡터 방향으로 버텍스를 확장시키는 간단한 기법

3D 모델을 좀 더 통통하게 할 때(혹은 반대로) 사용


-------------------------------------------------------------

   v.vertex.xyz += v.normal * _Amount;

-------------------------------------------------------------


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
Shader "Custom/NormalExtrusion" 
{
    Properties 
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Amount ("Extrusion Amount", Range(-1.01.0)) = 0
    }
    
    SubShader 
    {
        Tags 
        { 
            "RenderType" = "Opaque" 
        }
    
        CGPROGRAM
        #pragma surface surf Lambert vertex:vert
        
        struct Input
        {
            float2 uv_MainTex;
        };
    
        float _Amount;
        
        void vert (inout appdata_full v)
        {
            v.vertex.xyz += v.normal * _Amount;
        }
    
        sampler2D _MainTex;
                
        void surf (Input IN, inout SurfaceOutput o)
        {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        
        ENDCG
    } 
    FallBack "Diffuse"
cs


[ _Amount : 0 ]


[ _Amount : -0.2 ]


[ _Amount : 0.5 ]


'02.Development > Shader' 카테고리의 다른 글

[Shader] 셰이더 기초 (공유)  (1) 2016.02.04

출처 : http://kieuns.com/wiki/doku.php?id=language:shader:unity:shaderlab-basic#shaderlab



ShaderLab : 기본형식

  • Unity의 셰이더 작성용 ShaderLab 스크립트 언어.

기본 형식

Shader "BasicShader" {
  SubShader {
    // Shaderlab code
  }
}

SubShader

  • 한개의 Shader에 스크립트에, 여러개의 SubShader 작성.
  • 유니티 디바이스에 맞는 스크립트를 선택 사용.

개념상 (그럴걸이라고 믿고 가는) 지원 되는 디바이스 대응

Shader "BasicShader" {
  SubShader {
    // 매우 좋은 스펙 디바이스
  }
  SubShader {
    // 좋은 스펙 디바이스
  }
  SubShader {
    // 낮은 스펙 디바이스
  }
}

Fallback

Shader "BasicShader" {
  SubShader {
    // Shaderlab code
  }
  Fallback "Diffuse"
}
  • 여러 SubShader조차 실패 하는 경우, Fallback에 정의된 쉐이더를 사용.

Pass

  • SubShader 내부에 정의(추가)
  • 오브젝트의 렌더링 단위.
  • SubShader 안의 Pass 수만큼 오브젝트에 셰이더 작업을 처리
  • 위의 것부터 순서대로 처리
Shader "BasicShader" {
  SubShader {
    // Shaderlab code
    Pass {  }
    Pass {  }
  }
  Fallback "Diffuse"
}

ShaderLab 키워드

색 입히기

오브젝트를 퍼렇게 칠한다
Shader "Study/SolidBlue"
{
  SubShader {
    Pass {
      Color (0,0,1,1)
    }
  } 
}
  • 오브젝트를 퍼렇게 칠한다.
  • 색상 정보만 있기 때문에, 색만 칠한다.
  • Color 키워드는 색상을 부여하는구나,라고 이해하고.

재질과 조명추가

키워드

  • Material
    • Diffuse, Ambient
  • Lighting

매트리얼 설정

셰이더의 목적은 재질과 라이팅에 반응되도록 하는 것이므로,

  • 매트리얼과 조명 모델을 추가한다.

SubShader 의 Pass 내부에 Material 키워드로 조명 방식을 추가한다.

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

라이팅추가

재질이 표시되려면 조명을 (재질은 조명을 받은 반응이므로) 추가하는데,

  • 여기에서는 고정파이프라인을 사용하므로
  • 조명에 대한 전반 처리는 GPU에 미리 정의된 것을 사용
  • Lighting 키워드를 조명을 사용한다는 것을 명시
Lighting On

매트리얼, 조명 추가된 셰이더

MaterialLighting 키워드가 Pass 블럭 내부에 있다.

조명과 함께 퍼렇게 칠한다.
Shader "Study/VertexLitBlue"
{
  SubShader {
    Pass {
      Material {
        Diffuse (0,0,1,1)
        Ambient (0,0,1,1)
      }
      Lighting On
    }
  } 
}

Fixed Function Shader

  • VertexShader, PixelShader 이전에 사용되던 그래픽 파이프 라인.
  • 기능이 정해져 있어서, 단순 설정만으로도 동작된다.
  • Per-Vertex Lighting 만 사용.
  • 프로그래밍이 들어가지 않는다.(진짜?)

클래식한 기능이지만, 셰이더 이전의 쓰던 기능은 다 되므로 은근히 막강.

  • 활용은 개인기에 따라..

참고 : Shaders: ShaderLab & Fixed Function shaders

프로퍼티

변수다,생각.
  • 매트리얼의 Diffuse, Ambient에 적용할 색상을 “색상프로퍼티”로 받아 온다.
Material {
  Diffuse (0,0,1,1)
  Ambient (0,0,1,1)
}

위의 예제에서는 매트리얼에 값을 고정 값으로 설정 했는데, Properties 키워드와 구문 블럭 내부에, 변수 역할의 프로퍼티를 추가할 수 있다. 
(유니티 인스펙터에 노출되는 그것들)

Diffuse와 Ambient에 대한 프로퍼티를 추가. 다른 색상을 지정하도록 코드를 변경.

재질 색상을 프로퍼티로 받아들인다.
Shader "Study/VertexLitBlue2"
{
  Properties {
    _DiffuseColor ("DiffuseColor", COLOR) = (0,0,1,1)
    _AmbientColor ("AmbientColor", COLOR) = (0,0,1,1)
  }
  SubShader {
    Pass {
      Material {
        Diffuse [_DiffuseColor]
        Ambient [_AmbientColor]
      }
      Lighting On
    }
  } 
}
  • 매트리얼의 값을 [,]로 둘러서 사용했다.
_DiffuseColor ("DiffuseColor", COLOR) = (0,0,1,1)
  • _DiffuseColor : 셰이더에서 사용할 변수 이름
  • “DiffuseColor” : 인스펙터에서 보여질 이름
  • COLOR : 이 프로퍼티의 타입
  • (0,0,1,1) : 기본 값. 값을 변경하지 않는 경우 사용할 값.

사용 가능한 타입

  • name (“display name”, Range (min, max)) = number 
    : Defines a float property, represented as a slider from min to max in the inspector.
  • name (“display name”, Color) = (number,number,number,number) 
    : Defines a color property.
  • name (“display name”, 2D) = “name” { options } 
    : 2D texture 정의 프로퍼티
  • name (“display name”, Rect) = “name” { options } 
    : Defines a rectangle (non power of 2) texture property.
  • name (“display name”, Cube) = “name” { options } 
    : Defines a cubemap texture property.
  • name (“display name”, Float) = number 
    : Defines a float property.
  • name (“display name”, Vector) = (number,number,number,number) 
    : Defines a four component vector property.

SetTexutre

SetTexutre: 텍스쳐 프로퍼티

2D 프로퍼티로 텍스쳐를 설정해서 오브젝트에 입힐 수 있다.

  • 텍스쳐 프로퍼티 설정
  • 텍스쳐를 오브젝트 적용하는 스크립트
텍스쳐를 입힌다.
Shader "Study/VertexLitTex"
{
    Properties {
        _DiffuseColor ("DiffuseColor", COLOR) = (0,0,1,1)
        _AmbientColor ("AmbientColor", COLOR) = (0,0,1,1)
        _MainTex ("MainTex", 2D) = "white"
    }
    SubShader {
        Pass {
            Material {
                Diffuse [_DiffuseColor]
                Ambient [_AmbientColor]
            }

            Lighting On

            SetTexture [_MainTex] 
        }
    } 
}
최초의 화면 
텍스쳐를 등록한 후의 결과 
  • SetTexture : 키워드로 텍스쳐를 지정한다.

위의 결과는, 텍스쳐를 오브젝트에 적용되었지만 라이팅 연산이 반영되지 않았다.

SetTexutre: 라이팅 연산 추가

SetTexture 구문을 확장해서 ( {, }를 추가한다 ) 텍스쳐 연산 방법을 추가한다.

SetTexture [_MainTex] { Combine texture * primary } 
  • Combine : 텍스쳐에 다른 값과 연산( 곱)한다.
    • texture : Combine에 사용되는 키워드. 텍스쳐를 뜻함
    • primary : Combine 키워드가 호출되지 전까지의 계산 결과물.
Shader "Study/VertexLitTex2"
{
    Properties {
        _DiffuseColor ("DiffuseColor", COLOR) = (0,0,1,1)
        _AmbientColor ("AmbientColor", COLOR) = (0,0,1,1)
        _MainTex ("MainTex", 2D) = "white"
    }
    SubShader {
        Pass {
            Material {
                Diffuse [_DiffuseColor]
                Ambient [_AmbientColor]
            }
            Lighting On
            SetTexture [_MainTex] {
                Combine texture * primary
            }
        }
    } 
}

텍스쳐 색상(texture)과 이전 라이팅 계산 결과(primary)를 곱해서 화면에 표시한다.

Combine

Combine에 대한 온라인 도움말

  • 3개까지의 src1~src3 까지 사용가능한 듯
  • 가능한 연산
    • 곱/합/빼기

Combine의 조합 방식

combine <src1> * <src2>곱한다. 결과는 다소 어두워진다. 하지만 색상 결합의 기본은 곱이므로 이걸 사용한다.
(0.2 * 0.2 = 0.04) 색상이 점점 0(검정)에 가까워지므로.
하지만 1(white)로 곱하면 원래 색상이 나오므로.
combine <src1> + <src2>더한다. 밝아진다. 
점점 1에 가까워지니까.
combine <src1> - <src2>빼는데.. 이건 좀 미묘.
combine <src1> +- <src2>src1을 src2에 더한 다음, 0.5를 뺀다. (signed add?)
combine <src1> lerp(<src2>) <src3>src2의 알파값으로 src1(=1)과 src3(=0)사이의 보간 값을 사용한다.
숫자 범위가 (1~0)임의 주의
combine <src1> * <src2> + <src3>src1.rgba * src2.a + src3 . 이건 뭘까..
combine <src1> * <src2> +- <src3>src1.rgba * src2.a + src3 - 0.5
combine <src1> * <src2> - <src3>src1(전체) * src2(알파값) - src3(전체)

src 프로퍼티는 아래 키워드로 변경 가능

  • Texture : SetTexture [<텍스쳐 이름>]에서 지정한 텍스쳐. (SetTexture 구문에서 텍스쳐를 제어하려면 가리키는 키워드가 필요하잖아.)
  • Primary : 정점 색상을 사용한 라이팅 계산 결과. Material에 지정한 조명 모델에 대한 계산 결과.
  • Previous : 바로 직전까지 계산한 텍스쳐 연산 결과.
  • Constant : Constant is the color specified in ConstantColor

combine <연산> <보정용 키워드>

  • doublequad : combine 결과를 x2 또는 x4
    SetTexture [_MainTex] { Combine texture * primary DOUBLE }
  • src 프로퍼티에는 one - 옵션을 붙여서 반대값을 사용하게 할 수 있다.
  • src : src alphaalpha키워드를 붙어서 알파만 사용하게 할 수 있다.
src1 * src2src1 + src2src1 - src2

색상과 알파분리

combine src1 src2, color(alpha)

“,” 로 색상과 알파 부분을 분리.

  1. “src1 src2”에서는 원래 그대로 적용
  2. color(alpha)에서는 (1)번 연산에서 적용된 결과에 알파값을 적용

그외

  • ConstantColor color \\:Defines a constant color that can be used in the combine command.
  • matrix [MatrixPropertyName] \\: Transforms texture coordinates used in this command with the given matrix.

SetTexutre: 두장의 텍스쳐

combine lerp 활용

텍스쳐만 표시되도록 재질 설정을 삭제하고 텍스쳐만 받는 셰이더 작성

Shader "Study/TwoTexture"
{
  Properties {
    _MainTex ("MainTex", 2D) = "white"
    _SubTex ("SubTex", 2D) = "white"
  }
  SubShader {
    Pass {
      SetTexture [_MainTex] 
      
      SetTexture [_SubTex] { Combine texture * previous }
    }
  } 
}
 
  • primary 삭제 : primary를 넣으면, 연산된 라이팅 결과가 없기 때문에 검게 처리된다.
  • _MainTex 지정 후, 
    바로 _SubTex로 previous로 “곱”연산.
  • 결과는 두장의 이미지가 겹쳐 보인다.

두번째 텍스쳐는 알파값이 있는 투명한 이미지인데, 투명 처리를 하지 않아서 그림이 깨져보인다.

combine lerp()로 알파를 반영 해본다.

// texture의 알파값으로 src1, src2의 비율을 조정한다. 알파가 1(not 투명) _SubTex값을 사용, 0에 가까워질수록 previous를 사용
SetTexture [_SubTex] {
  Combine texture lerp(texture) previous
}

셰이더 수정

Shader "Study/TwoTexture"
{
    Properties {
        _MainTex ("MainTex", 2D) = "white"
        _SubTex ("SubTex", 2D) = "white"
    }
    SubShader {
        Pass {
            SetTexture [_MainTex] 

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

Blend

반투명 효과 같은, “색상섞은 후 효과”를 얻으려면 Blend 로 조합 방법을 적는다.

기본 포맷

Blend <SrcFactor> <DstFactor>
(새로 찍을 색상 값) SrcFactor * (기존 배경의 색상) DstColor

색상을 섞겠다는 뜻인데, SrcFactor와 DstFactor에는 블렌딩에 사용될 색상 속성(Blend Factor)을 지정해야 한다.

Blend SrcAlpha OneMinusSrcAlpha

원래 문서의 설명(이 명료해서)

 

 

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

 

 

  • Factor라는 것에 유의해야 하는데,
  • OneMinusAlpha는 원본 색상에서의 (1-Alpha)값을 배경색에 대한 알파값으로 적용하라는 뜻.

0~1 사이의 색상이나 알파 값을 어디에서 가져올 것이냐에 대한 선택 구문

잡…

Blend SrcAlpha DstAlpha
이 경우, 둘다 0.6이면 어떻게 될려나. 최대값은 1이니까

구문의 위치

SubShader, Pass의 안쪽에 위치.

오브젝트를 반투명하게 표시

텍스쳐 두장만 표시하는 셰이더에, Blend를 넣어 봅니다.

Shader "Study/TransObject"
{
  Properties {
    _MainTex ("MainTex", 2D) = "white" {}
    _SubTex ("SubTex", 2D) = "white" {}
  }
  SubShader {
    Pass {
  
Blend SrcAlpha OneMinusSrcAlpha

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

Blend만 넣어서는 결과가 나오지 않음.

어느 정도의 알파값을 SrcAlpha로 지정할 것인지 정할 수 있는 코드를 넣어야 하는데, 색상값을 입력 받아 
이 색상의 알파값을 최종 알파값으로 사용하도록 코드를 추가.

Shader "Study/TransObject"
{
  Properties {

_Alpha ("AlphaColor", COLOR) = (0,0,0,1)

    _MainTex ("MainTex", 2D) = "white" {}
    _SubTex ("SubTex", 2D) = "white" {}
  }
  SubShader {
    Pass {
  
Blend SrcAlpha OneMinusSrcAlpha

      SetTexture [_MainTex] 
      SetTexture [_SubTex] { 

ConstantColor[_Alpha]  
Combine texture lerp(texture) previous, constant

      }
    }
  } 
}
  • _Alpha라는 컬러 값을 받는 프로퍼티 추가
  • SubShader > Pass > 두번째 SetTexutre 에, ConstantColor 키워드로 색상 숫자값을 선언하고,
  • combine에서 텍스쳐 연산 이후에
  • 얼마나 알파 값을 적용할 것인지 _Alpha를 사용하도록 코드 추가

렌더링 큐를 설정하지 않는데도 앞뒤 제대로 나오니.. 쩝인데. 혹시 모르니,

오브젝트의 앞 뒤로 반투명하게 나와야 할게 제대로 나오지 않는다면 렌더링 큐 설정이 필요하다는 뜻.

Shader “Study/TransObject” {

Properties {
  _Alpha ("AlphaColor", COLOR) = (0,0,0,1)
  _MainTex ("MainTex", 2D) = "white" {}
  _SubTex ("SubTex", 2D) = "white" {}
}
SubShader {
  

Tags { “Queue” = “Transparent” }

  
  Pass {
    Blend SrcAlpha OneMinusSrcAlpha
    SetTexture [_MainTex] 
    SetTexture [_SubTex] { 
      ConstantColor[_Alpha]  
      Combine texture lerp(texture) previous, constant
    }
  }
} 

} </code>

렌더링 큐를 Transparent로 설정해서, Transparent 에 해당되는 녀석들이 그려질때 같이 그려지도록 설정한다.

렌더링 큐

오브젝트가 렌더링 되는 순서를 정할 수 있는데, 5단계가 기본이다.

키워드숫자인덱스설명
Background1000가장 먼저 그린다. 다른 것들에 가린다는 뜻
Geometry 
기본값
2000대부분 여기에 해당됨. 반투명 오브젝트 위주
AlphaTest2450반투명한 Geometry? 가 뭐지? 나무? 알파 테스트를 필요 있는 것.
Transparent3000투명한 (유리, 파티클) 오브젝트는 여기로 와야 함. 이게 거의 많이 쓰일 것 같은데..
Overlay4000가장 나중에 그리는데, 언제든 보이는 가장 앞에 있는 (렌즈플레어 같은) 오브젝트

렌더링 큐는 Tag 키워드로 입력하는데,

  • SubShader에 위치할 수도 있고,
  • Pass 구문 안에도 넣을 수 있다.
Tags = { "Queue" = "Transparent" }

또한 기본 키워드에 숫자를 더하거나 빼서 렌더링 순서를 임의 설정할 수 있다.

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

색상

셰이더에서도 색상값은 (0~1) 사이의 값을 사용.

  • 순서 : RGBA
  • Color( 1, 1, 1, 1 ) : Color ( <R>, <G>, <B>, <A> )

색상 연산

범위 값이, 0~1 사이 값인 것만 주의 하면 연산할때 도움이 된다.

    • 큰수와 작은수를 곱하면 색상값이 더 작아지는데 조심. ( 0.1 * 0.01, 작아지지잖아 )
    • Pow 함수를 사용할때는 요 주의
    • 곱보다는 숫자 변화가 적지만
    • 더한다는건 더 흰색에 가까워진다는 뜻이므로



'02.Development > Shader' 카테고리의 다른 글

[Shader] Normal Extrusion Shader  (0) 2016.02.04


1
2
3
4
5
6
7
8
9
    using UnityEngine;
 
    // return : -180 ~ 180 degree (for unity)
    public static float GetAngle (Vector3 vStart, Vector3 vEnd)
    {
        Vector3 v = vEnd - vStart;
 
        return Mathf.Atan2(v.y, v.x) * Mathf.Rad2Deg;
    }
cs




프로젝트를 하나 켜놓은 상태에서 Mac 터미널 실행 후


> open -na unity



정렬되지 않은 전체 자료 중에서 해당 위치에 맞는 자료를 선택하여 위치를 교환하는 정렬 방식

같은 값의 인덱스끼리도 교환 연산이 발생하기때문에 안전성을 만족하지 않고 전체적인 알고리즘 효율성을 볼 때 느림

 

 - 시간복잡도 (Big-O notation)  


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
using System.Collections.Generic;
 
    public enum SortType
    {
        Asc,
        Desc
    }
 
    public static void SelectionSort(int[] array, SortType st)
    {
        int length = array.Length;
 
        int key;
        int tmp;
 
        if (st == SortType.Asc)
        {
            // Asc
            for (int i = 0; i < length - 1++i)
            {
                key = i;
 
                for (int j = i + 1; j < length; ++j)
                {
                    if (array[key] > array[j])
                    {
                        tmp = array[j];
                        array[j] = array[key];
                        array[key] = tmp;
                    }
                }
            }
        }
        else
        {
            // Desc
            for (int i = 0; i < length - 1++i)
            {
                key = i;
 
                for (int j = i + 1; j < length; ++j)
                {
                    if (array[key] < array[j])
                    {
                        tmp = array[j];
                        array[j] = array[key];
                        array[key] = tmp;
                    }
                }
            }
        }
    }
cs

+ Recent posts