재귀호출



자기자신을 호출한다.

재귀적으로 문제를 해결함은 또 문제를 점점 더 작은 단위로 쪼개어 해결함을 의미한다.



잘못된 재귀 호출

문제 해결에 변화없는 호출

void rose()

{ rose(); }

무한루프 -> 시스템 내부의 스택에 복귀주소와 인자를 저장해야 하기 때문에

스택에는 자료가 쌓인다.




재귀 호출의 요건

재귀호출이 이루어질 때마다 문제는 점점 작아져야 하고,

재귀 호출이 끝이 나는 종료 조건이 있어야 한다.




1. 거듭제곱을 구하는 재귀 함수


N의 거듭 제곱은 N!이라고 표현한다.


ex) 4를 넣었을때

4 x(3 x(2 x(1 ))) = 24

//0! 되면 재귀함수 탈출




2. 피보나치 수열 구하는 재귀 함수

한 함수 내에서 한번만 이루어져야 하는 것은 아니다.

한 함수 내에서 두 번 혹은 그 이상도 자기자신을 호출할 수 있다.






3. 하노이의탑


세 개의 기둥과 서로 다른 크기의 N개의 원반으로 구성된다. 이 원반들은 세 개의 기둥 중의 하나에 반드시 꽂혀 있어야하며, 자신보다 작은 원반 위에는 그 원반을 놓을 수 없다.

즉 원반은 아래에 가장 큰 것이 와야하며, 자신보다 큰 원반은 자기 위에 놓을 수 없다.


N이 3일때

  1. 기둥 1의 원반을 기둥 3으로 옮긴다

  2. 기둥 1의 원반을 기둥 2로 옮긴다.

  3. 기둥 3의 원반을 기둥 2로 옮긴다.

  4. 기둥 1의 원반을 기둥 3으로 옮긴다.

  5. 기둥 2의 원반을 기둥 1로 옮긴다.

  6. 기둥 2의 원반을 기둥 3으로 옮긴다.

  7. 기둥 1의 원반을 기둥 3으로 옮긴다.


재귀적 표현

  1. 기둥 1에서 N-1개의 원반을 기둥 2로 옮긴다.

  2. 기둥 1에서 1개의 원반을 기둥 3으로 옮긴다.

  3. 기둥 2에서 N-1개의 원반을 기둥 3으로 옮긴다.



원반 n개 , from 기둥에서 to 기둥으로 by기둥을 이용해서 옮기는 과정

           void hanoi(int n, int from ,int by, int to)

           {

               if (n == 1)

               {

                   move(from, to);

               }

               else

               {

                   hanoi(n - 1, from, to, by); //알고리즘 1번

                   move(from, to);        //알고리즘 2번

                   hanoi(n - 1, by, from, to); //알고리즘 3번

               }

           }

           void move(int from, int to)

           {

               Console.WriteLine(string.Format("{0}에서~{1}로 ",from,to));

           }


<결과>





반응형

기초 조명셰이더 난반사광





(게임속에서 직접광은 계산하지만 간접광은 수없이 

반사의 반사를 거치므로 표현하기 어렵다고 한다.)


간접광 : 반사되는 빛

직접광 : 광원으로 직접 받는 빛



난 반사광(Diffuse Light)


우리가 물체를 볼 수 있는 이유는 다른 물체가 비추는 빛이 이 물체를 표면에 반사되기 때문에 볼 수 있는 것이다. 이 것이 난 반사광이라고 한다.


물체를 어느 방향에서 보든 명암이나 색조가 크게 변해 보이지 않는 이유가 여러 방향으로 고르게 퍼지는 난 반사광 덕분이다.


(그림 : https://kblog.popekim.com/2011/12/04-part-1.html)


수학적으로 난 반사광을 어떻게 계산하려면?


lambert모델(게임에서 주로 사용됨)을 살펴보면 

입사광이 이루는 각의 코사인 값을 구하면 난 반사광의 양을 구 할 수 있다.


입사광과 법선의 두개의 각도에 따라 코사인 값은 아래와 같다.

0도 => 1

90도 => 0

90도를 넘는 순가 음수 값을 갖다가

180도에서 -1

음수 값은 아예 깜깜해지기 때문에 값 변경이 필요하다.


 

코사인값을 구하는 수학 공식을 보면

cosθ = (A ∙ B) ÷ (| A |ⅹ| B |);

두 개의 내적을 구한 뒤 두 벡터의 길이를 곱한 결과를 나눈 것과 같다. 


여기서 두 벡터를 1로 정규화한다. (두 벡터가 이루는 각이 중요할뿐 길이는 중요하지 않기 때문에) 

cosθ = (A'  B')


두 벡터가 이루는 각의 코사인 값은 두 벡터의 내적과 같다는 의미이다.


셰이더에서 난 반사광을 계산하려면?


물체 표면 벡터는 정점에 저장되어 있기 때문에 그대로 가져오면 되고 입사광의 벡터는 광원의 위치에서

현재 픽셀 위치까지 긋고 그 그것의 벡터를 가져와야한다.


선을 긋는 것을 벡터의 뺄셈이라고 하는데, 모든 변수의 공간을 일치 시킨 뒤 현재 위치에서 광원의 위치를 빼면 입사광의 벡터를 구할 수 있다. (표면의 법선도 같은 공간에 있어야 함)


난 반사광 범위는 0~1이니까 위에서 코사인값을 0이하의 값은 0으로 1이상의 값을 1로 맞춰 줘야한다.




학습참고 출처 https://kblog.popekim.com/2011/12/04-part-1.html

반응형

하프 램버트



램버트 라이트는 cos그래프 연산의 특성상 밝다가 너무 갑자기 검게 음영이 떨어지는 단점이 있다.

이 단점을 해결하고자 만든것이 하프 램버트이다.



공식

Dot(L,N) *0.5 + 0.5

램버트에 0.5를 곱해 평준화 시킨 다음에 0.5를 더해서 x축으로 들어 올려준다.

즉 -1에서부터 1까지의 숫자를 0에서부터 1까지의 범위로 만듦으로서 좀 더 부드럽게 바꿔준다.


float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)

{

float4 ndot1 = dot(s.Normal, lightDir) *0.5 + 0.5;

return ndot1;

}



오른쪽 하프램버트 공식을 적용한 모델



부드러워졌지만 이 음영은 너무 부드러워서 물리적으로 전혀 옳지 않다.

명이 너무 넓고 암이 너무 좁다. (음영 콘트라스트가 거의 없음 콘트라스트:~대비)



해결방법


하프 램버트를 몇번 제곱해서 콘트라스트를 올려준다.


float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)

{

float4 ndot1 = dot(s.Normal, lightDir) *0.5 + 0.5;

return pow(ndot1,4);

}

pow 함수 : pow(X,n) -> X의 n제곱





위 쉐이더는 빛 방향에 따른 밝기만 구현되어있음


이번에는 조명의 색상과 강도, 빛 감쇠, 텍스처까지 적용 보려한다.



float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)

{

float4 ndot1 = saturate(dot(s.Normal, lightDir));

float4 final;

final.rgb = ndot1 * s.Albedo * _LightColor0.rgb* atten;

final.a = s.Alpha;


return final;

}


return 값이 final로 바뀌었다. 


s.Albedo : 램버트 라이트와 연산되어 Diffuse가 된다.

_LightColor0.rgb : 조명의 색상이나 강도를 가져옴

atten : 빛의 감쇠 현상을 시뮬레이트한다.

final.rgb :  이전에 연산했던 조명과 노멀의 각도인 ndot1연산과 Albedo , _LightColor0.rgb를 곱해 변화를 적용한다.

 

atten은 자기 자신의 그림자를 만들거나, 다른 물체에 때문에 생기는 그림자, 조명의 감쇠 현상(멀어지면 어두워지는 효과)때문에 어두운 부분 이 있다.




학습참고

1) 유니티쉐이더 스타트업 도서

2) https://www.slideshare.net/tartist/ss-56389186


반응형

Package Manager 에러





어느 순간부터 이런 콘솔창에 이런 에러가 났다아마도 패캐지매니터 

업데이트를 꼼꼼히 확인하지 않고 대충 업데이트해서 이런 결과가 난 것같다.



찾아보니 밑에 링크에서 해결법을 찾았다.


https://forum.unity.com/threads/couldnt-find-the-packages-list-be-sure-there-is-a-file-called-packageimportlist-in-your-project.534331/




 Assets / Scripts / Editor / PackageManagerAssembly / PackageManagerAssembly.dll

경로를 들어가본니 이 두파일이 문제인거 같다.


나는 이 두파일을 지웠다.




두 번째 방법


 Help -> Reset Packages to default 눌러서 초기화 해주자

설정사항이 초기화 되니 조심해서




반응형

문제

int형 배열과 정수 k를 입력받는데, 배열 요소에 정수 k를 더 했을 경우 k를 더하지 않은 요소들과 비교해가장 큰 수가 될 경우를 카운팅해서 출력하는 문제이다. 

예를 들어 2,3,5,2 배열이 주어지고 정수 3이 주어지면 첫 번째 요소( 2 )에 정수k인 3을 더하면 5가 되는데 나머지 요소와 비교했을 때 가장 큰 수가 아니기 때문에 카운트하지 않게 된다.

이렇게 차레대로 비교를 하면 되는데 요소 3일 경우와 5일 경우는 정수 k를 더했을 때 요소중 가장 큰 수가 되기 때문에 출력 결과는 2를 출력하면 된다.

조건 : 요소 비교 중 값이 같을 경우는 가장 큰수가 될 수 없다.

 

풀이

처음 생각하기에는 처음 요소를 다른 요소와 비교하면서 가장 크다고 판단되면 카운트해서 그 카운트가 배열의 길이에서 1을 뺀 수와 같으면(자기 자신을 제외한 다른 요소들의 개수) 가장 크다고 판단하여 그 개수를 출력하려고 했지만.. 

위에 경우 처럼 배열의 길이에서 1을 뺀 값을 나를 제외한 나머지 요소들의 개수라고 판단해버리면 배열 요소 중 자신과 같은 값을 갖고 있는 요소가 있게 되면 요소에 k를 더한 후 비교할 때 동점인 경우는 체크하지 않기 때문에 카운트 값이 맞지 않는다.

반대로 생각해보면  k를 더한 요소보다 더하지 않은 요소중에 더 큰 경우가 있다면? 더 비교할 필요도 없이 그 요소는 배열의 요소중 가장 큰 요소라고 할 수 없다. 

if(i!=j && vote[j] >= max) : 자기 자신과의 비교는 제외하고, 배열의 요소중 더 큰 값이 있다면 break 키워드로 반복문을 빠져나온다.

if(cnt ==0 ) : cnt가 0인 경우가 자기 자신이 가장 큰 경우(k를 더했을 때)이기 때문에 cnt가 '0'인 요소일 때 resscent를 증가시키고 최종적으로 반환한다.

cnt를 bool 플래그로 쓴 것이다.

 

arcde46

반응형

'알고리즘자료구조 > 알고리즘문제' 카테고리의 다른 글

03.28_CodeSignal  (0) 2019.03.28
03.27_CodeSignal  (0) 2019.03.27
03.26_CodeSignal  (0) 2019.03.26
02.05_CodeSignal(체스 말판)  (0) 2019.02.05
02.03_CodeSignal(자릿수를 줄인 가장 큰 수)  (0) 2019.02.03

램버트 라이트


커스텀 라이트로 램버트 라이트 구현하기


알아 두어야 할 것


법선 벡터가 조명벡터를 마주볼 때 해당 버텍스는 가장 밝다.


두 벡터의 각도의 따른 내적 값 

0도이면 =>1

90도이면 =>0

180도이면 => -1






램버트 라이트란 


밸브에서 개발했으며 하프라이프에서 처음 사용되었다고 한다.

주변 및 방향 조명을 통합하여 3D장면의 개체를 음영 처리한다.


공식 Dot(L,N) L:라이트벡터, N:법선 벡터(노말 벡터)

법선 벡터가 곱해지면 표면에 굴곡진 라이팅을 생성하게 해주는 방법



float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)

{

float4 ndot1 = dot(s.Normal, lightDir);

return ndot1;

}

dot 함수 : 노멀벡터와 라이트 벡터를 내적 연산


내적 연산한 값은 1~ -1까지 범위의 숫자가 나온다. 

즉, 그림에서 가장 밝은 곳은1, 어두워지는 경계선은 0 아예 어두운 곳은 -1일 것이다.



-1인 부분은 너무 어둡기 때문에 변경이 필요하다.


float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)

{

float4 ndot1 = saturate(dot(s.Normal, lightDir));

return ndot1+0.5;

} 

saturate 함수 : 이 안의 값을 0~1사이로 잘라줌 0보다 작은 값은 0으로 1보다 큰값은 1로

+ 차이를 위해 결과 값에 +0.5를 해준다.



(NormalMap 추가했음)



(아까 더했던 0.5를 지우고)




학습참고 : 

1) https://www.slideshare.net/tartist/ss-56389186

2) 유니티쉐이더 스타트업 도서


반응형

전처리기 / Define


유니티 Define 기능은 각 플랫폼 또는 특정 버전에 코드를 처리할때 유용한 방법이다.



전처리기


멀티플랫폼이 가능한 유니티다 이 말은 하나의 프로젝트로 여러가지 플랫폼으로 빌드가 가능하다는 말이다. 멀티플랫폼이라서 그렇게 간단하게 각기 다른 플래폼 적용이 쉬운것은 아니다. 


안드로이드,IOS,윈도우 각각 플랫폼마다 적용해야 하는 코드가 다를 수 있다. 그러면 플랫폼이 바뀔때마다 스크립트를 각각 별도로 만들어야하고 동적으로 플랫폼을 확인한 후에 컴포넌트를 추가해야 한다.

프로젝트가 커질 수록 작업양은 점점 많아 질 것이다.  



이럴 때 사용하는 것이 전처리기이다.


구문

#if UNITY_EDITOR

Debug.Log("Unity Editor");

#endif


예제)


유니티 에디터에서 각각 다른 플랫폼에서 다른 로그가 나오도록 설정할 수 있는 것이다. 


<유니티에서 지정해놓은 전처리기>



사용자 커스텀 전처리기

유니티가 지정해 놓은 전처리기 말고 사용자가 원하는 전처리기를 만들 수 가 있다.

1.UnityEditor에서 사용자 디파인 셋팅


edit -> Player Settings -> Other Setting -> Scripting Define Symbols




정의하고 싶은 특정 플랫폼의 기호 이름을 입력한다.



Player Settings에는 SHOW_DEBUG_MESSAGES 설정을 했으니


"디파인 테스트2"만 출력된다.






2. using UnityEditor; 필요



특징 컴파일 시 Define여부에 따라 컴파일 자체를 하지 않기 때문에, 실행파일에 코드가 포함되지 않는다.

Define으로 인해 유니티에서 현재 선택되어진 플랫폼이 아닌 다른 플랫폼의 코드들의 스킙트 에러 여부를 바로 확인할 수 가 있다.




학습참고

1) https://docs.unity3d.com/kr/530/Manual/PlatformDependentCompilation.html

2) http://blog.naver.com/PostView.nhn?blogId=hope0510&logNo=220079637714

3) https://bluemeta.tistory.com/12


반응형

서피스 툰 셰이더 #1

 

<툰 쉐이더 적용 전>


We're gonna set a custom lighting function

1) 우리가 설정 가능한 조명기능을 설정해주도록 한다.

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
Shader "Custom/ToonShaderPratice"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
 
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0
 
        sampler2D _MainTex;
        half4 _Color;
 
        half4 LightingToonLighting(SurfaceOutput s, half3 lightDir, half atten) 
        {
 
        }
        struct Input
        {
            float2 uv_MainTex;
        };
        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
cs



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
Shader "Custom/ToonShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
 
        CGPROGRAM
        #pragma surface surf ToonLighting
 
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
 
        sampler2D _MainTex;
        half4 _Color;
 
        half4 LightingToonLighting(SurfaceOutput s, half3 lightDir ,half atten)
        {
            float NdotL = saturate(dot(s.Normal, lightDir))* atten;
            float toonL = step(0.5, NdotL);
            return half4(toonL,toonL,toonL,1);
 
        }
        
        struct Input
        {
            float2 uv_MainTex;
        };
 
 
        void surf(Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex)* _Color;
            //o.Albedo = c.rgb;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
cs



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
Shader "Custom/ToonShader"
{
    Properties
    {
        _LitOffset("Lit Offset",Range(0,1))=0.25
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
 
        CGPROGRAM
        #pragma surface surf ToonLighting
 
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
 
        sampler2D _MainTex;
        half4 _Color;
        half _LitOffset;
 
        half4 LightingToonLighting(SurfaceOutput s, half3 lightDir ,half atten)
        {
            float NdotL = saturate(dot(s.Normal, lightDir))* atten;
            float toonL = step(_LitOffset, NdotL);
            return half4(s.Albedo * toonL,1);
 
        }
        
        struct Input
        {
            float2 uv_MainTex;
        };
 
 
        void surf(Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex)* _Color;
            o.Albedo = c.rgb;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
cs


우리의 음영을 향상시키기 위해 우리는 표면에서 완전히 어두운 부분을 원하지 않는다.

_SSColor 은 "피부" 아래 인물의 색감이라고 생각하다. 그리고 그늘진 부분에서 우리는 알베도를 사용한다.

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
Shader "Custom/ToonShader"
{
    Properties
    {
        _LitOffset("Lit Offset",Range(0,1))=0.25
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _SSSTex("SSS Map",2D) = "black" {}
        _SSSColor("SSS Tint",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
 
        CGPROGRAM
        #pragma surface surf ToonLighting
 
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
 
        sampler2D _MainTex;
        sampler2D _SSSTex;
        half4 _Color;
        half _LitOffset;
        half _SSSColor;
 
        struct CustomSurfaceOutput {
            half3 Albedo;
            half3 Normal;
            half3 Emission;
            half Alpha;
            half3 SSS;
        };
 
        half4 LightingToonLighting(CustomSurfaceOutput s, half3 lightDir ,half atten)
        {
            float NdotL = saturate(dot(s.Normal, lightDir))* atten;
            float toonL = step(_LitOffset, NdotL);
            half3 albedoColor = lerp(s.Albedo * s.SSS, s.Albedo *toonL, toonL);
            return half4(albedoColor,1);
 
        }
        
        struct Input
        {
            float2 uv_MainTex;
        };
 
 
        void surf(Input IN, inout CustomSurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex)* _Color;
            o.Albedo = c.rgb;
            o.SSS = tex2D(_SSSTex, IN.uv_MainTex) * _SSSColor;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
 
cs

당신이 보는 것처럼 우리는 우리가 질감으로 더럽힐 수 있는 그림자에 단정한 어두운 색을 가지고 있다.

그러나 문제는 있다 .만약 우리가 라이트 색을 바꾼다면 그것은 머테리얼에 영향을 미치지 않을 것이다.

간단한 해결책: 단지 밝은 색과 우리의 밝은 알베도를 곱하면 된다.




next problem : look at that It's totally flat even when it should have shadows between the plates

다음 문제: 저것 좀 봐. 접시 사이에 그림자가 있어야 하는데도 완전히 평평해.



We'll paint the vertices of our model to mark the should shaded parts and read that colors in our shader

우리는 우리 모델의 정점을 칠해서 음영처리 된 부분을 표시하고 그 색을 우리의 음영처리 된 부분에 읽을 것이다.



This is how our model looks like when we map the vertx color

vertx 색상의 지도를 만들 때 우리의 모델은 이렇게 보인다.



To fix it we use a fancy rick: Vertex color

그것을 고치기 위해 우리는 화려한 릭을 사용한다


Then mult our lighting by the occlussion given by the vertex and done

그 다음 버텍스에 의해 주어지는 오쿨루젼으로 우리의 조명을 여러 번 혼합하여 완성한다.


To improve it even more, we'll add a third map: Combined Map

한층 더 향상시키려면, 3개의 맵: 결합 맵을 추가한다


We'll map if a pixel it's glossy or not in it's Red channel

우리는 픽셀에 광택이 나는지 아닌지를 빨간 채널에서 매핑할 것이다.


We'll use again the dot product and get the specular lights

우리는 다시 도트 제품을 사용할 것이고, 투시 조명을 받을 것이다.


We'll need the view direction(facing of the camera)

우리는 전망 방향이 필요할 것이다.


With the pow function we'll control the size of our reflection

파워 기능으로 우리는 반사되는 크기를 조절할 것이다.

We add a prop to control the Specular size

우리는 시편 크기를 조절하기 위해 받침대를 추가한다.



Then we apply the light color and the light attenuation to our specular component and mult the Glossy from the CombMap

그리고 나서 우리는 밝은 색과 빛 감쇠를 우리의 시야에 있는 성분과 콤비맵의 여러 가지 광택에 바른다.



Caution: darker it's bigger. The smaller the exponent, the bigger the highlight

주의: 더 어둡다. 지수가 작을수록 하이라이트는 커진다.


Finally, to not waste any memory, let's put at use the Green and Alpha channels of the CombMap

마지막으로, 어떤 기억도 낭비하지 않기 위해 콤비맵의 그린과 알파 채널을 사용하자.


We'll use Green channel to add extra shadows.(Caution: if you don't make this texture correctly shadows could pixelate)

우리는 그린 채널을 사용하여 그림자를 더 추가할 것이다.

(주의: 이 질감을 정확하게 만들지 않으면 그림자가 화소화될 수 있음)


Now, the Alpha channel will be used as an InnerLine mask. Alpha 0 it's a line

이제 알파 채널은 이너라인 마스크로 사용될 것이다. 알파 0 선이다.


COOL TRICK: if you structure a surface shader like this, you can mix regular vertex/fragment passes with surface passes

COOL TRICK: 만약 당신이 표면 하드어를 이렇게 구조화한다면, 당신은 일반 정점/약점 패스와 표면 패스를 혼합할 수 있다.


Very easy trick. We get the normal, and offset the vertex position along it a little bit.. Fragment just returns black

매우 쉬운 속임수 우리는 정상과 정점 위치를 약간 상쇄한다. 파편이 그냥 검은색으로 돌아온다.


PRO TIP : Renormalize the normal, some models have normals whith! =1 magnitide. This could result in inconsistent outlines.

PRO TIP: 정상화를 다시 하고, 어떤 모델들은 표준적인 기동을 가지고 있다! =1자석 이렇게 되면 윤곽이 일관되지 않을 수 있다.


To fix it just cull the front faces. We'll render only the back of the surface

그것을 고치기 위해서 앞면만 도려낸다. 우리는 표면만 다듬을 것이다.



All left to do it's add some props to control the outline color and thickness

남은 것은 윤곽의 색과 두께를 조절하기 위한 소품 몇 가지를 더하는 것이다.

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
Shader "Custom/ToonShader"
{
    Properties
    {
        _LitOffset("Lit Offset",Range(0,1))=0.25
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _SSSTex("SSS Map",2D) = "black" {}
        _SSSColor("SSS Tint",Color) = (1,1,1,1)
        _CombMap("Comb Map",2D) = "white" {}
        _SpecPower("Specular Power",Range(0,100)) =20.0
        _SpecScale("Specular Scale",Range(0,10)) = 1.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
 
        CGPROGRAM
        #pragma surface surf ToonLighting
 
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
 
        sampler2D _MainTex;
        sampler2D _SSSTex;
        sampler2D _CombMap;
        half4 _Color;
        half4 _SSSColor;
        half _LitOffset;
        half _SpecPower;
        half _SpecScale;
 
    
 
 
        struct CustomSurfaceOutput {
            half3 Albedo;
            half3 Normal;
            half3 Emission;
            half Alpha;
            half3 SSS;
            half vertexOc;//
            half Glossy;
            half Glossiness;
            half Shadow;
            half InnerLine;
        };
 
        half4 LightingToonLighting(CustomSurfaceOutput s, half3 lightDir, half viewDir ,half atten)
        {
            float oc = step(0.9, s.vertexOc);
            float NdotL = saturate(dot(s.Normal, lightDir))* atten;
            float toonL = step(_LitOffset, NdotL)*s.Shadow * oc;
            half3 albedoColor = lerp(s.Albedo * s.SSS, s.Albedo* _LightColor0 *toonL, toonL);
            half3 specularColor = saturate(pow(dot(reflect(-lightDir, s.Normal), viewDir),s.Glossiness* _SpecPower))
                                * toonL * _LightColor0 * s.Glossy * _SpecScale;
 
            //return half4(s.Glossy, s.Glossy, s.Glossy);
            return half4( (albedoColor + specularColor)*s.InnerLine,1);
 
        }
        
        struct Input
        {
            float2 uv_MainTex;
            float4 vertColor : COLOR;
        };
 
 
        void surf(Input IN, inout CustomSurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex)* _Color;
            half4 comb = tex2D(_CombMap, IN.uv_MainTex);// *_SSSColor;
            o.Albedo = c.rgb;
            o.SSS = tex2D(_SSSTex, IN.uv_MainTex) * _SSSColor;
            o.vertexOc = IN.vertColor.r; //
            o.Glossy = comb.r;
            o.Glossiness = comb.b;
            o.Shadow = comb.g;
            o.InnerLine = comb.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
cs




학습참고 https://www.youtube.com/watch?v=pMq--FUR5VE

반응형

 PostProcessing 

(Post Processing Stack은 2018년 이전 버전)


이번 학습목표 최종 결과




라이트를 사용하면 보통 라이트가 비추는곳은 빛이 나기때문에 괜찮은데

정작 빛을 내주는 라이트에서는 빛나지가 않다(후광이 없음)

라이트효과 참고 : https://funfunhanblog.tistory.com/94


이유는 라이팅 시스템이 그작업을 해주지 않기 때문이다.

그 작업을 해보려고 한다.


1) Post Processing 다운로드

유니티 Package Manager에서 다운로드 한다.

그런데 아마 유니티 2018년 이전 버전은 방법이 조금 다르다

Post Processing Stack 

에셋 스토어에서 다운로드 해준다.

다운로드 하면 post processing behavior스크립트를 추가 할 수 있다.

프로젝트창에서 Post - Processing Profile을 생성한다.

모든 셋팅값이 저장되는 곳이다.

생성한 Profile을 아까 추가한 스크립트 profile에 넣어준다. 

그런 뒤 값 세팅

참고 :  https://www.youtube.com/watch?v=o39SjRcA__U (14분 부터)


2) 포스트프로세싱 생성

a) 메인카메라에 Post Process Layer 컴포넌트를 추가한다. 

Layer는 volume이 작동할 레어를 설정해주어야한다. 없다면 추가한다.

Trigger는 This를 눌러준다.


b) Post-processing Profile을 생성한다.

모든 셋팅이 저장되는 곳 이다.


C) Post-processing Profile을 생성한다.

Is Global  : 전역으로 효과를 줄건지 체크

Profile : new를 눌러줘서 새로 만들어준다.

Add effect를 눌러서 추가할 셋팅요소들을 추가 한다. 이번에는 빛자체에서 후광 효과를 주기 위해서 Bloom만 추가해줬다. 

처음에 모든 셋팅설정 값들이 회색으로 나와서 안되는 줄 알았는데 저렇게 체크박스를 체크하니까 값을 넣을 수 있게 활성화가 됐다.


학습참고

1) https://github.com/Unity-Technologies/PostProcessing/wiki/Quick-start

2) https://github.com/Unity-Technologies/PostProcessing/issues/725

3) https://docs.unity3d.com/kr/2017.4/Manual/PostProcessing-Stack.html

4) https://www.youtube.com/watch?v=4Qjm4FQyZuE



반응형

struct와 class의 차이점




1) Type차이



구조체는 값 타입(value)

=> 스택메모리에 생성된다.


클래스는 참조 타입(reference)

=> 힙 메모리에 생성된다.


힙과 스택차이점


값타입과 참조타입 관련 예제


구조체는 함수안에서 1로 변경을 해도 외부 Main에서 값이 변경되지 않는다. 복사된 데이터는 원본 데이터가 아니기 때문에 복사된 값을 수정하면, 스택에 복사된 값을 변경하게 되는것이다. 

(복사본에 입력된 값들은 함수가 끝나면 사라짐)


 클래스는 참조타입으로 힙의 주소를 전달하기 때문에 값이 아닌 같은 주소가 참조된다. 

그래서 Main으로 출력했을 때, 값이 변경되지 않고 원본값을 출력한다.

(변경된 데이터들은 함수가 종료되어도 남아있음)




  

2) 클래스와 비슷하면서도 다른 구조체


a. 구조체는 생성자를 선언할 수 있으나 반드시 파라미터가 있어야한다.

b. 구조체는 상속이 불가능하다.

c. 구조체는 필드선언 시 const또는 static으로 선언한 경우에만 초기화가 가능하다.

d. new연산자를 사용하지 않고 인스턴스화 할 수 있다.




메모리절약은 클래스, 속도는 구조체


인스턴스한다면 가비지 컬렉션(속도)

구조체는 스택에 바로 할당되기 때문에 가바지컬렉터션이 발생 하지 않고, 클래스는 인스턴스를 생성할 때 마다 힙에 메모리 할당하기 때문에 값을 폐기하기 위해서는 가바지컬렉션이 필요하다. 


많은 양의 변수를 가지고 있는 구조체는 NO(메모리)

참조형식인 클래스는 값들을 가리키는 주소만 스택에 저장하지만 구조체는 가지고있는 변수들의 값들을 모두 스택에 저장되기 때문에 그 크기만큼 스택의 위치 역시 커지게 된다. 하지만 스택은 크기가 제한적이기 때문에 너무 많은 양을 가지게 되면 스택 오버플로우가 발생할 수 있는 위험이 있다. 


구조체는 언제 사용할까?


상황에 맞게 사용해야 하는 구조체

변수의 크기가 작거나, 수명이 짧고, 자주 할당되는 객체는 구조체로 만들어 주는게 좋다.

유니티에서는 position, color, quaternion, rotation, scale등이 구조체로 구현 되어있다.



학습참고

1) https://vaert.tistory.com/111

2) https://m.blog.naver.com/PostView.nhn?blogId=wow0815&logNo=90184259071&proxyReferer=https%3A%2F%2Fwww.google.com%2F

3) https://nowonbun.tistory.com/84 

4) https://ronniej.sfuh.tk/c-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EC%B2%B4-vs-%ED%81%B4%EB%9E%98%EC%8A%A4/


struct

단순 데이터 컨테이터용으로 50개의 struct를 사용해봅시다.


struct는 메모리를 많이 사용합니다.


A->B->C 로 값을 전달하면, 총 150 개의 struct 가 생성되게 됩니다.


레퍼런스와 관련된 처리가 없으므로 더 빠를 수 있습니다.


구조체는 스택에 할당되고, 자신을 호출한 함수가 종료되면 즉시 사라집니다.

GC를 유발하지 않고 빠르게 동작합니다.


오히려 더 느려질 수도 있습니다.


단일 struct 오브젝트의 크기가 너무 커지거나, 너무 긴 리스트에 넣고 쓰면, 복사하는 시간이 길어집니다. 이때 class 형보다 처리 시간이 더 길어질 수 있습니다.


변수가 2~3개 뿐인 단순한 데이터 컨테이너라면 struct 를 사용하는게 빠릅니다.

일반적인 경우에는 class 를 쓰세요.

반응형

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

C#) 키워드 간단 정리  (0) 2019.09.24
C#) List / LinkedList (List.AddRange)  (0) 2019.06.05
C#) 쓰레드(Thread), 프로세스  (0) 2019.02.22
C#) 리플렉션 Reflection  (0) 2019.02.20
C#) Action과 Func (람다식)  (0) 2019.02.16

서피스 셰이더



서피스 셰이더 동작원리


먼저 3D모델이 3D모델 지오메트리를 변경할 수 있는 어떤 함수로 넘겨진다. 

그런 다음, 몇몇 직관적인 속성을 사용하여 겉모습을 정의하는 어떤 함수로 넘겨진다.


마지막으로, 이 속성들은 지오메트리가 근처의 광원들에 의해 어떻게 영향을 받을지를 결정하는 라이팅 모델에 의해 사용된다. (각 모델의 픽셀들의 RGB색상으로 나타내짐)


서피스 함수


서피스 함수는 3D모델의 데이터를 인풋으로 가져와서, 결과값으로 렌더링 프로퍼티들을 반환한다. 

아래의 서피스 셰이더는 오브젝트를 흰색 diffuse를 갖는 오브젝트로 렌더링한다.



#pragma surface : 해당 셰이더를 위한 서피스 함수이름은 surf이고 라이팅 모델로는 램버시안 모델을 사용할 것이다 라는 뜻이다.


o.Albedo = 1; : 머티리얼의 알베도 즉, 재질 기본색상이 흰색이라는 것을 나타낸다.


최초의 3D모델로부터의 어떠한 데이터도 사용하지 않지만,

그럼에도 인풋 구조체를 정의하는 형식이 필요하다.




서피스 아웃풋


SurfaceOutput구조체 안에는 머티리얼의 최종 모습을 결정하기 위해 사용되는 여러가지 프로퍼티들을 포함하고 있다.


fixed3 Albedo : 기본색상

fixed3 Normal : 반사각을 결정하는 면의 방향

fixed3 Emission : 이 오브젝트가 스스로 생성하는 빛의 양

half Specular : 머티리얼이 빛을 반사하는 정도(0~1)

fixed Gloss : 스펙큘러 반사가 퍼지는 정도

fixed Alpha : 머티리얼의 투명한 정도


Cg/HLSL은 전통적인 float타입을 지원한다. 하지만 보통 32비트의 정밀도는 거의 필요하지 않을 것이고,

보통 16비트 정도면 충분하기 떄문에 half타입을 주로 사용하게 될 것이다.



텍스처 샘플링


텍스처를 붙이는것을 배우기전에 텍스처 매핑이 어떻게 일어나는지 알아보자

텍스처를 입힐 모델은 여러 개의 삼각형들로 만들어져있고, 각각의 삼각형은 3개의 버텍스로 구성된다.

데이터(UV와 색상)는 이러한 각 버텍스들 안에 저장된다.


삼각형의 버텍스들을 통해 텍스처 상에 맵핑되는 0에서 1사이의 정규값을 가져 올 수 있는데 이것이 텍스처 UV좌표이다.

즉, 3D오브젝트에 텍스처를 매핑하려면, 각 버텍스의 UV좌표가 필요하다.




_MainTex라는 이름으로 텍스처 프로퍼티를 선언했는데 이 프로퍼티는 머티리얼

인스펙터를 통해 접근가능하도록 처리가 되도록 한 것이다.

<쉐이더를 적용한 머티리얼 인스펙터창>


현재 픽셀의 UV데이터는 float2 uv_MainTex에서 추출된다.


tex2D는 텍스처와 UV값을 넘기면, 이에 해당하는 RGB색상으로 반환한다.

tex2D함수는 텍스처를 임포팅할 때, Unity에 의해 직접 설정될 수 있는 다른 파라미터들을

계산에 고려하게 된다.



서피스 인풋

서피스 인풋인 Input의 필드들을 Unity가 계산 할 값들로 채울 수 있다.

예를 들어, float3 worldPos를 추가함으로써 surf에서의 해당 점에 대한 월드 포지션을 가지고 초기화 되는데

이것은 특정 점으로부터의 거리에 의존적인 이펙트를 생성하는데 사용된다.


그 밖의 다른 인풋들

- float3 viewDor : 카메라의 방향(뷰 방향)

- float4 name : COLOR : name변수에 버텍스 색상을 포함한다.

- float4 screenPos : 스크린 좌표 상의 해당 픽셀의 위치

- float3 worldPos : 해당 픽셀에 대한 월드 좌표 상의 위치



버텍스 함수


surf함수로 보내기 전에 이들을 수정/변경해준다. 

surf함수가 RGBA공간 안에서 색상을 다루는 반면, 공간에의 3D점을 제어하기 위해서는 버텍스 모디파이어 사용방법을 알아야한다. 


3d모델을 원래의 모습보다 좀 더 통통하게 만드는 셰이더를 예제로 만들어보자

이렇게 만드려면 모델의 삼각형을 삼각형면이 향하는 방향을 따라서 확장시키면 된다. 

삼각형면이 향하는 방향은 결국 노멀값으로 나타내며, 삼각형 자신의 포면에 수직인 단위 벡터이다.

그렇다면, 이제 다음과 같이 노멀 방향으로 버텍스를 확장하려면 다음과 같은 수식을 사용할 수 있다.



#pragma surface surf Lambert vertex:vert : vert라는 이름을 갖는 버텍스 모디파이어를 설정한다.


실제 vert함수 코드를 보면 ,(v.vertex.xyz += v.normal * _Amount; )

버텍스 위치를 받아서 노멀을 따라 해당 버텍스를 밀어낸다.


appdata_full 구조체는 해당 현재 버텍스의 모든 데이터를 포함하고 있다. 


<결과 화면>



눈 쉐이더


surf함수와 vert함수 이 두가지를 사용하는 snow이펙트가 있다.

어떤 모델의 삼각형 위에 눈이 쌓이는 것을 시뮬레이션하는 것이다.

처음에는 _SnowDirection을 직접 마주하는 삼각형들에만 적용되다가, _Snow가 증가함에 따라서

하늘을 향하지 않는 삼각형들도 결국엔 영향을 받게 되는 방식이다.


우선, 삼각형이 하늘을 향해 있다는 의미를 파악해보자

_SnowDirection은 눈이 내리는 방향을 의미하며 단위벡터의 형태이다.

그것들이 어떻게 정렬되어 있는지를 확인하는 방법중 가장 쉬운 방법은 눈 방향으로 법선(노멀)을 투사하는 것인데,

두 벡터 모두 크기는 1이므로, 결과값은 +1(같은 방향)에 -1(반대 방향)사이가 될 것이다.

이것은 내적과 같다는 것을 Cos세타와 동일하다는 것만 알면된다.

특정 _Snow값보다 도트 연산이 커지게 된다는 것은


  • cos (\ theta) \ geq +1 두 방향이 같은 경우에만 참입니다.
  • cos (\ theta) \ geq 0\ theta90도 미만일 때 true입니다 .
  • cos (\ theta) \ geq -1 항상 사실입니다.

<결과 화면>


벡터 사이의 코사인 값을 수동으로 계산하는 대신,

Cg는 dot라는 이름의 도트 연산 구현을 포함하고 있다.

float4 sn = mul(_SnowDirection, _World2Object);

if (dot(v.normal, sn.xyz) >= _Snow)

v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;

이 부분은 노멀을 월드좌표로 바꾸기 위해서 조금 다른 메소드를 활용하고 있다.

WorldNormalVector함수는 버텍스 모디파이어 안에서는 사실상 이용할 수 없기 때문이다.



http://www.alanzucconi.com/2015/06/17/surface-shaders-in-unity3d/

https://jinhomang.tistory.com/137

반응형

라이트맵


라이트맵을 학습하기 전에 알아둬야 할 것이 있다.


GI

우리 모두는 빛이 들어옴으로써 눈이 작동하는 것은 알게된다. 

직접 광원을 바라보고 있지 않는 한 GI(전역광)은 우리의 눈으로 들어오기 전에 물체 사이를 돌아다닌다.

빛은 비추는 물체마다 다르게 반사되거나 흡수되어 새로운 방향으로 보내게된다.


컴퓨터는 그래픽에서 빛이 반동하는걸 두 가지 카테고리로 나눈다.

직접광, 간접광

직접광

광원으로부터 빛이 한번 튕겨지고 눈에 들어오는것을 말한다.

간접광

빛이 눈에 들어오기 전에 여러 표면에서 튕겨져서 우리 눈에 들어오는 광이다.


ENLIGHTEN = realtime 

리얼타임 GI를 전달하는 움직이는 라이트나 오브젝트가 있을 때 쓰면 좋지만 문제는 연산비용이 들기 때문에 미리 라이트를 계산해서 오브젝트에 덮어씌우는 큰 텍스처를 만들어 주는것이 좋다. 

=> 그것이 바로 베이킹


프로그레시브 라이트맵퍼

인라이튼을 써서 라이트를 굽는것말고 프로그레시브 라이트 맵퍼를 사용하는것이 좋다. 

프로그레시브 라이트 맵퍼는 경로 추적 기반의 라이트맵퍼로 주변에서 튕겨지는 빛을 사실적으로 시물레이션 할수 있다. 


이 방법은 카메라에서 광원한테 점진적으로 광선을 보내는것인데,  그래서 렌더링 할 때 에디터에서 바로바로 결과를 볼 수 있고, 씬을 다시 렌더링 할 필요없이 리이팅 프로퍼티를 설정 할 수 있다. 


테스트하기 위해 벽을 만들어 방처럼 오브젝트를 위치시켰다.

그리고 Directional Light은 삭제했다. (그래서 어둡게 보임)


Light가 될 plane 생성

왼쪽 벽에 붙이고 material을 생성 한 후 Emission을 Color 오렌지 색을 넣고 수치값을 1.5로 설정했다.

생성한 material을 plane에 넣어준다.


아직 아무 반응이없다. Emission이있는 오브젝트는 리얼타임에서는 빛이 퍼지지 않기 때문이다.

빛을 주려면 베이크를 해야한다. 

이 효과를 보기 위해서는 움직이지 않는 오브젝트들이니까 베이크해도된다고 유니티에게 알려야한다.

방, 큐브, plane모두 Static 객체로 만들어준다. 

Static 오브젝트를 만든순간 유니티는 베이크를 시작한다.

오렌지색 라이트가 생겼다.

반대편에는 블루라이트를 추가해줬다. 이미션값은 2로 설정

둘다 켜주면 뭔가 빛세기가 약해지는데 이 이유는 잘모르겠다 ㅠ


그 다음에 Lighting Scene을 설정해준다.

Scene 탭의 설정은 개별 게임 오브젝트가 아니라 전체 씬에 적용/ 이 설정은 조명 효과와 최적화 옵션도 제어

실내를 연출할 것이기 때문에 Ambient Color는 있어서 안된다. 

빛은 방금전에 만든 라이팅만 있어야하기 때문에 검은색으로 맞춰준다. (초기 셋팅은 회색으로 되어있음)


Realtime Lighting은 체크 해제 해준다.


Lightingmapping Seting설정

Lightmapper -> Progressive 로 바꿔준다.


그런데도 변화가 업다. 이유는?


프로그레시브 라이트 맵퍼를 사용할 때에는 이미시브 메테리얼로 가서

Global IlluminationRealtime 에서 Baked로 바꿔야한다. 


베이크 중

노이즈가 보이는데 베이크중이라서 그렇다.


베이크 후

라이팅 매퍼의 몇 가지 설정들에 대해서 알아보자

Lightmap Size : 오브젝트사이즈가 작다면 줄여주자 같은 결과이지만 라이트맵 용량이 크게 부여 되어 있을 수 있다.

Compress Lightmap : 라이트맵 압축 여부 

=> 용량이 클 수록(높은 사양)일 수록 베이크시간이 오래걸리기 때문에 작업수정할때는 낮췄다가 최종적으로 올리도록 한다.

Ambient Occlusion : 근처 두 오브젝트간의 그림자를 발생시킨다.







학습참고

1) https://www.youtube.com/watch?v=o39SjRcA__U

1) https://docs.unity3d.com/kr/2018.2/Manual/class-LightmapParameters.html

2) https://chulin28ho.tistory.com/443

3) https://docs.unity3d.com/kr/current/Manual/GlobalIllumination.html


반응형


라이트맵


라이트맵이란?

유니티 프로젝트를 새로 생성하면 "Directional Light"이 하이어라키상에 만들어져있다. 연극에서 무대를 비추는 조명이 있다면, 이 것은 게임장면을 비추는 조명이다.

당연히 게임이 진행되는 동안 조명 연산비용이 들어간다. 이  조명연산 또한 가볍지 않기 때문에 라이트맵을 사용한다.


라이트맵은 어떻게 연산비용을 줄이는가?

조명을 텍스처로 만들어서, 실시간으로 조명 연산을 하지 않아도 조명이 비추는것 처럼 표현 하는것이다.  


정적 오브젝트 (움직이지 않는 오브젝트)

조명을 텍스처로 만든다라고 하면, 물체에 있는 오브젝트에 그림자 등 조명이 비춰져 있는 상태를 미리 그려 놓는다는 것인데, 당연히 움직이는 오브젝트는 해당이 안되는것이다.(움직이는 오브젝트의 그림자를 미리 그려 놓을 수 없기 때문, 라이트맵의 종류에따라 표현방법이 다름)



라이트 맵 베이크 방법

라이트맵을 만드려면 일단 조명이 움직이지 않는 환경,물체 오브젝트를 베이크를 해야한다. 


1) 라이트 설정

라이트에는 필수로 두가지 설정이 필요하다.

첫 번째, Static 정적오브젝트로 만들어준다.

두 번째, Mode -> Baked or Mixed (Baked로 라이트맵을 만드는것과 Mixed로 만드는것은 차이점이 있다.)


2) 라이팅 셋팅 창

Window -> Rendering -> Lighting Settings창을 활성화 시킨다.

Scene탭에서 

라이트맵 종류에 대해서는 다음번에 정리를 해야겠다.

그 다음 Generate Light를 Bake해준다.

(Auto를 켜주면 변할때마다 자동으로 원치 않을때 Bake를 해서 불편 한 것같다.)


Baked Lightmap을 보면 텍스쳐럼 구워진게 보인다.



라이트맵# 2

https://funfunhanblog.tistory.com/94


전역 조명(GI)은 직접 표면에 닿는 광원(직접광)뿐만 아니라 다른 표면에 부딪혀 반사되는 광원(간접광)을 모델링하는 시스템입니다. 오브젝트는 서로의 형상에 영향을 미치기 때문에 간접 조명을 모델링하면 가상 세계를 더 현실적이고 연결된 것처럼 만들 수 있습니다. 예를 들어 빨간색 소파에 햇살이 비치면 붉은 광원이 그 뒤에 있는 벽 위로 반사됩니다. 또한 동굴 입구의 바닥에 햇살이 비춰지면 동굴 내부로 반사되어 동굴 안쪽이 밝게 비춰질 수 있습니다.

출처 : https://docs.unity3d.com/kr/2018.2/Manual/GIIntro.html


학습 참고 : 

1) https://chulin28ho.tistory.com/441?category=458928

2) https://docs.unity3d.com/kr/2018.2/Manual/class-LightmapParameters.html


반응형

미니맵 만들기 ( RenderTexture)


미니맵을 만드는 방법도 여러가지가 있겠지만 

이번에 학습할 내용은 랜더텍스처로 카메라를 추가해 캐릭터를 밑에 바라보게 하는것이다.

(보통은 카메라도 연산비용이 크기 때문에 카메라를 추가하지 않고 

캐릭터 실제 움직이는 위치를 계산해 이미지로 작은 미니맵을 만들어 준다고 한다.)


1) 카메라 추가 

위에서 밑을 바라보는 카메라를 만들어 미니맵처럼 보이기위함이다.

하이라키창에서 마우스 오른쪽 클릭하고 카메라를 추가해준다.

이름은 기존 카메라와 헷갈리지 않게 미니맵카메라 정도로 바꿔준다.


2) 캐릭터 플레이어를 대신 할 오브젝트 추가

새로 추가한 카메라가 플레이어 캐릭터를 비추면되지만 보통 캐릭터에는 폴리곤과 많은 이미지들이 붙어 있기 때문에 미니맵에는 이렇게 보일 필요가 없다. 

캐릭터의 어디에 있는지 위치정도만 보여주면 되기 때문에 나는 큐브를 생성하고 플레이어 자식으로 넣었다.

(미니맵 카메라는 사각이미지로 보일것이다.)

그리고 이 큐브 레이어를 'playercube'로 해준다. 이유는 밑에 설명


3) RenderTexture추가

랜더텍스처를 추가한다.

Render Texture는 런타임에서 생성 및 갱신되는 특수한 Texture로 카메라가 비추는 화면을 텍스처럼 가지고 있고 원하는 이미지에 그려줄 수 있는 텍스처라고 생각하면 된다.


4) 미니맵카메라 인스펙터 설정

새로 추가한 카메라에 셋팅을 해줘야한다. 

첫 번째로 카메라가 Culling Mask를 설정해준다. 

Culling Mask는 카메라가 보여질 레이어들은 체크하는 것이다. 맨 처음에는 Everything으로 되어 있을것이다. 

방금 추가한 Cube는 'playercube'이기 때문에 이것은 체크해주고 player(실제 캐릭터)는 체크 해제 해준다.

나머지는 그릴 필요가 있는지 없는지 체크해서 상황에 맞게 설정해준다. 


두 번째는 Target Texture를 아까 생성한 RenderTexture를 추가한다.


5) Raw Image 미니맵 이미지 생성

캔버스 밑에 Raw Image를 생성한다. 

일반 Image와 다르게 텍스처를 설정 할 수 있기때문이다.

Texture에 여기에도 아까 생성한 랜더텍스처를 넣어준다.


플레이 (우측 상단)


여기서 추가 셋팅은 기존 카메라에서는 당연히 하늘을 보게 되면 아까 추가한 큐브가 보일 것이다. 

기존 카메라에서 Culling Mask 'playercube'를 체크 해제해서 안보이게 해주어야한다.


랜더 텍스처 : https://docs.unity3d.com/kr/530/Manual/class-RenderTexture.html

반응형

유니티) 셰이더 #1




3D모델은 버텍스라고 불리우는 3D 좌표들의 모음이다.

그것들은 삼각형을 이루기 위해서 서로간에 연결이 되어 있다.

 각 버텍스에는 몇가지 다른 정보들을 포함할 수 있다.

색상값, 방향(노말) 그리고 UV값이라고 불리는 텍스처 매핑을 위한 좌표들이 있다.





모델은 머터리얼없이는 렌더링이 불가능하고, 머터리얼은 셰이더와 그 셰이더 속성값을 포함한 일종의 덮개?이다.



셰이더구조


유니티는 서피스 셰이더와 버텍스버텍스 와 프래그먼트 셰이더 두 가지 타입의 셰이더를 지원한다. + 픽스드 함수 셰이더도 있다.


먼저 셰이더 기본 구조를 알아보자 어떤 셰이더든 구조는 모두 아래와 동일하다.



SubShader섹션은 여러개가 될 수 있다. 여기에 실질적인 GPU에 대한 명령을 담겨 있는데, 유니티 엔진은 그래픽 카드에 호환되는 것을 찾을 때까지 이것들을 순서대로 실행을 시도한다. 

여러 플랫폼을 고려하여 여러개 섹션을 나누는것이다.



프로퍼티

셰이더 프로퍼티는 머터리얼의 인스펙터 안에서 볼 수 있다. 따라서 인스펙터를 통해 그 값을 변경할 수도 있는 것이다. (C#의 public 필드 느낌)


스크립트를 사용하는 것과 다르게 머터리얼은 Asset이라서 프로퍼티 변경값이 그대로 남아있는다. 따라서 멈춘 후에도, 머티리얼에서 변경헀던 부분의 남아있는 것을 확인할 수 있다.



 첫번재와 두번째 프로퍼티에 사용된 2D 타입은 파라미터가 텍스처라는 것을 뜻한다. 

이들은 white, black, gray로 초기화 될 수 있다. 

또한 텍스처가 노멀맵으로 사용될 것을 의미하는 bump를 사용 할 수도있다.


Vector와 Color는 각각 X,Y,Z,W와 R,G,B,A이렇게 항상 4가지 요소를 가진다.


프로퍼티는 유니티에 의해 셰이더 내의 숨겨진 변수에 액세스 할 수 있도록 할 뿐이지 실제로 적용되지는 않는 것이다

그렇기 때문에 SubShader 본문을 정의 해야한다.



렌더링오더

 SubShader섹션은 셰이더의 실제 코드를 담고 있다. 이 코드는 C와 비슷한 Cg / HLSL로 작성된다.

대략적으로, 셰이더의 바디느느 이미지의 각각의 픽셀을 위해 실행된다. 


셰이더 안에서 수행할 수 있는 명령의 수가 제한 되지만, 계산을 여러 패스(pass)로 나누어 이런 것을 피할 수 도 있다. 



Tag는 우리가 작성 중인 셰이더의 특정 프로퍼티에 대해 Unity에게 얘기하는 수단이다.


여기서 말하는 특정 프로퍼티를 예를 들자면, 렌더링이 되어야 하는 순서를 뜻하는 Queue

모델을 어떻게 렌더링이 되어야 하는지를 뜻하는 RenderType같은 것들이 있다.


쉽게 Queue는 머터리얼의 렌더링 순서를 제어하기 위해 Queue태그를 지정할 수 있는 것이다.

(숫자가 작을 수록 먼저 그림)


Background(1000) : 배경과 스카이박스르 위해 사용

Geometry(2000) : 대부분의 불투명 오브젝트를 위해 사용되는 기본 레이블

Transparent(3000) : 투명 프로퍼티를 가지는 머티리얼(유리, 불, 파티클, 물 등등)에서 사용된다.

Overlay(4000) : 렌즈 플레어같은 이펙트, GUI, 텍스트와 같은 것을 그릴때 사용



서피스 셰이더


머티얼이 매 순간 빛에 의해 영향 받으며 현실감 넘치게 시뮬레이션하고 싶은 경우

라이트(빛)가 반영되는 방법에 대한 계산은 내부에 숨기고, surf라는 함수 안에서 알베도, 노멀, 반사율등 과 같은 "직관적인" 속성만 정해주는것이다.



예제코드를 보면 sampler2D _MainTex라인을 사용하여 입력되는 것을 알 수 있다. 

이것은 surf함수에서 머티리얼의 Albedo속성으로 설정된다.



버텍스/프래그먼트 셰이더


버텍스/프래그먼트 셰이더는 거의 GPU가 삼각형을 렌더링하는 방법대로 동작하게된다.

그리고 라이트와 어떻게 작용하는지에 대해 특별히 내장되거나 정해진 컨셉이없다. 

모델의 지오메트리는 먼저 버텍스를 변경할 수 있는 vert라는 함수로 전달된다. 

그 후에, 각 픽셀에 대해서 최종 RGB색상을 결정하는 frag라는 함수로 전달된다.

보통 이 유형의 셰이더는 2D이펙트, 후처리 또는 서피스 셰이더로 표현되기 어려운 복잡한 3D특스 이펙트를 구현하는 유용하다. 


<적용>

vert함수 안을 보면, 버텍스들을 그 원래의 3D공간에서 스크린 상의 최종 2D포지션으로 변환하는 코드가 있다. 

유니티는 이 변환에 대한 수학적인 내용을 감추기 위해서 UNITY_MATRIX_MVP를 사용하여 단순화 하였다. 이 후, frag함수를 반환하면 모든 픽셀이 빨간색으로 칠해진다.

https://unity3d.com/kr/learn/tutorials/topics/graphics/gentle-introduction-shaders

반응형


코루틴Coroutine 작동원리



코루틴?



코루틴은 실행을 중지하여 Unity에 제어권을 돌려주고, 그러나 계속할 때 는 다음 프레임에서 중지한 곳부터 실행을 계속할 수 있는 기능. 함수를 호출하면 반환값을 반환하기 전에 실행 완료된다. 즉 , 함수에서 수행되는 작업은 하나의 프레임에서 수행된 다는 것을 의미하고, 함수 호출은 절차적 애니메이션을 포함하거나 시간의 경과 함께 일련의 이벤트에는 사용할 수 없다.

<유니티 공식 메뉴얼>


유니티로 게임 제작을 하다보면 코루틴을 많이 사용하게 된다. 쓰레드의 대해서 정확하게 알지는 못하지만 유니티는 단일 쓰레드로 동작한 다는것은 알고 있었다. 그렇다면 유니티에서 멀티쓰레드처럼 멀티태스킹이 불가능한걸까?  멀티 쓰레드처럼 비슷하게 작동하게 해주는 것이 코루틴이다. 그렇다고 멀티쓰레드가 되는것은 아님


코루틴은 어떻게 작동될까?

코루틴은 IEnumerator를 반환한다. C#의 이터레이터 인터페이스는 프로그래머가 IEnumeraotr를 구현 할 수 있도록 도와준다. 

진입하는 지점을 한 개인 보통 서브루틴 방식과 다르게  진입하는 지점까지 여러 개를 가질 수가 있다. yield return을 통해 그 바로전 시점을 기억하고 다음 호출하게 될떄 그 다음부터 실행이 되는 것이다. 

유니티에서는 대부분 그 시점을 Update에서 체크한다. 


코루틴을 사용할 때 IEnumerator와 같이 사용한다. 이 IEnumerator는 데이터의 목록을 하나씩 넘겨줄 때 사용되는 인터페이스인데, 코루틴은 호출 한 함수와 상호작용하면서 진행되는데 자신을 호출한 함수에 데이터를 하나 넘겨주고 쉬게되는데, 받은 함수에서는 데이터를 받고 처리한 후에 코루틴에게 다음 데이터를 달라고 요청하게 된다. 





(서브 루틴 : 일반적인 함수에서 사용되는 작동개념인데 함수는 시작할 때 진입하는 지점이 저장하고, return구문에 의해서 종료되는 지점을 저장한다. 

Iterator :컬렉션에 대해 사용자 지정 반복을 수행, yield return 문을 사용하고 각 요소를 한 번에 하나씩 반환한다. 이터레이터는 현재 위치를 기억하고 다음 반복에서는 다음 요소를 반환.)






학습 참고:

1). https://m.blog.naver.com/PostView.nhnblogId=captainj&logNo=221102892952&proxyReferer=https%3A%2F%2Fwww.google.com%2F

2). https://seungngil.tistory.com/

3) .http://blog.naver.com/PostView.nhn?blogId=yoohee2018&logNo=220699457038&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView


반응형

 

쓰레드(Thread)

 

쓰레드 란?

쓰레드는 어떠한 프로그램 내에서 특히 프로세스 내에서 실행되는 흐름의 단위를 말한다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 쓰레드를 동시에 실행할 수 있다. 이러한 실행 방식을 멀티쓰레드라고 한다.

<위키 백과>

 

멀티프로세스와 멀티스레드는 양쪽 모두 여러 흐름이 동시에 진행되는 공통점이 있다. 하지만 멀티프로세스에서 각 프로세스는 독리적으로 실행되며 각각 별개의 메모리를 차지하고 있는 것과 다르게 멀티스레드는 프로세스 내의 메모리를 공유해 사용할 수 있다. 위키백과에서 나와 있듯이 쓰레드는 운영체제가 아닌 CPU시간을 할당하는 기본 단위이고, 프로세스는 하나 이상의 쓰레드로 구성 되는것이다.  또한 프로세스 간의 전환 속도보도다 스레드 간의 전환 속도가 빠르다. 

 

쓰레드의 상태

Unstarted

 쓰레드 객체를 생성한 후 Thread.Start() 메소드가 호출 되기 전의 상태

 Running

 쓰레드가 시작하여 동작 중인 상태.

 Unstarted 상태의 쓰레드를 Thread.Start() 메소드를 통해 이 상태

 Suspended

 쓰레드의 일시 중단 상태.

 쓰레드를 Thread.Suspend() 메소드를 통해 이 상태로 만들 수 있으며, Suspended 상태인 쓰레드는

 Thread.Resume() 메소드를 통해 다시 Running 상태

 WaitSleepJoin

 쓰레드가 블록(Block)된 상태

 쓰레드에 대해 Monitor.Enter(), Thread.Sleep(), Thread.Join() 메소드를 호출하면 이 상태

 Aborted

 쓰레드가 취소된 상태.

 Thread.Abort() 메소드를 호출하면 이 상태가 됩니다. Aborted 상태가 된 쓰레드는 다시 Stopped 상태로 전

 환되어 완전히 중지

 Stopped

 중지된 쓰레드의 상태

 Thread.Abort() 메소드를 호출하거나 쓰레드가 실행 중인 메소드가 종료되면 이 상태

 Background

 쓰레드가 백그라운드로 동작되고 있음, Foreground 쓰레드는 하나라도 살아 있는 한 프로세스

 가 죽지 않지만, Background는 여러개가 살아 있어도 프로세스가 죽고 사는 것에는 영향을 미치지 않는다.

 하지만 프로세스가 죽으면 Background 쓰레드는 모두 죽음. Thread.IsBackground 속성에 true 값을

 입력하면 쓰레드를 이 상태로 바꿀 수 있음.

 

쓰레드 라이프 싸이클

 

 

쓰레드 간단한 예제



 

 

학습 참고 :

 

1)  https://ko.wikipedia.org/wiki/%EC%8A%A4%EB%A0%88%EB%93%9C_(%EC%BB%B4%ED%93%A8%ED%8C%85)

2) https://nshj.tistory.com

3) https://codedragon.tistory.com/3526

4) https://dlgnlfus.tistory.com/295

5) https://acroama.tistory.com/m/43

 

반응형

Physics.Raycast 오브젝트 검출하기




레이를 쏴서 그 오브젝트가 어떤 오브젝트인지 파악하고 각각 다른 액션이 필요하다.


타입을 나누는 이유는 캐릭터가 검출대상(box,무기,아이템등..)의 대한 메소드를 캐릭터 스크립트에서 모두 정의 하는것은 다향성부분에도 떨어지고, 코드도 보기가 좋지 않다. 오브젝트 객체가 각각 가지고 있는 스크립트에서 정의해주고 캐릭터는 어떤 상황에 어떤 함수를 실행할 것 인지 제어해 주는것이 좋을 것 같다.

그래서 타입을 나누고, 인터페이스를 활용한다.  


타입 나누기

타입의 따른 설정을 주기 위해 열거형으로 만들어줬다.


인터페이스 만들기 

InterType : 어떤 타입인지 받아 오기위함

enable : 오브젝트 스크립트에서 제어 하기 위함

ActionInter : 입력버튼을 실행 할 경우 실행되는 메서드

ShowInter, NonShowInter : 각각 상황에 맞게 실행 


박스 스크립트 부분(인터페이스)

Box, Npc, Monster 등등 IInteraction을 인터페이스 구현이 필요하다.


플레이어스크립트 레이캐스트

if (hit.transform.CompareTag(Tag.RAYCAST_TAG_INTERACTION)) 

=> 오브젝트가 INTERACTION 태그인지 확인한다.

if (iInteraction.enable==true)

=> 오브젝트 enable를 체크한다.

UI_Hud.Ins.StringSetText(iInteraction.InterString);

=> 오브젝트 스크립트에 있는 InterString를 UI_Hud에 보낸다.(싱글턴)

UI_Hud.Ins.StringSetText

=> 각각 오브젝트 스크립트에서 설정한 텍스트 박스를 띄워준다.

 


반응형

리플렉션 Reflection


실행 중(런 타임)에 클래스나 객체의 타입 정보를 조사하는 기능, 리플렉션을 사용하여 형식의 인스턴스를 동적으로 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져오고 해당 메서드와 속성을 액세스 할 수 있다.(형식 이름, 포로퍼티, 메소드, 필드, 이벤트 목록을 모두 볼수 있고, 해당 메소드를 호출하거나 필드, 프로퍼티에 접근하는 것도 가능하 다는 것임)

타입 정보는 보통 컴파일 중에만 사용되며 컴파일러에 의해 기계어로 바뀌고 나면 사라지는 것이 일반적이다 하지만 C#은 컴파일된 결과 코드 뿐만 아니라 타입에 대한 메타 데이터를 실행 파일에 같이 기록해 놓기 떄문에 실행중에도 정보를 조사할 수 있다.


접근방법

지정된 이름으로 접근

1) Type t = Type.GetType("Test");


클래스 타입으로 접근

2) Type t = tyoeof("Test");


객체로 접근

3) Type t = Test.GetType()


프로그램의 metadata에 접근할 수 있도록 해주는 이 클래스는 System.Refelection namespace에 정의 해 주어야한다.

해당 메서드와 멤버변수들을 불러 올수 있다.







학습 참고

1) http://www.csharpstudy.com/Practical/Prac-reflection.aspx

2) https://hsj0511.tistory.com/350

3) https://hongjinhyeon.tistory.com/86

반응형


Action과 Func (람다식)


Action

Action은 파라미터가를 받아 들이는데, 리턴 값이 없는 함수에 사용되는 Delegate이다.


Action은 쓰려면 네임스페이스에 선언해줘야한다.

using System;

무명메델리게이트와 다른 Action 델리게이트

delegate void MyDelegate<T1, T2>(T1 a, T2 b); 와 다르게 네임스페이스에 선언한 System에 Action은 유니티시스템에 이미 정의되어있다.



Func 


Func는 Action과 비슷한데, 리턴값이 있을 경우 사용한다.

3번째 는 반환값 타입을 입력해줘야한다.


실제사용 코드 

테이블종류를 넣고 데이터를 리스트에 추가하는 코드이다, 여기서도 Action<bool>을 사용하였다. 



학습참고 : https://www.youtube.com/watch?v=7H3MHXfFkhI&index=19&list=PLUZ5gNInsv_O7XRpaNQIC9D5uhMZmTYAf

http://www.csharpstudy.com/Tip/Tip-Func.aspx

반응형



암시적 변환


C#은 컴파일 타임에서 정적으로 데이터 형이 지정된다. 변수의 형을 변환 하지 않으면 다시 선언하거나 다른 형태의 값에 넣는 것이 당연히 불가능하다,예외가 있는데 바로 암시적 변환 될 경우다.


형식이 안전하고 데이터가 손실되지 않으므로 특수 구문이 필요하지 않는다. 


1) 파생클래스 -> 기본클래스로 변환



2) 동일 데이터형의 큰타입으로 변환






명시적 변환(캐스트)


명시적 변환은 캐스트가 필요하다. 변환 시 정보가 손실 될 수도 있고, 암시적으로 변환된 작은 타입에서 큰타입으로 변환되는데 값의 손실이 없었지만 반대(큰타입->작은타입)는 값 손실이 있다.


그냥 변환하는것이 불가하고 캐스팅 키워드를 이용해야 한다.

앞에 '( 타입 )' 써준다.


1) 기본클래스 -> 파생클래스


2) (같은 타입)작은자료형 -> 큰자료형




학습참고 : https://guslabview.tistory.com/68

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/types/casting-and-type-conversions

반응형

ScriptableObject


스크립팅 가능한 오브젝트

메소드를 실행하는 스크립트가아닌 데이터를 사용하기 위한 스크립트이다.




생성방법

[CreateAssetMenu(fileName = "New HeroData", menuName = "Sword HeroData", order = 2)]

public class test : ScriptableObject

{


}

유니티에서 스크립트를 생성하면 Monobehaviour를 자동으로 상속 받게 되는데 지우고

ScriptableObject를 상속받아온다.


[CreateAssetMenu] : 없어도 되지만 있으면 편함

fileName : 새로 제작하게되면 임시로 생성된다. 

menuName : 유니티 메뉴에 표기되는 이름

order : 메뉴에서 보일 순서

Assets -> Create 보면 방금 생성한 HeroData가 보이게 된다.


SerializeField의 원본 데이터들은 수정되면 안되기 때문에 

데이터 값을 받을 게터를 public으로 추가해준다.

public class test : ScriptableObject

{

    [SerializeField]

    string heroName;

    [SerializeField]

    string description;

    [SerializeField]

    int goldCost;


    public string sName => heroName;

    public string sDesc => description;

    public int    nCoin => goldCost;

}


사용법

데이터 값 입력

ScriptableObject를 받을 코드 작성

public class Scene : MonoBehaviour

{

    public Text m_txtUI;

    public test m_Data;

    void Start()

    {

        m_txtUI.text = string.Format(m_Data.sName + " // " + m_Data.sDesc);

    }

}

Data 매칭 인스펙터 창에서 끌어다 놓는다

실행


데이터를 한 오브젝트나 클래스에서 사용하기에는 괜찮은 것 같다. 데이터가 왔다갔다 하거나 값이 변하는 데이터는 쓰기에 어려울 것 같다.


학습 참고 : https://docs.unity3d.com/kr/current/Manual/class-ScriptableObject.html

https://m.blog.naver.com/yoohee2018/220725032540

반응형

C#) 네임스페이스



가끔 "네임스페이스에 형식또는 네임스페이스 이름이 없습니다."라는 문구를 볼때가 있다.

네임스페이스를 알아보기 전에 작업 하다가 생긴 오류를 살펴보자


Define클래스

인터페이스나 enum같은 경우는 Defie클래스를 만들어서 한 곳에 정의하였다.


인터페이스를 사용하려는 클래스

아까 정의한 인터페이스를 사용하려는데? 

오류가 났다. 바로 'Global_Define' namespace를 선언해 주지 않았기 때문이다.


예시

콘솔창에서 텍스트를 출력하는 Console이 오류가 났다. 

이유는 System이라는 namespace가 없기 때문이다. 


Console클래스 정의로 이동해봤다.

Console이라는 클래스에는 WirteLine( )이라는 메서드가 있었다. 


Console이라는 클래스는 System 네임스페이스의 정의 되어있다.

System은 -> 네임스페이스

Console은 -> System의 정의되어있는 클래스

WirteLine은 -> Console클래스 에 정의된 메소드


네임스페이스 정의방법

첫 번째 

맨 윗단에 해당 using을 사용하여 C#파일에서 사용하고자 할때

두 번째

클래스명 앞에 네임스페이스 전부를 적는 경우 이 경우는 네임스페이스 안의 메소드, 

변수를 사용하려면 앞에 매번 적어줘야함 



네임스페이스는 즉 

소속이라는 정의하는 역할을 하는 것이다. 

네임스페이스를 귀찮게 나눠서 쓰는 이유.

이름이 같은 변수,메소드등등 이지만 실행되는 내용들은 다를 수 있기 떄문이다. 

A프로그램과 B프로그램이 있는데 같은 이름을 사용하지만 그 내용들이 조금 다르면?

프로젝트가 커지고 스크립트 수가 증가할수록 스크립트 클래스 이름이 충돌할 가능성이 커집니다. 이것은 게임의 다른 파트를 ​​따로 작업하고 궁극적으로 같은 프로젝트에 통합하는 경우에 적용됩니다. 예를 들어 한 명의 프로그래머가 메인 플레이어의 제어 코드를 만들고, 또 한사람이 적 캐릭터의 제어 코드를 작성했다고합니다. 둘 다 기본 스크립트 클래스를 Controller 라고 명명하고 프로젝트를 통합할 때 이름이 충돌합니다.

<유니티도큐>


유니티에 생성되는 namespace

유니티에서 스크립트를 생성하면 자동으로 추가 되는 것들이다. 클래스에서 Start나 Update메소드가 자동으로 생성되지만 있는 존재만으로도 비용이 들기 때문에 사용하지 않으면 삭제한다. namespace도 마찬가지이다. 사용하지 않으면 지워주자



학습참고 : 

1) https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/namespaces/

2) https://thinkpro.tistory.com/22

3) http://www.csharpstudy.com/csharp/CSharp-namespace.aspx


반응형

객체지향 언어와 절차지향 언어 차이점



절차지향 언어

프로그램의 프로세스 흐름을 표현하는데 비중


객체지향 언어

데이터나 정보의 표현에 비중

데이터와 함수를 하나의 덩어리로 묶어서 생각하는 방법


절차지향 언어는 대표적으로 C언어가 있다. 컴퓨터의 작업처리 방식과 유사하기 때문에 객체지향 언어를 사용하는 것보다 속도가 빨랐다. 

하지만 지금은 기술발전으로 소프트웨어가 발달로 절차지향과 객체지향언어는 속도차이가 거의 나지 않음 뿐더러

객체지향 언어는같은 기능을 중복으로 사용하는 경우를 없애고, 모듈을 재활용하기 때문에 하드웨어가 감당하는 처리량을 많이 줄 뿐만 아니라  새로운 기능을 수정,추가 하는 유지보수, 캡슐화로주변에 미치는 영향을 최소화 해준다. 


객체지향 프로그래밍은 실행절차에 영향을 받지 않는 프로그램 기법인 것처럼 생각할 수 있지만, 구조와 모듈을 먼저 설계한 다음에 실행순서와 흐름을 짜는 방식일뿐, 객체지향방식으로 개발한 프로그램도 절차에 따라 실행된다. 


객체지향언어의 장점이 더 많지만 코드의 사이즈를 최소화 하고 빠른 동작이 필요한 프로그램이면 절차지향언어로 개발하는게 맞는것 처럼, 프로그램의 목적의 맞게 선택해서 개발해야한다.




학습참고

1) http://blog.wishket.com/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-vs-%EC%A0%88%EC%B0%A8%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/

2) https://sesok808.tistory.com/32


반응형

'프로그래밍언어' 카테고리의 다른 글

실수비교/ Epsilon 엡실  (0) 2019.06.09
오버로딩,오버라이딩 Overloading / Overriding  (0) 2019.01.19

스태틱 오브젝트


게임이 실행되는 동안 움직이지 않는 오브젝트를 스태틱으로 설정해 놓으면 유니티 에디터 상에서 미리 계산해놓아 실시간으로 계산하지 않도록 하여 비용을 줄이는 최적화 방법이다.



  • Lightmapping: 씬을 위한 고급 라이팅;
  • Occluder and Occludee: 특정 카메라 위치로부터 오브젝트의 가시성에 기반한 렌더링 최적화;
  • Batching: 여러 오브젝트를 더 큰 하나의 오브젝트로 결합하는 렌더링 최적화;
  • Navigation: 씬 안에서 캐릭터가 장애물을 지나게끔 만들어주는 시스템.
  • Off-mesh Links: Navigation 시스템에 의해 만들어지는 씬에서의 불연속적인 영역 간의 연결.
  • Reflection Probe: 모든 방향에 대한 주위 환경을 둥근 뷰의 형태로 캡처합니다.

유니티는 베이크된 GI(전역 조명)을 간접광에 대한 내용을 미리 계산해서 데이터화를 미리 가능하다. 




Occlusion Culling오클루젼 컬링


땅을 스태틱오브젝트로 설정하고 카메라가 비추는 곳만 그리게 된다.

오클루젼 컬링 (Occlusion Culling)은 오브젝트가 다른 오브젝트에 의해 가려져서 카메라에 보이지 않을 때 해당 오브젝트의 랜더링을 비활성화 하는 기능이다.

+ 이거는 절두체컬링이네.. 

정적오브젝트는 리소스가 사전준비가 되어있어야 하며 추가/변경 될때는 베이크를 해주어야 한다.  



참고자료 : https://docs.unity3d.com/kr/530/Manual/StaticObjects.html

반응형

boxing/unboxing


박싱/언박싱?


내가 알고 있는 박싱/언박싱은 자료의 형변환이 일어날때

값 형식을 참조형식으로 박싱

참조형식을 값형식으로 언박싱이다 라는정도인데 메모리영역에서 어떻게 일어나는지 알아보았다.


박싱

값 형식을 참조 형식으로 변환되는 것을 말한다. 

static void Main(string[] args)

{ int i = 93; object s = i; //박싱 }

int i 는 93이라는 값을 받고, object s는 i의 값을 받고있다.

값 형식인 정수형 자료 int를 object(참조형식)객체로 할당하게 되면 박싱이 일어나게 된다.


=> 스택에 있는 데이터가 힙으로 복사 된 것이다.


Stack에 있는 93이라는 정수형 자료가 Heap영역으로 복사되고, 이영역을 객체 변수 s가 가리키게 된다.



언 박싱

참조 형식을 값 형식으로 변환하는 것,

static void Main(string[] args) { int i = 93; object s = i; int j = (int)s; //언 박싱(명시적 변환) }

s를 int로 값 형식으로 변환한다. 

박싱된 객체인 s를 다시 값 형식으로 변환하면 언박싱이 일어난다.


=> 힙에 있는 데이터가 스택으로 복사된다.



언 박싱이 가능한 객체

값 형식을 박싱하여 생성된 객체이어야만 가능

static void Main(string[] args) { short i = 93; object s = i; int j = (int)s; //언 박싱 불가 }

short타입을 박싱한 객체를 int타입으로 언 박싱은 불가능

해당 타입보다 작은 범위로 변환 불가능



높은 비용의 박싱/언박싱

박싱과 언박싱은 많은 시간이 소모된다고 한다. 되도록 제네릭을 사용해서 박싱과 언박싱이 일어나지 않도록 구성하도록 하는게 좋다.


박싱의 과정 : 

1. 값 타입을 힙에 생성하기 위해 메모리를 힙 영역에 생성

2. 값을 힙 영역에 할당된 메모리로 복사

3. 참조할 변수에 할당된 메모리 주소를 할당


언박싱의 과정 :

1. 박싱값인지 확인

2. 박싱된 값이라면 값 타입 변수에 복사

3. 박싱한 메모리와 언박싱한 메모리 2개 존재 ( 가비지 발생 )



학습참고 : http://www.mkexdev.net/Article/Content.aspx?parentCategoryID=1&categoryID=5&ID=671

 https://grayt.tistory.com/87 [IT's me]

https://hongjinhyeon.tistory.com/90


반응형

ref 키워드 (참조자)


C++에서 참조자 & 있다면 C#에는 단순히 참조를 하기위한 ref키워드가 있다. 

값형식이든 참조 형식이든 둘다 스택에 있는 값이 복사된다.


값 형식 : 스택영역메모리에 값이 직접 들어가있어서 그것이 복사된것

참조 형식 : 스택영역메모리에 힙영역의 주소가 들어있어서 그 주소가 복사되면서 같은 객체를 가리 킴

ref키워드를 이용하면 스택영역 메모리의 주소를 사용하면서 Call by reference를 사용하게된다. 


예제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        static void Main(string[] args)
        {
            int a = 100;
            int b = 200;
            swapvalue(ref a, ref b);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""a", a);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""b", b);
 
        }
        static void swapvalue(ref int a, ref int b)
        {
            int temp = a;
            a = b;
            b = temp;
        }


a값과 b값이 변경되었다.


out 키워드 (참조자)


out키워드도 Call by reference를 가능하게 하는 키워드이며 

'출력용 매개변수'라고도 하여 값을 읽을 수 없는 쓰기용이다.

ref와 다른 특징들이 있다.


1. 현재 메소드가 끝나기 전에 out키워드가 붙은 변수는 값을 할당 받아야한다.

1
2
3
4
5
6
7
        static void swapvalue(ref int a, ref int b, out int c)
        {
            int temp = a;
            a = b;
            b = temp;
  //c 변수는 값을 할당 받아야한다.
        }

cs


2. out 키워드가 쓰인 변수는 함수내에서 값을 읽을 수 없다.

1
2
3
4
5
6
7
8
        static void swapvalue(ref int a, ref int b, out int c)
        {
            int temp = c; //에러
            a = b;
            b = temp;
            c = a;
 
        }
cs


예제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        static void Main(string[] args)
        {
            int a = 100;
            int b = 200;
            int swapA ; //값 초기화 의미 X
            int swapB;  //값 초기화 의미 X
            swapvalue(a,b, out swapA, out swapB);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""a", swapA);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""b", swapB);
 
        }
        static void swapvalue(int a,int b, out int swapA, out int swapB)
        {
            swapA = b;
            swapB = a;
        
        }
cs


ref와 기능이 같은데 out키워드를 쓰는 이유?

ref키워드를 이용해서 매개 변수를 넘기는 경우에는 메소드가 해당 매개변수에 결과를 저장하지 않더라도 컴파일러는 아무런 경고를 하지 않는다.

하지만 out 키워드는 매개변수를 넘길 때 메서드가 해당 매개변수에 결과를 저 정하지 않으면 컴파일러 에러를 주면서 강제적으로 제한을 준다.

 또 첫 번째처럼 초기화를 하지 않아도 되는 이유는 호출당할 때 값을 할당할 것을 보장하기때문이다. 프로그램이 실행됐을 때가 아닌 코드를 작성하는 중에 컴파일 과정 에러를 확인할 수 있다.



공부참조

https://grayt.tistory.com/90?category=683217

https://runtoyourdream.tistory.com/279

https://grayt.tistory.com/90?category=683217

반응형

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

C#) 네임스페이스 / 유니티 네임스페이스  (0) 2019.02.13
C#) 박싱/언박싱 (boxing/unboxing)  (0) 2019.02.10
C#) Lamda #2  (0) 2019.02.07
C#) Lamda 람다 식  (0) 2019.02.06
C#) Generics 제네릭 클래스  (0) 2019.02.04

람다 Lamda  #2





앞서 공부한 

델리게이트를 써서 sum값을 출력해봤다.

//결과 값 15


이번에는 Print함수를 작성하지 않고 

무명메소드로 sum을 출력해봤다.

 //결과 값 15


이번에는 

람다식으로 sum을 출력했다.

// 결과 값 15


마지막으로

형식 매개변수를 이용해서 람다 식을 사용해 봤다.

// 결과 값 15 (유니티 디버그로 출력)

반응형

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

C#) 박싱/언박싱 (boxing/unboxing)  (0) 2019.02.10
C#) ref 키워드 (참조자) , out 키워드  (1) 2019.02.09
C#) Lamda 람다 식  (0) 2019.02.06
C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) 비트연산자  (0) 2019.01.30

 Lamda 람다 식




람다 식은 대리자또는 식트리 형식을 만드는 데 사용할 수 있는 익명 함수이다.

람다 식을 사용하여 인수로 전달되거나 함수 호출값으로 반환되는 로컬 함수를 쓸 수 있다.


작성법

' => ' 왼쪽에 입력 매개 변수를 지정(있는 경우) 다른 쪽에 식이나 문 블록을 넣는다.


delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 }




람다 식

무명 메소드를 단순한 계산식으로 표현 한 것이다.

메소드는 매개변수, 내부 식, 반환 값으로 구성 되어 있는데 이들을 가지고 메소드를 계산 식으로 표현할 수 있다.(=람다 식)



먼저 무명 메소드를 보면

매개변수 타입, 내부의 { } 내부 식, 반환 값을 입력 해주어야 한다.


무명메소드를 람다 식으로

 매개변수 타입, { }안 내부 식,내부 반환 값이 생략이 가능하다.




https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions

반응형

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

C#) ref 키워드 (참조자) , out 키워드  (1) 2019.02.09
C#) Lamda #2  (0) 2019.02.07
C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) 비트연산자  (0) 2019.01.30
C#) abstract, virtual, override  (0) 2019.01.26

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀 점

문제

체스 말판이 위 특정 셀에 위치할 때 움직일 수 있는 방법의 개수를 구하는 문제이다.  특정 위치 이기 때문에  특정 셀 밑이거나 위에 있을 때 제한된다.

풀이 : 내가 작성한 코드

 

메모장에 적으면서

 처음 생각하기에 문자열의 앞cell[0]뒤cell[1] 8방향의 이동하게 문자를 더하는 값으로 확인이 가능할까 생각했지만 안된다고 판단하고, 체스 말이 위 체스 말이 움직 일 수 있는 방법이 최대 8가지이기 때문에 8방향을 모두 체크하고 각각 케이스에 검사하는 방법밖에 생각이 안 났다. 그래서 그냥 case문을..

풀이 : 다른 사람이 작성한 코드

가장 높은 vote를 받은 코드이다. 5개의 길이의 배열을 만드는데 무엇인가 했더니 체스 말판이 움직일 수 있는 방향 개수들이다. 그다음으로 문자 알파벳과 숫자를 체크하는데 'a'나 'h'를 빼는 이유는 체스가 저 두 문자에 위치했을 때 이동 방향 개수가 제한된다. 또, 체스 말판이 a에 가깝나 h에 가깝나를 체크한다. Math.Min(int,int )는 둘 중 작은 수를 반환하는 함수인데, 'a', h'에 가깝지 않아 2를 넘으면 4방향은 이동 가능이 보장이 된다. 반대로  마찬가지로 숫자 부분도 체크하여 리턴된 두 정수를 더한 뒤 배열에서 찾는다.. 

일단 문제 접근에 대한 방식 자체가 다르다ㅜ. 훨씬 더 깔끔하고 나도 문제에서 8가지 방향을 정해져 있다고 판단하고 포문을 8번 돌렸지만, 이 코드는 한 번 더 생각하여 제한된 이동 방향 개수 생각하고 그 값을 배열로 넣어두었다. 그리고 dist라는 말의 칸 이동 수가 정해져 있기 때문에 체스판 테두리에 가까웠을 때 제한되는 방법으로 깔끔하게 구현했다. 역시 접근 방법은 여러 가지로 생각하는 게 좋겠다. 

문제 : https://codesignal.com/

반응형

+ Recent posts