Unity SVN 연동
'협업 > SVN' 카테고리의 다른 글
Unity3D SVN 연동 방법 (0) | 2014.01.03 |
---|---|
Unity3D SVN 연동방법 (0) | 2012.12.25 |
Unity3D SVN 연동 방법 (0) | 2014.01.03 |
---|---|
Unity3D SVN 연동방법 (0) | 2012.12.25 |
This is something I use to capture errors and log-information from games that are out in the wild, and report them back home to my server.
using UnityEngine;
using System.Collections;
public class ErrorReporter : MonoBehaviour
{
string debugText = "";
string postURL = "";
void Awake ()
{
Application.RegisterLogCallback (new Application.LogCallback (CaptureLog));
}
IEnumerator SendDebugToServer ()
{
WWW www = new WWW (postURL, System.Text.ASCIIEncoding.ASCII.GetBytes (debugText));
yield return www;
}
void CaptureLog (string condition, string stacktrace, LogType type)
{
string sep = "------------------------------------------------------------------------------\r\n";
debugText = sep + type.ToString () + " " + Time.realtimeSinceStartup.ToString () + "\r\n" + condition + "\r\n" + stacktrace + debugText;
if (type == LogType.Exception) {
StartCoroutine (SendDebugToServer ());
if (!Application.isEditor) {
SomethingReallyBadHappened ();
}
}
}
void SomethingReallyBadHappened ()
{
//NB: Try and recover or fail gracefully here.
}
}
On the server side, you could use a very simple piece of PHP to receive the request and write it to a file.
$data = file_get_contents('php://input');
$file = "reports/report_" . time() . ".txt";
$fp = fopen($file, "w") or die("Couldn't open $file for writing!");
fwrite($fp, $data) or die("Couldn't write values to file!");
fclose($fp);
echo "Saved to $file successfully!";
출처 : http://entitycrisis.blogspot.kr/2011/01/error-reporting-from-your-unity3d-game.html
[펌] iOS Crash Reporting Tools 소개 (1) | 2017.12.11 |
---|---|
참조 자료 (0) | 2014.04.08 |
[Unity Asset] Crash Reporter($10) : https://www.assetstore.unity3d.com/#/content/8215
[Unity] CrashReport Class : http://docs.unity3d.com/Documentation/ScriptReference/CrashReport.html
[Unity] Application.RegisterLogCallback : http://docs.unity3d.com/Documentation/ScriptReference/Application.RegisterLogCallback.html
Link : http://answers.unity3d.com/questions/207913/crash-reporting-for-unity-game.html
[펌] iOS Crash Reporting Tools 소개 (1) | 2017.12.11 |
---|---|
Error Reporting from your Unity3D game. (0) | 2014.04.08 |
어플리케이션의 스테이트에 따라서 특정한 처리가 요구될 경우가 있습니다.
이를 위해 안드로이드. iOS 양쪽 모두 통용되는 메소드를 지원하고 있습니다.
OnApplicationPause(bool pause) : 홈키로 어플을 내려 pause 상태로 만들었을 때의 처리
OnApplicationQuit() : 어플을 종료할 때의 처리
메소드의 이름만 보면, 안드로이드 쪽에 가깝군요. 하지만 Unity3D는 여러가지 면에서 iOS를 더 사랑하는 것 같습니다.
(푸시 지원 해주는 것만 봐도...) 간략한 예제를 통해서 어떤 식으로 쓰이게 되는지 알아보도록 하겠습니다.
1) OnApplicationPause
bool bPaused = false; // 어플리케이션이 내려진 상태인지 아닌지의 스테이트를 저장하기 위한 변수
void OnApplicationPause(bool pause)
{
if (pause)
{
bPaused = true;
// todo : 어플리케이션을 내리는 순간에 처리할 행동들 /
}
else
{
if (bPaused)
{
bPaused = false;
//todo : 내려놓은 어플리케이션을 다시 올리는 순간에 처리할 행동들
}
}
2) OnApplicationQuit
void OnApplicationQuit()
{
// todo : 어플리케이션을 종료하는 순간에 처리할 행동들
}
출처] [Unity3D] 어플을 내렸을때, 어플을 종료할때의 처리|작성자 베르제
ScreenWipe CrossFade with C# (0) | 2014.04.22 |
---|---|
A simple cross fade shader for Unity (0) | 2014.04.22 |
Unity Singleton (0) | 2014.03.24 |
Platform Dependent Compilation (0) | 2014.03.11 |
Generic Based Singleton for MonoBehaviours完全版(?) (0) | 2014.03.05 |
얼마 전 Justin Yoo님의 논리 연산자와 이진 연산자의 차이에 대한 블로그 포스트가 그 분의 의도와는 다르게 페이스북 ‘생활코딩’ 그룹에 콜로세움(?)을 세운 사건이 있었습니다. 저는 포함되지 않아서 아쉬운 내용이 조금 있었지만 많은 분들에게 도움이 될만한 글이며 크게 문제가 될 부분은 없다고 생각했는데 다른 의견을 가진 분들이 많이 있었습니다. 급기야 C#의 논리 연산자와 이진 연산자 내부 동작이 C/C++과 같지 않다는 오해까지 번져나갔습니다. C/C++에만 익숙한 분들이 겉모습이 비슷하긴 하지만 C#에서는 논리 연산자는 오직 System.Boolean
값 사이에만 사용 가능하며(연산자 사용자 정의는 논외로 하겠습니다. 일이 너무 커져요…) 조건식 결과 역시 System.Boolean
형식만 가능함을 알지 못했기 때문입니다. 마찬가지로 C#만 경험한 분들은 C/C++에서 if
구문과 조건부 삼항 연산자(?:
)에 다양한 형식의 식이 사용된다는 점을 몰랐겠죠.
그리고 2주 쯤 전에 회사에서 코드 리뷰를 할 때 동료 사원 한 분이 플래그 열거형의 필드 값을 10진수가 아닌 16진수를 사용해 정의한 이유를 물어보셨는데 당시 시간 여건 상(점심시간이 다가오고 있었어요!) 충분한 설명을 해 드리지 못했습니다. 그래서 해당 내용과 Justin Yoo님의 포스트에 포함되지 않은 내용을 함께 정리해 봅니다. 언어는 C#을 기준으로 진행하지만 개념적인 부분은 다른 프로그래밍 언어에도 그대로 적용됩니다.
C#의 논리 연산자와 이진 연산자 내부 동작이 궁금하면 C++로 작성된 CLI 소스 코드를 확인하는 것을 권합니다.
이 글을 먼저 읽고 Justin Yoo님의 포스트를 읽으시면 더 이해가 쉬울 거라 생각됩니다.
Flags를 말 그대로 풀이하면 여러 개의 깃발입니다. 프로그래밍에서 깃발은 올려진 상태 또는 내려진 상태를 나타내는 데이터를 의미합니다. 다시 말해 0 또는 1, 참 또는 거짓, 예 또는 아니오 등의 상태를 표현하기 위해 사용됩니다. 서로 다른 두 개의 상태를 표현하기 위해 컴퓨터는 0 또는 1을 나타내는 한 개의 비트를 사용합니다. 일반적으로 컴퓨터가 데이터를 처리하는 최소 단위는 8개의 비트로 이루어진 바이트(byte)입니다. 그리고 매우 자주 사용되는 자료형인 System.Int32
의 크기는 4바이트, 즉 32비트입니다. 32비트 공간에는 1개의 32비트 크기의(도메인에 정의되는) 정보를 저장할 수 있습니다. 16비트 크기의 정보는 2개를, 8비트 크기의 정보는 4개를 저장할 수 있지요. 마찬가지로 1비트 크기의 정보는 32개를 저장할 수 있습니다.
1 2 3 | 16 x 2 ++++ ++++ ++++ ++++ ---- ---- ---- ---- 8 x 4 ++++ ++++ ---- ---- ++++ ++++ ---- ---- 1 x 32 +-+- +-+- +-+- +-+- +-+- +-+- +-+- +-+- |
정수 데이터의 각 비트 값을 표현하는 데에는 10진수보다는 16진수가 더 다루기 쉽습니다. 이유를 설명하기 전에 16비트 데이터의 모든 비트 값을 10진수와 16진수로 나타낸 표를 먼저 확인하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Dec Hex ------------------- ------- ------- 0000 0000 0000 0001 1 0x0001 0000 0000 0000 0010 2 0x0002 0000 0000 0000 0100 4 0x0004 0000 0000 0000 1000 8 0x0008 0000 0000 0001 0000 16 0x0010 0000 0000 0010 0000 32 0x0020 0000 0000 0100 0000 64 0x0040 0000 0000 1000 0000 128 0x0080 0000 0001 0000 0000 256 0x0100 0000 0010 0000 0000 512 0x0200 0000 0100 0000 0000 1024 0x0400 0000 1000 0000 0000 2048 0x0800 0001 0000 0000 0000 4096 0x1000 0010 0000 0000 0000 8198 0x2000 0100 0000 0000 0000 16384 0x4000 1000 0000 0000 0000 32768 0x8000 |
10진수의 경우에는 별다른 패턴이 없지만 16진수는 4개 비트 마다 1, 2, 4, 8이 자릿수가 변경되며 반복되는 것을 볼 수 있습니다. 8진수를 사용해도 1, 2, 4가 반복되지만 3개 비트 단위로 반복되는 것보다 4개 단위로 반복되는 것이 8비트, 16비트, 32비트, 64비트 데이터를 표현하기에 좀 더 편리합니다. 그래서 4비트가 넘어가는 플래그 데이터를 정의할 때 16진수가 주로 사용됩니다.
플래그 데이터를 다룰 때에는 산술 연산이 아닌 이진 연산을 사용합니다. 이진 연산을 이용해 플래그를 조합하거나 제거하고 데이터에 특정 플래그가 포함되어 있는지 검사합니다.
플래그 데이터를 조합하는 데에 이진 |
연산자를 사용합니다. 이진 OR 연산은 두 개의 데이터에 대해 각 비트 별로 논리합 연산을 수행합니다. 두 비트 중 하나 이상의 값이 1이면 결과 비트는 1이고 두 비트 모두 0이면 결과 비트는 0입니다. 이진 OR 연산을 사용해 어떻게 플래그 데이터를 조합하는지 살펴봅니다.
1 2 3 4 5 6 7 8 | var a = 1; // 1(0001) var b = 2; // 2(0010) var c = a | b; // 3(0011) var d = a + b; // 3(0011) var e = a | c; // 3(0011) var f = a + c; // 4(0100) |
위 코드를 보면 a
와 b
는 중복되는 비트가 없는데 이런 경우는 | 연산 결과는 + 연산 결과와 같습니다. 그래서 c
와 d
는 값이 같습니다. 하지만 중복되는 비트가 있으면 두 연산 결과는 같지 않습니다. e
의 경우 a
가 가진 비트 값과 c
가 가진 비트 값이 모두 유지되어 있지만 f
는 두 개의 비트가 사라지고 하나의 비트가 생겼났습니다.
데이터에 특정 플래그가 포함되어 있는지 검사하는 데에 & 연산자를 사용합니다. 이진 AND 연산은 두 개의 데이터를 각 비트 별로 논리곱 연산을 수행합니다. 두 비트 모두 1이면 결과 비트는 1이고 둘 중 하나 이상의 비트가 0이면 결과 비트는 0입니다. 이진 AND 연산을 사용해 플래그 포함 여부를 검사하는 방법을 살펴봅니다.
1 2 3 4 5 6 7 8 9 10 11 | var a = 1; // 1(0001) var b = 2; // 2(0010) var c = 4; // 4(0100) var d = a | c; // 5(0101) var e = d & a; // 1(0001) var f = d & b; // 0(0000) bool hasA = (d & a) == a; // True bool hasB = (d & b) == b; // False |
a
, b
, c
는 서로 다른 비트 값을 가지는 플래그들이고 d
는 a
와 c
의 조합입니다. 이 때 d
가 특정 플래그를 포함하는지 여부를 알아보려면 대상 플래그와 논리곱 연산을 수행한 결과가 해당 플래그와 같은지 검사하면 됩니다.
이진 배타적 OR 연산을 사용하면 데이터에서 특정 플래그를 제거할 수 있습니다. 이진 배타적 OR 연산은 두 개의 데이터에 대해 각 비트 별로 배타적 논리합 연산을 수행합니다. 두 비트의 값이 다르면 결과 비트는 1이고 두 비트의 값이 같으면 결과 비트는 0입니다. 아래 코드는 이진 배타적 OR 연산을 사용해 데이터에서 특정 플래그를 제거하는 방법을 보여줍니다.
1 2 3 4 5 6 7 | var a = 1; // 1(0001) var b = 2; // 2(0010) var c = 4; // 4(0100) var d = a | b | c; // 7(0111) var e = (d ^ b) & d; // 5(0101) |
d
는 a
, b
, c
플래그가 조합된 값입니다. d
와 b
의 이진 배타적 OR 연산을 수행한 결과와 d
의 논리곱 연산을 수행하면 d
에서 b
플래그를 제거한 값을 얻을 수 있습니다. 이때 주의할 것은 마지막 논리곱 연산을 빠뜨리면 안된다는 점입니다. 위 코드의 경우는 b
가 가진 모든 비트를 d
가 포함하고 있기 때문에 논리곱 연산 전후 값이 같지만 만약 d
가 가지지 않은 비트를 가진 데이터(예를 들어 1010 비트를 가진 데이터)의 플래그들을 d
에서 제거하려할 경우 논리곱 연산을 생략하면 잘못된 값을 얻게됩니다.
이진 보수 연산을 사용해도 데이터에서 특정 플래그를 제거할 수 있습니다. 이진 보수 연산은 앞에 설명된 연산자들과는 달리 이항 연산이 아닌 단항 연산이며 각 비트에 대해 0은 1로, 1은 0으로 변환된 값을 반환합니다.
1 2 3 4 5 6 7 | var a = 1; // 1(0001) var b = 2; // 2(0010) var c = 4; // 4(0100) var d = a | b | c; // 7(0111) var e = (d & ~b); // 5(0101) |
이진 배타적 OR 연산의 예제와 동일한 작업을 하는 코드입니다. 제거하려는 플래그를 가진 데이터의 이진 보수 연산 결과와 논리곱 연산을 수행하면 원본 데이터에서 특정 플래그를 삭제할 수 있습니다. 이진 배타적 OR 연산을 사용한 플래그 제거와 결과는 동일하지만 성능은 조금 더 높습니다.
System.FlagsAttribute
.NET Framework 코드에서 플래그를 정의할 때 System.FlagsAttribute
특성을 가진 열거형(enum
)을 사용합니다. System.FlagsAttribute
특성은 열거형의 멤버가 플래그 데이터 또는 플래그 데이터의 조합을 나타냄을 의미합니다. 이런 경우 하나의 변수에 여러 개의 이진 데이터를 담기 때문에 열거형의 이름은 주로 복수형 명사가 됩니다.
다음은 추적 출력 대상을 지정하는 System.Diagnostics.TraceOptions
열거형 정의입니다.
1 2 3 4 5 6 7 8 9 10 | [Flags] public enum TraceOptions { None = 0, LogicalOperationStack = 0x01, DateTime = 0x02, Timestamp = 0x04, ProcessId = 0x08, ThreadId = 0x10, Callstack = 0x20, } |
System.FlagsAttribute
특성을 가진 열거형 멤버의 값이 반드시 하나의 비트 플래그만 가질 필요는 없습니다. 예를 들어All = 0x3F
와 같은 멤버를 추가로 정의할 수 있습니다. 또 이런 여러 플래그를 포함하는 멤버를 정의할 때 이미 정의된 멤버를 조합하는 것도 가능합니다.
123456[Flags]
public
enum
TraceOptions {
...
All = LogicalOperationStack | DateTime | Timestamp |
ProcessId | ThreadId | Callstack
}
이 열거형에 대해 플래그 조합하고, 포함 여부를 검사하고, 그리고 특정 플래그를 제거하는 예제입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | TraceOptions options = TraceOptions.DateTime; Console.WriteLine(options); // DateTime // Add ProcessId and Callstack options |= (TraceOptions.ProcessId | TraceOptions.Callstack); Console.WriteLine(options); // DateTime, ProcessId, Callstack // Remove DateTime options &= ~TraceOptions.DateTime; Console.WriteLine(options); // ProcessId, Callstack Console.WriteLine((options & TraceOptions.DateTime) == TraceOptions.DateTime); // False Console.WriteLine(options.HasFlag(TraceOptions.ProcessId)); // True |
위 코드의 마지막 줄에 사용된 HasFlag()
메서드는 .NET Framework 4.0에 등장한 메서드입니다. 논리곱 연산자를 사용한 플래그 검사와 비교할 때 성능이 낮은 반면 코드를 좀 더 직관적이고 간편하게 작성하도록 도와줍니다. 아래는 HasFlag()
메서드의 내부 구현입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [System.Security.SecuritySafeCritical] public Boolean HasFlag(Enum flag) { if (flag == null ) throw new ArgumentNullException( "flag" ); Contract.EndContractBlock(); if (! this .GetType().IsEquivalentTo(flag.GetType())) { throw new ArgumentException(Environment.GetResourceString( "Argument_EnumTypeDoesNotMatch" , flag.GetType(), this .GetType())); } return InternalHasFlag(flag); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.None)] [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern bool InternalHasFlag(Enum flags); |
저는 플래그 검사가 짧은 시간에 반복적으로 아주 많이 수행되지 않는다면
HasFlag()
메서드를 사용해 가독성 높은 코드를 작성하는 편을 선호합니다.
이진 연산을 이해하고 플래그 데이터를 적절히 사용하면 메모리 효율을 높이고 메서드 매개 변수를 줄여주는 등 간결한 코드 작성에 도움을 줍니다. 지금보다 메모리가 많이 귀하던(물론 지금도 귀하죠!) 시절에는 하나의 비트로 표현될 수 있는 여러 개의 데이터를 각각 정수형 변수에 저장하는 것은 더더욱 피해야 할 과소비였습니다.
Standard Numeric Format Strings (0) | 2014.05.13 |
---|---|
숫자 3자리마다 콤마(,) 찍기 (0) | 2014.05.13 |
How to create Excel file in C#(Source) (0) | 2014.03.26 |
C# Excel Tutorial (0) | 2014.03.26 |
C# 에서 Excel 로 데이터 기록 및 읽기 [OleDB] (46) | 2014.03.25 |
Unity Pro에 있는 Blob Project Shadow를 쓰는데 그림자가 알파가 있는 텍스쳐보다 아래 그려져서 떠보이길래 약간 수정..
겸사겸사 Color Offset도 추가..
Shader "Projector/Projector Multiply Offset" { Properties { _ShadowTex ("Cookie", 2D) = "gray" { TexGen ObjectLinear } _FalloffTex ("FallOff", 2D) = "white" { TexGen ObjectLinear } _Tint ("Offset", Color) = (0,0,0,0) } Subshader { Tags { "RenderType"="Transparent" "Queue"="Transparent+100" // 알파 텍스쳐보다 위에 그리기 위해 Queue를 올림.. } Pass { ZWrite Off Offset -1, -1 Fog { Color (1, 1, 1) } AlphaTest Greater 0 ColorMask RGB Blend DstColor Zero SetTexture [_ShadowTex] { combine texture, ONE - texture Matrix [_Projector] } SetTexture [_FalloffTex] { constantColor (1,1,1,0) combine previous lerp (texture) constant Matrix [_ProjectorClip] } SetTexture [_FalloffTex] { // add offset constantColor [_Tint] combine previous + constant } } } }
참조 : http://forum.unity3d.com/threads/25162-Projector-Multiply-With-Alpha
셰도우캐스터(ShadowCaster)를 활용한 그림자 생성 변경 (0) | 2014.03.29 |
---|---|
유니티 3D에서 그림자 설정 (0) | 2014.03.29 |
실시간 그림자 (0) | 2014.03.07 |
Rendering Order - Queue tag (0) | 2014.06.24 |
---|---|
UnityDiffuseLightmap.shader by jimfleming (0) | 2014.06.23 |
Toon/Tf2Shader (0) | 2013.07.19 |
Toon/Basic with Alpha (0) | 2013.07.19 |
Toon/Lighted with Alpha (0) | 2013.07.19 |
질문글이 올라왔길래 궁금해서 찾아보다가 정리..
필요한 시기에 GUI.enabled를 제어하는 방법으로 처리하면 된다.
bool bModify = false; void OnGUI() { GUI.enabled = bModify; if (GUILayout.Button("Test Disable Button")) { // Func.. } GUI.enabled = true; }
소스가 수정 되는 등 활성화가 필요한 시기에 bModify 값을 true로 고쳐주면 될 것 같다.
참조 : http://answers.unity3d.com/questions/32962/disable-a-button.html
Nullable Types (0) | 2014.08.18 |
---|---|
ObjectPool (0) | 2014.04.22 |
Save Scene while on play mode (0) | 2014.01.12 |
Auto-Save Scene on Run (0) | 2014.01.12 |
Combine Children Extented (sources to share) (0) | 2013.01.17 |
원문 - http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html
자주 보는 문서라 한글로 보는 게 편할 것 같아 번역해서 적어놓습니다. 오역이 있을 수 있습니다. T_T
2012년 7월 29일 자 버전 번역본
대다수 게임의 성공에 있어 좋은 성능은 가장 중요한 부분입니다. 아래 내용은 여러분 게임의 그래픽 렌더링 속도를 최대화하는 몇 가지 요약 지침입니다.
여러분 게임의 시각적 부분은 컴퓨터의 두 장치 GPU나 CPU에서 주로 비용을 사용합니다. 무슨 최적화든지 간에 첫 번째로 볼 것은 성능 문제가 있는 곳을 찾는 것입니다. 왜 그러냐 하면 GPU와 CPU의 최적화 방법은 상당한 차이(도 있고 반대로 GPU일 때도 그렇지만, CPU를 최적화하는 동안 GPU를 좀 더 일하게 하는 일이 상당히 흔하기)가 있기 때문입니다.
발생 빈도가 높은 병목과 그를 확인하는 방법
- GPU는 보통 fillrate나 메모리 대역폭에 의해 제약을 받습니다.
- 게임을 저해상도로 돌리면 더 빨라진다고요? 그렇다면, 아마도 GPU상의 fillrate에 의해 제약받고 있을 겁니다.
- CPU는 보통 draw calls라고 부르는 렌더링을 해야 하는 물체 수에 의해 제약을 받습니다.
- 렌더링 통계 창에 나오는 draw calls를 확인해 보세요. 이게 몇천(PC에서)이나 몇백(모바일에서)을 넘는다면 물체 수를 최적화해야 합니다.
물론, 이건 보통 이렇다는 겁니다. 병목은 다른 곳에 있을 수도 있습니다. 발생 빈도가 낮은 병목은 다음과 같습니다.
- GPU와 CPU 둘 다 확인해도 렌더링이 문제가 아닌 경우! 예를 들어, 직접 작성한 스크립트나 물리가 실제 문제를 일으키고 있을 수 있습니다. 분석기를 사용해 문제를 찾아보세요. (하지만 난 베이직이지.)
- GPU가 처리할 정점이 너무 많은 경우. 얼마나 많은 수의 정점이 “괜찮은지”는 GPU와 정점 셰이더의 복잡도에 의해 좌우됩니다. 모바일에서는 “십만 개를 넘지 않고”, PC에서는 “몇백만 개를 넘지 않는 것”이 적당합니다.
- CPU에서 처리하는 정점이 처리하기엔 너무 많은 경우. 골격 메시(skinned meshes), 피복 시뮬레이션, 입자(particle) 등이 될 수 있습니다.
어떤 물체를 화면에 표현하기 위해 어느 조명이 물체에 영향을 주는지 알아내기, 셰이더와 셰이더 매개변수를 설정하기, 그리기 입력을 그래픽 드라이버로 보내기, 그래픽 카드로 보낼 명령을 준비하기 같은 몇 가지 작업을 CPU가 합니다. “물체당 한 번씩”하는 앞에 나온 모든 CPU 작업은 그렇게 많이 저렴하지 않기 때문에 보이는 물체가 많다면 비용이 증가할 것입니다.
예를 들어서, 삼각형(보통 국내에서는 폴리곤이라고 부르는데 원문 그대로 번역합니다.) 천 개를 가지고 있다고 합시다. 삼각형 하나로 구성된 천 개의 개별적인 메시가 있을 때보다 모든 삼각형이 한 개의 메시 안에(로 구성되어) 있을 때가 비용이 더 많이 많이 저렴할 겁니다. GPU상에서 두 경우의 비용은 아주 비슷하지만, CPU에서 천 개의 물체를 표현 완료하는 것은(한 개에 비해) 비용이 상당할 것입니다.
CPU가 일을 더 적게 하도록 보이는 물체 수를 줄이는 것이 좋습니다.
- 근접해 있는 물체들을 유니티의 draw call 일괄처리를 사용하거나 직접 합칩니다.
- 개별 텍스처를 큰 텍스처 지도(texture atlas)에 넣거나 다른 방법으로 물체의 재질(materials)을 적게 사용합니다.
- 물체를 여러 번 렌더하게 하는 기능을 적게 사용합니다. (반사, 그림자, 픽셀당 조명 등등, 아래 참조.)
각각의 메시가 최소 몇백 개의 삼각형을 갖도록 물체들을 함께 합치고 합친 메시당 한 개의 재질만 사용합니다. 재질을 공유하지 않는 두 물체를 합치는 것은 성능 향상이 전혀 없다는 것을 이해하는 것이 중요합니다. 두 메시가 같은 텍스처를 사용하지 않는 것이 다중 재질을 사용하는 가장 흔한 이유이기 때문에 CPU를 최적화하려면, 반드시 합치려는 물체가 같은 텍스처를 사용하게 해야 합니다.
하지만 전방 렌더링 패스에서 픽셀 조명을 많이 사용하면 아래에 설명한 대로 물체 결합을 할 수 없는 상황이 생길 수 있습니다.
모델의 지오메트리(기하 구조, 외형, 외견, 폴리곤)를 최적화하려면 기본적으로 두 가지 해야 할 것이 있습니다.
- 필요하지 않은 경우 삼각형을 더 사용하지 말 것
- UV 매핑 이음새(재봉선, uv가 분리되어 버텍스를 공유하지 못하기 때문에 처리해야 할 데이터가 늘어납니다.)와 심한 모서리(겹쳐있는 정점) 수를 가능한 한 적게 유지하도록 노력할 것
그래픽 하드웨어에서 처리하는 실제 정점 수는 삼차원 모델링 프로그램에서 알려주는 수와 대개 같지 않다는 것을 주목하세요. 모델링 프로그램은 보통 기하학적 정점 수, 즉 모델을 구성하는 꼭짓점 수를 표시합니다. 하지만 그래픽 카드는 렌더링을 위해 어떤 기하학적 정점을 두 개나 그 이상의 실제 정점으로 분리해야 할 때가 있습니다. 정점이 다중 법선, 다중 UV 좌표나 다중 정점 색상을 가지고 있다면 반드시 분리해야 합니다. 그 결과 유니티에서 표시되는 정점 수는 언제나 삼차원 프로그램이 알려주는 것보다 많습니다.
모델의 지오메트리 양은 GPU와 관련이 가장 많지만, CPU에서 모델을 처리하는 메시 스키닝과 같은 유니티의 몇몇 기능도 관련이 있습니다.
전혀 계산하지 않는 조명이 항상 가장 빠릅니다! (당연한 거 아닌가?!) 프레임마다 계산하는 대신 정적 조명을 딱 한 번 “굽는” 라이트맵을 사용해보세요. 유니티에서는 단순히 장면(scene) 안에 조명을 설치하는 것보다 라이트맵이 적용된 주변 환경(지형, 배경 등)을 생성하는 과정이 살짝 할 게 더 많지만,
- 이렇게 하면 더 많이 빨라집니다. (픽셀당 조명 2개의 경우 2~3배 정도)
- 전역 조명을 구울 수 있고 라이트매퍼가 결과물을 부드럽게 할 수 있으므로 시각적으로 더 많이 좋아집니다.
대개 곳곳에 조명을 더 추가하는 대신 셰이더와 콘텐츠로 간단하게 속임수가 가능합니다. 예를 들어 “뒷조명(rim lighting, 역광)” 효과를 얻기 위해 카메라 쪽을 곧게 비추는 조명을 추가하는 대신 전용 “뒷조명” 계산을 셰이더를 안에 직접 추가하는 방법을 고려해보세요.
픽셀당 동적 조명은 영향을 받는 모든 픽셀에 큰(significant) 렌더링 부담(overhead)를 추가로 주고 물체들을 다중 패스 렌더링을 하게 합니다. 모바일과 최저사양 PC의 GPU처럼 사양이 낮은 기기에서는 한 개의 물체에 하나 보다 많은 픽셀 조명이 비추는 것을 피하고, 정적 물체를 밝히기 위해 조명 계산을 모든 프레임마다 하는 대신 라이트맵을 사용해보세요. 정점당 동적 조명은 정점 변형 시 큰 비용이 추가될 수 있습니다. 어떤 물체든지 여러 조명이 비추는 상황을 피하려고 노력하세요.
만약 픽셀 조명을 사용하고 있다면 각각의 메시는 픽셀 조명이 메시를 비추는 만큼 여러 번 렌더링 될 것입니다. 아주 멀리 떨어진 두 메시를 합쳤다면 합쳐진 물체의 영향을 받는 크기는 증가할 것입니다. 이 합쳐진 물체를 비추는 모든 픽셀 조명은 렌더링 중 (조명 빛이 닿지 않는 멀리 떨어진 부분도) 계산에 들어가기 때문에 렌더링해야 할 렌더링 패스 수가 증가할 것입니다. 일반적으로 합쳐진 물체를 반드시 렌더해야 되는 패스 수는 (합치게 될) 각 개별적 물체의 패스 수의 합이기 때문에 물체를 합쳐서 추가되는 것은 없습니다. 이런 이유로 다른 위치에 있는 픽셀 조명의 영향을 받게 될 너무 멀리 떨어진 메시들을 합쳐서는 안 됩니다.
렌더링 중, 유니티는 메시를 둘러쌓고 있는 모든 조명을 찾고 이중 가장 영향을 주는 조명을 계산합니다. 얼마나 많은 조명이 픽셀 조명과 정점 조명이 될 건지 조정할 때 품질 설정을 사용합니다. 각각의 조명은 메시로부터 얼마나 멀리 떨어져 있는지에 기반을 둔 중요도와 빛이 얼마나 강할지를 계산합니다. 그뿐만 아니라 몇몇 조명은 순전히 게임 맥락(개발자의 의도)에 따라 다른 조명보다 더욱 중요하므로 모든 조명은 Important또는 Not Important를 설정할 수 있는 표현 방법(Render Mode) 설정을 가지고 있고 조명이 Not Important로 설정된 조명은 일반적으로 낮은 간접 처리를 하게(갖게) 됩니다.
예를 들어, 플레이어의 자동차가 전조등을 켜고 어둠 속에서 달리고 있는 자동차 게임을 생각해보세요. 전조등은 게임에서 시각적으로 가장 중요한 조명이기 때문에 표현 방법을 아마도 Important로 설정할 것입니다. 반면에 게임의 (다른 자동차의 후면등 같은) 다른 조명들은 덜 중요하고 픽셀 조명의 시각 효과 향상이 없을 것입니다. 이런 조명들이 작은 이익을 줄 수 있는 곳에서 렌더링 비용이 버려지는 것을 피하고자 렌더 모드를 안전하게 Not Important로 설정할 수 있습니다.
픽셀당 조명 최적화는 CPU와 GPU 양쪽 모두 자원을 절약하게 합니다. CPU는 draw call이 적게끔 하고 GPU는 처리할 정점이 적게끔 하며 이 모든 추가적인 렌더링의 레스터라이즈될 픽셀을 적게 만듭니다.
압축된 텍스처를 사용하면 텍스처의 크기를 줄일 수(그 결과 로딩 시간이 더 짧아지고 메모리가 차지하는 공간이 더욱 작아집니다.) 있고 렌더링 성능을 극적으로 올릴 수 있습니다. 압축된 텍스처는 압축되지 않은 32bit RGBA 텍스처가 필요로 하는 메모리 대역폭의 일부만 사용됩니다.
(mipmaps 또는 MIP maps, 앞에 있는 MIP는 라틴어 multum in parvo에서 온 말로 작으면서 효율적(much in little)이라는 뜻이 있다네요…그렇다고요 ;ㅁ;)
경험상 말하자면, 삼차원 장면에서 사용되는 텍스처는 밉 맵 생성하기(Generate Mip Maps)를 항상 켜두세요. 같은 식으로 텍스처 압축도 적용하면 GPU가 렌더링 중일 때 전송되는 텍스처 데이터양을 제한하는 데 도움이 됩니다. 밉 맵이 적용된 텍스처는 GPU가 작은 삼각형에 저해상도 텍스처에 사용할 수 있게 합니다.
위 내용의 한 가지 예외는 텍셀(텍스처 픽셀)이 2D 게임상이나 UI 요소로써 화면 픽셀에 1:1로 적용되어 렌더할 때입니다.
몇몇 게임에서는 CPU와 GPU가 사용되는 것을 줄이기 위해 큰 것보다 작은 물체를 더욱 공격적으로 배제(cull)하는 것이 적절할 수 있습니다. 예를 들어서 먼 거리에서 큰 건물이 아직 보일 때 작은 돌과 파편은 (화면 픽셀보다 작아져서) 보이지 않을 수 있습니다.
위 내용은 Level Of Detail 기능(하지만 난 베이직이지.)을 사용하거나 카메라의 레이어당 배제 거리를 직접 설정해서 적용 할 수 있습니다. 작은 물체를 개별 레이어에 넣고 Camera.layerCullDistances 스크립트 함수를 사용해서 레이어당 배제 거리를 설정할 수 있습니다.
실시간 그림자는 멋지긴 하지만 CPU에는 draw calls를 추가하고 GPU에는 추가 처리를 하게 하여 성능에 상당한 비용이 들게 합니다. 더 상세한 내용은 그림자 항목을 보세요.
최고사양 PC의 GPU와 최저사양 모바일 GPU는 성능이 말 그대로 수백 배 차이가 날 수 있습니다. 단일 플랫폼에서도 마찬가지입니다. PC 상에서 빠른 GPU는 느린 통합형 GPU보다 수십 배 빠르고 모바일 플랫폼에서도 똑같이 GPU 간에 큰 차이를 볼 수 있습니다.
그러므로 모바일 플랫폼과 최저사양 PC의 GPU 성능은 여러분이 개발하고 있는 장비보다 매우 낮을 거란 것을 기억해야 합니다. 보통 셰이더는 좋은 성능을 얻기 위해 계산과 텍스처 읽기를 줄여 손수 최적화해야 합니다. 예를 들어 몇몇 내장(built-in) 유니티 셰이더는 더 빠른(지만 어떤 제약을 받거나 비슷한 효과를 사용하기 때문에 더 빠른) “모바일” 대용 셰이더를 갖고 있습니다.
아래 내용은 모바일과 최저사양 PC의 그래픽 카드에 가장 중요한 몇 가지 지침입니다.
수학의 (pow, exp, log, cos, sin, tan, 등등 같은) 초월함수는 비용이 상당하기 때문에 경험상 볼 때 저런 픽셀당 연산은 하나를 넘으면 안 됩니다. 하나를 넘었다면 대용으로 룩업 텍스처를 사용하는 것을 고려해 보세요.
어떻게 하든 간에 직접 normalize, dot, inversesqrt 연산을 작성하려는 것은 권장하지 않습니다. 내장 함수를 사용하면 드라이버가 더 나은 코드를 만들어 낼 것입니다.
알파를 테스트하는(버리는) 연산은 프라그먼트(픽셀) 셰이더를 느리게 만든다는 것을 기억하세요.
직접 셰이더를 작성할 경우 부동 소수점 변수의 정밀도(precision)를 반드시 적어야 합니다. 최상의 성능을 얻기 위해 가능한 낮은 정밀도의 부동 소수점 형식을 고르는 것은 중요합니다. 연산의 정밀도는 대게 데스크톱 GPU에서는 완전히 무시되지만, 대부분의 모바일 GPU 상에서의 성능에 있어서는 중요합니다.
셰이더가 Cg/HLSL로 작성되어 있다면 정밀도는 다음과 같이 명시되어 있습니다.
- float : 32-bit를 전부 사용하는 부동 소수점 형식으로 정점 변형에 적합하지만, 성능에 있어 가장 느립니다.
- half : 16-bit로 줄인 부동 소수점 형식으로 텍스처 UV 좌표에 적합하고 대략 float보다 두 배정도 빠릅니다.
- fixed : 10-bit 부동 소수점 형식으로 색상, 조명 계산과 다른 고비용 연산에 적합하며 대략 float보다 네 배정도 빠릅니다.
셰이더가 GLSL ES로 작성되어 있다면 부동 소수점 정밀도는 각각 highp, mediump, lowp로 명시되어 있습니다.
셰이더 성능에 대해 더 자세한 내용은 셰이더 성능 항목을 읽어보세요.
- PC 게임을 만들고 있다면 목표로 하고 있는 GPU에 맞춰 정점 수를 프레임당 2십만에서 3백만 아래로 유지하세요.
- 내장 셰이더를 사용하고 있다면 모바일이나 Unlit 범주에 있는 것을 고르세요. 이 셰이더들은 기존 복잡한 셰이더를 간략화하고 비슷한 효과를 내게 만든 형태로써 모바일이 아닌 플랫폼에서도 잘 동작합니다.
- 장면 하나당 재질 종류 수를 적게 유지하세요. 다른 물체끼리 재질을 가능한 한 많이 공유하세요.
- 움직이지 않는 물체에 Static 속성을 적용해서 Static batching 같은 내부 최적화를 할 수 있게 하세요. (하지만 난 베이직이지.)
- 필요하지 않다면 픽셀 조명을 사용하지 말고 지오메트리에 영향을 주는 픽셀 조명(될 수 있으면 방향 조명)을 하나만 있게끔 하세요.
- 필요하지 않다면 동적 조명을 사용하지 말고 대신 라이트맵을 사용하세요.
- 가능하다면 압축된 텍스처 형식을 사용하세요. 사용할 수 없다면 32bit보다 16bit를 주로 사용해보세요.
- 필요하지 않다면 안개를 사용하지 마세요.
- 차폐 배제(Occlusion Culling)의 이점을 알고 차폐가 많은 복잡한(물체가 많은) 정적 장면의 경우 보이는 지오메트리와 draw-calls 양을 낮추기 위해 차폐 배제를 사용하세요. 차폐 배제로 인한 이점에 맞춰 레벨을 제작하세요. (하지만 난 베이직이지.)
- “가짜” 원거리 지오메트리를 표현하기 위해 스카이박스를 사용하세요.
- 여러 텍스처를 섞기 위해 다중 패스 방식 대신 픽셀 셰이더나 텍스처 합성기를 사용하세요.
- 직접 셰이더를 작성하고 있다면 가능한 항상 가장 정밀도가 낮은 부동 소수점 형식을 사용하세요.
- fixed / lowp : 색상, 조명 정보와 법선용.
- half / mediump : 텍스처 UV 좌표용.
- float / highp : 픽셀 셰이더에서는 피할 것. 정점 셰이더에서 위치 계산용으로 사용하는 것이 좋습니다.
- 픽셀 셰이더에서 pow, sin, cos 등등과 같은 복잡한 수학 연산 사용을 최소화하세요.
- 프래그먼트(화면 픽셀)당 텍스처를 적게 사용하세요.
로딩 페이지 및 로딩 프로그래스바 사용하기. (0) | 2014.04.14 |
---|---|
Unite Korea 2014 후기. by 임사장님 (0) | 2014.04.11 |
유니티 관련 3DS Max 에서 작업시 유의사항 (0) | 2014.04.02 |
How to use Visual Studio 2013 at Unity3D (0) | 2014.03.21 |
Project 탭의 오브젝트를 스크립트상에서 Selection 하고 싶을 때.. (0) | 2014.03.19 |
Unite Korea 2014 후기. by 임사장님 (0) | 2014.04.11 |
---|---|
유니티 – 그래픽 성능 최적화 (0) | 2014.04.02 |
How to use Visual Studio 2013 at Unity3D (0) | 2014.03.21 |
Project 탭의 오브젝트를 스크립트상에서 Selection 하고 싶을 때.. (0) | 2014.03.19 |
유니티 Profiler 안드로이드 사용하기 (0) | 2014.02.27 |