Jenkins 및 기타 등등으로 자동 빌드를 하기 위해서 필수적으로 들어가는 Command Line

Unity에서도 Command Line을 지원해주고 있습니다.


근데 이걸 어느 컬럼에 써야 될지 난감하긴 하네요.... 주된 내용이 C#이니 그리고 활용은 Unity뿐만 아니라 Command Line에서 사용 가능하니 C# 컬럼에서 작성하겠습니다.


기본적으로 Unity에서 Command Line을 지원하는데 그에 대한 정보는 

https://docs.unity3d.com/kr/current/Manual/CommandLineArguments.html 에 참조하시면 됩니다.


Jenkins에서는 기본적으로 plugin을 제공해주고 있고, 이부분은 다음에 설명을 하도록 하고 대충 사용 방법을 설명하면 이렇게 사용합니다.

 - Mac 기준

{유니티설치경로}/Unity.app/Contents/MacOS/Unity -quit -batchMode - buildTarget Android -projectPath 프로젝트경로 -excuteMethod 클래스명.함수명


프로젝트 경로가 Users/Dosinamja/project 이고 사용하는 함수가 AutoBuilder.BuildAndroid라고 했을 경우에는 다음과 같습니다.


/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchMode -buildTarget Android -projectPath Users/Dosinamja/project -excuteMethod AutoBuilder.BuildAndroid


대충 이런식으로 사용됩니다.


이렇게 하면 Command Line 으로 빌드를 수행할수 있긴 하지만 사용하다가 보면 명령인자를 넘겨줘서 처리하고 싶을 경우가 많이 생깁니다. 그렇다고 매번 소스를 수정한 뒤에 빌드를 수행하기에는 매우 번거롭고 생산적이지 못하지요.


그래서 실질적으로 arguments 를 넘겨줘서 원하는 작업을 수행하기 위해서 작업이 필요합니다.

기본적으로 명령인자를  parsing 하여 우리가 원하는 값들을 뽑아내서 사용한다고 생각하면 된됩니다.


기본적인 코드는 GitHub에 올라와 있습니다.( 물론 제가 만든것은 아닙니다. )

https://github.com/EpixCode/CommandLineCustomArguments/blob/master/Assets/Scripts/Com/EpixCode/Util/CommandLineReader.cs#L76


#region Using

using System;

using System.Collections.Generic;

using System.Linq;

using UnityEngine;

#endregion


public class CommandLineReader

{

    //Config

    private const string CUSTOM_ARGS_PREFIX = "-CustomArgs:";

    private const char CUSTOM_ARGS_SEPARATOR = ';';


    public static string[] GetCommandLineArgs()

    {

        return Environment.GetCommandLineArgs();

    }


    public static string GetCommandLine()

    {

        string[] args = GetCommandLineArgs();

        if (args.Length > 0)

        {

            return string.Join(" ", args);

        }

        else

        {

            Debug.LogError("CommandLineReader.cs - GetCommandLine() - Can't find any command line arguments!");

            return "";

        }

    }


    public static Dictionary<string,string> GetCustomArguments()

    {

        Dictionary<string, string> customArgsDict = new Dictionary<string, string>();

        string[] commandLineArgs = GetCommandLineArgs();

        string[] customArgs;

        string[] customArgBuffer;

        string customArgsStr = "";


        try

        {

            customArgsStr = commandLineArgs.Where(row => row.Contains(CUSTOM_ARGS_PREFIX)).Single();

        }

        catch (Exception e)

        {

            Debug.LogError("CommandLineReader.cs - GetCustomArguments() - Can't retrieve any custom arguments in the command line [" + commandLineArgs + "]. Exception: " + e);

            return customArgsDict;

        }


        customArgsStr = customArgsStr.Replace(CUSTOM_ARGS_PREFIX, "");

        customArgs = customArgsStr.Split(CUSTOM_ARGS_SEPARATOR);


        foreach (string customArg in customArgs)

        {

            customArgBuffer = customArg.Split('=');

            if (customArgBuffer.Length == 2)

            {

                customArgsDict.Add(customArgBuffer[0], customArgBuffer[1]);

            }

            else

            {

                Debug.LogWarning("CommandLineReader.cs - GetCustomArguments() - The custom argument [" + customArg + "] seem to be malformed.");

            }

        }


        return customArgsDict;

    }


    public static string GetCustomArgument(string argumentName)

    {

        Dictionary<string, string> customArgsDict = GetCustomArguments();


        if (customArgsDict.ContainsKey(argumentName))

        {

            return customArgsDict[argumentName];

        }

        else

        {

            Debug.LogError("CommandLineReader.cs - GetCustomArgument() - Can't retrieve any custom argument named [" + argumentName + "] in the command line [" + GetCommandLine() + "].");

            return "";

        }

    }

}



사용방법은 다음과 같습니다.



/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchMode -buildTarget Android -projectPath Users/Dosinamja/project -excuteMethod AutoBuilder.BuildAndroid -CustomArgs:Language=en_US;Version=1.02


이런식으로 인자를 붙여서 CommandLine 을 사용하면 됩니다.


.cs파일에서는 


public class AutoBuilder

{

static void BuildAndroid

{

string language = string.Empty;

language = CommandLineReader.GetCustomArgument("Language");

if( String.IsNullOrEmpty(language) == false && language == "en_US" )

{

// 해당 코드 수행.

}


string version = string.Empty;

version = CommandLineReader.GetCustomArgument("Version");

if( String.IsNullOrEmpty(version) == false && version == "1.02" )

{

// 해당 코드 수행.

}

}

}


이런식으로 작업하면 됩니다.

또 다른 방법으로는 매번 CustomArgs: 를 설정하기 싫으시면 BuildAndroid()함수를 호출하기 전에 명령인자를 죄다 저장한뒤에 특정 문자열 키를 저장하고 그 해당 키값을 가져쓰는 방식으로 사용하면 됩니다.


static void BuildAndroid

{

InitCommandLineArgs();

}


static private void InitCommandLineArgs()

{

string[] commandArgs;

commandArgs = Environment.GetCommandLineArgs();


for( int i = 0; i < commandArgs.Length; ++i )

{

// 명령인자 매개변수 앞에 특정 문자열을 파싱해서

// 해당하는 매개변수 명을 키값으로 하는 Dictionary 설정.

// 나중에 Dictionary에서 특정 키값으로 하는 값을 가져옴.

}

}


이렇게 사용하면 가능합니다. CommandLine을 좀 더 유연하게 사용하여 원하시는 작업 하시길 바랍니다.

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

partial classes  (0) 2016.11.21
JsonFx 파일 가독성 높게 저장하기  (0) 2016.11.21

원문 http://cafe.naver.com/graphicsprogramming/6


근데 사이트가 없어짐....


0. 들어가며

 

Shader X2 를 살펴 보기 이전에 쉐이더가 무엇인지에 대해서 살펴 보고 가야 할 것 같습니다.

 

물론 책을 보는 것이 더 빠를 수도 있겠지만 저같은 허접이 한 번 정리해 보는 것도 쉽게 접근할 수 있는 방법이 되지 않을까 생각합니다.

 

1. Fixed Pipeline

 

보통 게임 프로그래밍을 한다고 하면 C/C++ 에 대해서 공부하고, Win32 나 MFC 응용프로그램을 작성하고, 그리고 나서 OpenGL 이나 Direct3D 같은 라이브러리를 응용프로그램에 통합하여 사용하게 됩니다.

 

Direct3D 튜토리얼이나 입문 서적들을 보면서 장치, 정점 버퍼, 인덱스 버퍼, 텍스처, 변환, 광원, 재질 등에 대해서 공부하게 됩니다. 제일 간단한 형태가 아래와 같은 구현이겠죠.

 

    장치 생성

    정점 버퍼 생성

    인덱스 버퍼 생성

    텍스처 생성

 

    Clear()

    BeginScene()

        각종 렌더 스테이트 설정

        정점 스트림 설정

        인덱스 스트림 설정

        광원 설정

        재질 설정

        텍스처 설정

        각종 텍스처 스테이지 스테이트 설정

        각종 샘플러 스테이트 설정

        텍스처 설정

        뷰행렬 설정

        투영행렬 설정

        월드 행렬 설정

        DrawXXX()

    EndScene()

    Present()

 

    텍스처 언로드

    인덱스 버퍼 언로드

    정점 버퍼 언로드

    장치 언로드

 

뭐 빠진 부분도 있겠지만 BeginScene() 과 EndScene() 사이에 여러 가지 상태를 설정하고 Draw() 한 다음에 장치에 제출(Present)하게 됩니다. 이러한 일련의 과정들을 수행하기 위해서 Direct3D9 는 여러 가지 함수들을 제공합니다.

 

하지만 자세히 살펴 보면 이러한 여러 가지 작업들은 자료(Data) 와 연산(Operation) 으로 구분할 수 있다는 것을 알 수 있습니다.

 

SetXXX() 를 통해 설정하는 광원, 재질, 텍스처, 정점, 인덱스, 행렬 등은 응용프로그램에서 준비한 자료라 할 수 있고, 각종 SetXXXState() 메서드는 실제 그래픽 카드 내에서의 연산 방식을 설정하는 것이라 할 수 있습니다. 물론 이 연산에는 조건이라는 부분도 포함이 됩니다.

 

예를 들어 하나의 메시(Mesh) 를 렌더링한다고 합시다. 메시는 Max 등의 모델링 툴을 통해서 생성되거나 프로그래머가 직접 정점 위치를 설정하여 만들어 낼 수 있습니다. 보통 메시 자체의 공간인 오브젝트 공간을 가지고 있으며, 이를 3D 월드 내에서 표현하기 위해서는 월드 변환을 수행해 주어야 합니다. 그 다음으로는 보이지 않는 데이터들에 대한 처리 비용을 줄이기 위해서 뷰 공간으로 이동하고, 최종적으로 화면에 투영하기 위해서 절단 공간으로 이동하게 됩니다.

 

이러한 과정들을 처리하기 위해서 우리는 아래와 같이 간단하게 행렬들을 설정합니다.

 

    m_pD3DDevice->SetTransform( D3DTS_WORLD, &mWorld );      // 월드 행렬

    m_pD3DDevice->SetTransform( D3DTS_VIEW, &mView );          // 뷰 행렬

    m_pD3DDevice->SetTransform( D3DTS_PROJECTION, &mProj); // 투영 행렬

 

내부적으로는 어떤 식으로 처리되든지 우리는 개념적으로 파악하고 있기만 하면 되고 모든 계산은 라이브러리 및 GPU 가 알아서 해 줍니다.

 

안개를 설정할 때도 마찬가지로 자료와 연산의 종류만 설정해 주면 됩니다. 안개 이론이 어쩌고 저쩌고 알지 못해도 간단하게 몇 줄 호출함으로써 이를 구현하는 것이 가능합니다.

 

    float Start = 0.5f,
          End   = 0.8f;
 
    g_pDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
 
    g_pDevice->SetRenderState(D3DRS_FOGCOLOR, Color);
    
    g_pDevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
    g_pDevice->SetRenderState(D3DRS_FOGSTART, *(DWORD *)(&Start));
    g_pDevice->SetRenderState(D3DRS_FOGEND,   *(DWORD *)(&End));

 

D3DRS_FOGENABLE 에서 안개를 렌더링하라고 조건을 설정한 것이며,

D3DRS_FOGCOLOR 는 안개 색상을 설정하고,

D3DRS_FOGVERTEXMODE 는 안개 연산을 위한 알고리즘을 설정하고,

D3DRS_FOGSTART 와 D3DRS_FOGEND 는 안개의 시작 및 종료를 설정합니다.

 

매우 간단하지 않습니까. 안개를 렌더링하는 알고리즘은 D3DFOG_EXP, D3DFOG_EXP2 등으로 간단히 대체될 수도 있습니다.

 

우리는 단순하게 FOG 를 사용할 것임을 설정하고 관련 자료 및 연산을 "미리 정해진 형식"으로 설정하기만 하면 됩니다.

 

이렇게 "미리" 정해진 연산을 수행할 수 있도록 제공되는 기능이 바로 고정함수 파이프라인(Fixed Pipeline)입니다.

 

2. Shader, Programmable Pipeline

 

고정함수 파이프라인을 이용해서 왠만한 것들을 표현하는 것이 가능합니다. 하지만 시간이 지날 수록 사람들은 고정함수 파이프라인이 제공하지 않는 다른 특별한 표현을 하고자 하는 욕망을 품게 되었으며, 고정 함수 파이프라인의 성능에 대한 의구심을 품게 되었습니다.

 

예를 들면 "D3DFOG_LINEAR, D3DFOG_EXP, D3DFOG_EXP2 가 아니라 Volume Fog 를 사용하고 싶어요" 같은 요구가 생겨나게 된 것입니다. D3D 가 고정적으로 지원하는 기능이 아니기 때문에 사용자가 직접 구현해야 할 필요가 생긴 것입니다.

 

이러한 요구를 받아 들여 하드웨어 제조 업체와 마이크로 소프트는 Vertex Shader 와 Pixel Shader 라는 것을 발표하게 됩니다. 이 쉐이더를 사용하는 일련의 작업들은 하드웨어 상에서 처리되는 작업들에 대한 코드를 프로그래머가 직접 조작할 수 있다는 의미에서 프로그래밍 가능한 파이프라인(programmable pipeline) 이라고 불립니다.

 

Vertex Shader 는 정점 처리에 대한 부분이고 Pixel 쉐이더는 픽셀 처리에 대한 부분입니다. Pixel 쉐이더는 fragment 쉐이더라고 불리기도 하는데, 개인적으로는 프래그먼트 쉐이더가 더 정확한 의미를 전달한다고 생각합니다. 일반적으로 픽셀이라는 것은 화면에 찍히는 점 하나를 의미합니다. 하지만 프래그먼트는 그 점이 계산하기 위해서 필요로 하는 여러 가지 정보들을 모두 포함하는 개념입니다.

 

여기에서 우리가 눈치챌 수 있는 사실은 어떤 자료를 가지고 작업을 하느냐에 따라서 XXX Shader 라는 이름이 붙는다는 것입니다. 물론 자료에 따라서 수행할 수 있는 연산들은 다르겠죠. 이 시점에서 우리는 Direct3D 아키텍처를 살펴볼 필요가 있습니다.

 

 
Vertex Data 와 Primitive Data 는 Tessellation 을 거쳐, Vertex processing, Geometry Processing, Pixel Processing, Pixel Rendering 의 과정을 밟습니다. 이것을 보통 그래픽 파이프라인이라고 부르는 거죠.
 
만약 우리가 Tessellation 에 개입할 수 있다면 ( 프로그램 코드를 작성하여 원하는 대로 할 수 있다는 의미입니다 ), Tessellation Shader 라고 부르겠죠.
 
Direct3D 가 Vertex Shader 와 Pixel Shader 를 제공한다는 것은 위의 그림에서 Vertex Processing 과 Pixel Processing 부분에 대한 실행 코드를 프로그래머가 작성할 수 있게 한다는 의미입니다.
 
현재 Direct3D 9 에서는 Vertex Shader 와 Pixel Shader 만을 지원하고 있지만 Direct3D 10 부터는 Geometry Shader 와 Rasterize Shader 를 지원한다고 합니다. 즉 하드웨어 상에서 Primitive 단위로 연산을 수행할 수 있다는 의미입니다. 아직 쉐이더에 익숙하지 않으신 분들은 그것이 어떤 의미를 가지는 지에 대해 잘 못 느끼시겠지만, 간단한 예를 들면 "마우스가 올라간 삼각형을 빨갛게 만들어 주세요" 라는 식의 작업을 Geometry Shader 에서 할 수 있다는 것입니다.
 
아래 그림은 Direct3D 10 의 파이프라인 개념도입니다.
 
 
그림만 보고 쉽게 이해하기는 어렵겠지만 VS, GS, RS, PS, 이렇게 네 개의 쉐이더를 지원한다는 것만 알고 계시면 될 것 같습니다. Direct3D9 의 architecture 와는 약간 다른 형태를 띠고 있기는 하지만 큰 부분에서의 차이는 없기 때문에 Direct3D9 architecture 및 쉐이더에 대해서 이해하는 것은 중요하다고 할 수 있습니다.


'프로그래밍 > 3D Programming' 카테고리의 다른 글

[3D수학] 백터 내적의 활용  (0) 2017.12.04
[3D수학] 벡터의 내적  (1) 2017.11.24
[3D수학] 벡터의 덧셈과 뺄셈  (0) 2017.11.09
< Culling의 종류 >  (0) 2016.11.21

+ Recent posts