Stack 스택




스택,큐,트리와 같은 자료 구조는 자신이 행위적 측면을 포함하는 구조이다.


스택은 밑이 막힌 긴 통이라 생각하고 입,출구가 하나인 과자통을 생각하면 쉽다(프링글스)


 먼저 들어간 것은 가장 나중에 빠져 나오게 된다. (후입선출)LIFO

반대로 가장 늦게 들어간 데이터가 가장 먼저 빠져나가게 되는 것이다.


스택에는 구현방법이 배열구현과 리스트구현 두가지가 있다.

배열은 처음 배열의 크기를 설정해두고 시작해야 하기 때문에 나중에 배열의 크기를 변경 할 수 없는 반면에 리스트구현은 동적인 할당을 통해서 구현되기 때문에 연결리스트로 구현 하는것이 유연하다.


배열로 구현한 스택구조가 더 빠르다고 하는데 하드웨어가 발단된 지금에서는 별 차이가 없다.


그렇기에 이번 학습일지에도 리스트를 이용한 스택구현을 연습!


입출구가 하나이기 때문에 단순결리스트로 쉽게 구현이 가능하다.



Node클래스



PUSH 데이터 노드 삽입

연결리스트를 이용 할 경우 TOP이라는 변수의 역할은 머리 노드가 대신한다.

머리노드의 다음 노드가 스택의 상단이 되는 것이다.


POP데이터 노드 삭제

후입선출이기 때문에 최상단 노드를 삭제한다.

머리노드의 다음 노드가 꼬리 노드이면 비어있는 스택이다. 


출력



POP하고 다시 출력








 


이진탐색트리(Binary Search Tree)




















학습참고 : https://www.youtube.com/watch?v=9ZZbA2iPjtM


Queue 큐




큐는 스택과 다르게 앞뒤가가 뚫린 긴통이라고 보면 된다. 즉 입구와 출구가 다르다.

뒤에서 넣고 앞에서 꺼낸다. 그래서 먼저 들어간 것이 제일 먼저 나오는 FIFO구조이다.


큐를 조작하는 방법에는 put동작과 get동작이 있다.

put  : 뒤에서 넣음

get  :  앞에서 꺼냄




사진 : 위키백과


 

큐 구현방법에는 배열구현과 연결리스트 구현이 있다. 


큐노드 클래스



큐 초기화(이중연결 리스트)


Put

K의 값은 가지는 노드를 만들어 꼬리의 앞에 삽입한다. 


Get

큐가 비었는지 확인하고(머리노드 다음이 꼬리이면 꺼낼 노드가 없다)

C++같은 메모리 직접 관리가 가능한 언어는 당연히 메모리를 삭제했으니 메모리해제과정이 필요하다.


출력


확인

맨처음 put한 데이터가 맨 위에있다.


Get테스트




유니티) 포물선 궤적그리기


포탄을 날리거나 포탄 궤적을 보여주기 위해 사용된다.

유니티에서는 Line Renderer을 이용해봤다.



나는 포탄궤적이 아닌 캐릭터가 날아갈 점프 궤적을 그리는데 이용했다.


스크립트


셋팅


playerPlane :  라인이 그려질 plane(땅)의 위치를 셋팅해준다. 궤적이 시작하는 위치


마우스로 캐릭터 방향바꾸기 글에서 학습한적이 있었다.

https://funfunhanblog.tistory.com/40 (평면을 결정하는 최소 조건)


targetPoint는 포물선이의 끝점이된다. (내 게임에서는 노란색 circle에서 최종 위치를 받아온다)


체크



Raycast를 통해 마우스가 위치하는 곳을 가져온다. 

center : 시작벡터와 착지위치벡터의 합에 1/2은 위치가 포물선의 중간위치가 된다.

targetRotation : 라인위치와 최종위치를 빼면 포물선의 방향백터를 구할 수 있다. 

(벡터OA -벡터 OB = 벡터BA) center가 아닌 캐릭터의 위치로 계산해도됨)

Physics.Linecast : 포물을 그리는 라인에 물체가 걸리면 부딪힌 지점을 넘겨준다.



실제로 궤적(라인)을 그리는 부분




theArc : 두 벡터 사이를 원하는 간격으로 보간 부분

(이해를 못함.. 이쪽은 공부가 필요하다)

lineRenderer.SetPosition : 위에서 구해준 라인의 벡터들의 위치를 설정



학습참고 : http://maedoop.dothome.co.kr/660









이진트리 구현


순회종류의 맞게 출력결과를 보기 위해 테스트해봤다.


트리는 목적/구성에 따라 맞는 순회방법을 결정해주어야한다.




테스트예제



레벨그림



결과

1. 전위순회(Preorder) - 루트 -> 왼쪽서브트리 -> 오른쪽 서브트리

2. 후위순회(Posterorder) - 왼쪽서브트리 -> 오른쪽서브트리 -> 루트 

3. 중위순회(Inorder) - 왼쪽서브트리 -> 루트 -> 오른쪽서브트리


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

이진탐색트리(Binary Search Tree)  (0) 2019.03.22
Queue 큐_이중연결리스트 구현  (0) 2019.03.21
트리구조_이진트리(Binary tree)  (0) 2019.03.18
Tree구조  (0) 2019.03.17
알고리즘)재귀호출  (0) 2019.03.10

트리


어떤 요구조건을 만족하는 정점과 선분들의 비어있지 않는 집합

정점은 또한 노드라고도 표현한다.


노드 : 동그라미 하나하나 트리의 요소를 나타냄

루트노드 : 부모노드의 최상단

단말노트 : 자식이 없는 노드

서브트리 : 트리안에 존재하는 트리

트리의 높이 : 가장 최상단부터 레벨을 1,2,3이라 매긴다.



트리의 성질

트리에는 어떤 두 노드를 연결 하는 경로는 한개다. (부모-자식간 양방향의 경로가 있어도 경로는 한개)



최소 공통 선조

어떤 두 노드는 최소 공통 선조를 지님.

루트가 최소공통선조가 되거나 근의 자식들중의 부분트리에 두 노드가 속하게 되므로 최소 공통선조는 항상 존재

각 노드가 최소공통 선조에 이르는 경로를 연결하면 두 노드간 최소 경로가 된다.



이진트리(Binary tree)


각각의 노드가 최대 두 개의 자식 노드를 가지는 트리 자료 구조로, 자식 노드를 가지는 트리 구조로,

 자식 노드를 각각 왼쪽 자식 노드와 오른쪽 자식 노드라고 한다.


이진트리 순회방법

목적/구성에 따라 순회하는 방법이 다르다.(기준은 루트노드 기준)


1. 전위순회(Preorder) - 루트 -> 왼쪽서브트리 -> 오른쪽 서브트리

F, B, A, D, C, E, G, I, H (root, left, right)


2. 중위순회(Inorder) - 왼쪽서브트리 -> 루트 -> 오른쪽서브트리

A, B, C, D, E, F, G, H, I (left, root, right)


3. 후위순회(Posterorder) - 왼쪽서브트리 -> 오른쪽서브트리 -> 루트

A, C, E, D, B, H, I, G, F (left, right, root)




학습참고 :

https://ko.wikipedia.org/wiki/%ED%8A%B8%EB%A6%AC_%EC%88%9C%ED%9A%8C

https://coderkoo.tistory.com/9


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

이진탐색트리(Binary Search Tree)  (0) 2019.03.22
Queue 큐_이중연결리스트 구현  (0) 2019.03.21
트리구조_이진트리(순회방법)_연습  (0) 2019.03.19
Tree구조  (0) 2019.03.17
알고리즘)재귀호출  (0) 2019.03.10

Tree구조



naver


트리는 2차원적 비 선형구조이다. 

나무의 모양을 거꾸로 뒤집어 놓은 모양이다. 

뿌리가 가장 위에 있으며 가지(link)들은 밑으로 벌어지며 향한다.



트리구조에서 쓰는 용어


노드 :  어떤 정보를 담고 있다

링크 :  노드간의 연결을 나타냄

경로 : 나무 내에서 링크에 의해 연결된 일년의 노드의 집합




루트(root) : 나무의 제일 위에 있는 있는 노드, 뿌리노드라고 하며 이 뿌리 노드로부터 다른 노드에 이르는 경로는 오직 하나 밖에 존재하지 않는다. ex 위 그림에서는 0노드가 루트(뿌리)노드


부모자식관계  :  노드 1(부모)과 3,4(1노드의 자식)은 부모 자식관계이다.


조부모노드 : 두 단계 위에있는 노드를 가리킬때 ex 노드1은 노드7의 조부모


레벨 : 뿌리 노드로부터 현재 노드까지 의 경로를 거치는 동안의 노드 수를 말한다. 즉 뿌리노드로부터 얼마나 떨어져 있는가 ex 밑에서 부터 4레벨(7,8,9,10,11,12,13,14),3레벨(3,4,5,6),2레벨(1,2),1레벨(0) 올라감


형제관계 :같은 레벨에 있는 노드들은 형제 관계이다. ex (1, 2)노드들은 형제관계 (3 , 4 , 5 , 6 )노드들은 형제관계


종료 노드 : 자식이 없는 노드 ex (7,8,9,10,11,12,13,14)노드



트리의 성질


-나무 구조에서 한 노드에서 다른 노드로 가는 경로는 유일하다.

(나무를 타는 경로가 중복되지않고, 되돌아감이 없다면)

나무구조의 임의의 두 노드는 최소 공통 선조(두 노드가 갖는 가장까운 부모 노드)를 갖는다. 



-N개의 노드를 갖는 나무는 N-1개의 링크를 가진다.

트리구조는 뿌리 노드를 제외하고 모든 노드가 자신의 선조를 향해 하나의 링크를 갖는다.

그래서 N개의 노드를 가진 나무는 N-1개의 링크를 가진다



-꽉 찬 이진트리는 레벨이 d라고 할때 노드의 수는 N이 다음과 같은 조건을 만족한다.




학습참고 : C로배우는 알고리즘_이재규



구글독스 데이터 저장



인게임 데이터를 구글폼과 연동해 저장하기


로그인시스템과(실제 게임과 로그인은 많이 다르 지만) 스코어를 저장시스템을 만들기



1. 구글독스 설정

데이터를 저장할 장소와 입력을 받을 매체를 설정하는 부분이다.


1). 구글설문지

이 구글 설문지가 유니티에서 데이터 입력매체가 되는 것이다.


저장할 데이터 항목 맞게 질문을 추가해준다 (단답형으로 변경)  



질문항목을 다 채웠다면 '응답'탭을 누르고 스프레드 시트를 만들어준다. 


2). 구글스프레드시트

스프레드시트를 열어보면 자동적으로 질문항목들이 채워져있다.

이 부분이 설문지로 받은 입력데이터들이 저장 될 곳이다. 


2. 스크립트 부분


유니티 스크립트에서 데이터를 입력매체(설문지)를 연결하는 방법



SAVE_URL : 데이터폼의 키 값이다. 


설문지에서 '미리보기' 클릭 후 페이지소스 보기

form action = 이 부분이다.


entry. : 스프레드 시트 각각 열의 항목



만약 질문 항목이 5개라면 entry 값도 5개 인 것이다





실행 테스트 


5가지 항목을 추가 했다.

nID : 로그인 정보가 입력된 번호(순서)

sID : 아이디

sPassword : 패스워드

sDeviceID : 기기 아이디

eOS : 운영체제 (안드로이드,IOS,PC)


이 부분은 디바이스 아이디를 받아오고, OS정보를 파악 


유니티 INPUT.TEXT를 통해 아이디와 패스워드를 입력한다.



결과

타임스탬프는 설문지를 통해 스프레드 시트를 만들면 자동으로 생성되는 항목인데 데이터를 입력한 시간을 입력해준다.


이 부분은 데이터를 입력하는 부분만 있다. 

로그인이라면 입력한 아이디와 패스워드가 맞는지 체크하는 기능이 있어야한다.  

그럴라면 데이터를 스프레드시트에서 받아오는 기능이 필요하다.

병합정렬(merge sort)


병합의 개념 : 두개 이상의 정렬된 자료집합을 하나의 정렬된 자료집합으로 합치는 것


병합의 방법 : 대상 자료집합이 정렬된 상태이므로 순차적으로 작은 자료를 뽑아서 놓으면됨



장점 :안정적인 정렬 방법

데이터의 분포에 영향을 덜 받는다.

즉, 입력 데이터가 무엇이든 간에 정렬되는 시간은 동일하다. (O(nlog₂n)로 동일)


단점 :

만약 레코드를 배열(Array)로 구성하면, 임시 배열이 필요하다.

제자리 정렬(in-place sorting)이 아니다.

(나열된 것 중에 가장 작은 또는 가장 큰 것을 선택하여 앞 또는 끝으로 보내는 작업을 반복하여최종적으로 크기 순서대로 정렬이 되는 방식.(오름차순 , 내림차순))



두 집합이 병합되는 부분


i: 정렬된 왼쪽 리스트에 대한 인덱스

j: 정렬된 오른쪽 리스트에 대한 인덱스

k: 정렬될 리스트에 대한 인덱스



실제로 숫자들이 정렬되는 과정




거품 정렬


배열의 인접 요소를 비교하여 교환하는 모양이 보글보글하다고 해서 붙여진 이름이다.


거품 정렬은 인접한 배열의 요소를 비교 교환하여 전체적으로는

대충 정렬을 하면서 최대값을 배열의 제일 뒤로 보내는 것을 반복한다.



거품 정렬의 개선

아래 함수를 추가하여 정렬이 되어있는 체크





정렬이 되어 있다면 바로 빠져 나오도록

(but 정렬이 되어있을때는 시간이 적지만 그렇지 않을 경우는 더 시간이 느려진다.

s=0과 if s인지 판단하는 문장이 추가 됐기 때문)



3차원 물체를 그려지는 과정

 

컴퓨터 모니터는 평면이기 때문에 렌더링 된 3D 장면들은 

2D 이미지로 컴퓨터 스크린에 투영되어야 한다.

 

스크린에 표시되기 까지 아래 변환 과정이 필요하다.

 

월드 변환 -> 카메라 변환 -> 투영 변환 순서

즉 오브젝트 공간 -> 월드 공간 -> 카메라 공간 -> 클립 공간

 

그 중에서 투영 변환은 월드의 모든 물체를 카메라 공간으로 이동시킨 뒤,

 2차원 평면으로 그려주기 위한 변환이다.

 

 

클리핑 : 절두체의 경계에 걸치게 되면 걸쳐진 바깥쪽 부분은 잘려 버려지게 되는 것 (클립 공간에서 수행)

 

월드 변환

 

오브젝트 공간은 3차원 세상에서 표현될 각각의 오브젝트들이 정의된 공간이다.

하나의 물체가 자신만의 공간에서 고정 불변의 좌표를 가지는 것이라고 생각할 수 있다. 

이러한 오브젝트 공간의 물체는 다른 오브젝트의 공간과 전혀 관계가 없기 때문에 3차원 세상에서 표현하고자

하는 모든 오브젝트들은 하나의 단일 공간으로 변환 할 필요가 있다. 

 

이 때 모든 대상들이 모아질 공간을 월드 공간이라 하고, 각각의 대상을 월드 공간에 모으는 과정을 월드 변환이라 한다.

 

 

카메라 변환(뷰 공간 / 카메라 공간)

 

모든 물체가 월드 공간에 모아지면 카메라가 볼 수 있는 영역의 공간을 뷰 공간이라고 한다. 월드 공간의 모든 물체를 카메라 공간으로 변환하게 되면 보다 효율적인 랜더링 알고리즘을 설계할 수 있기 때문에 이 카메라 변환이 필요하다.

 

 

투영 변환

 

투영 변환은 이러한 원근법을 구현하기 위해 카메라 공간에서 정의된 절두체를 축에

나란한 직육면체 볼륨으로 변경하여 카메라 공간의 모든 물체를 3차원 클립 공간으로 변환하는 것을 의미한다.

 

여기서 투영 변환이라는 이름과 달리 3차원 카메라 공간의 물체를 2차원 평면으로 투영하는 것이 아니라 

또 다른 3차원 물체로 '변형'이다.

이 물체들은 관찰해보면 절두체 뒤쪽에 있던 영역의 폴리곤은 상대적으로 작아진다.(원근법 적용)

 

투영변환을 통해 원근법이 적용된 3차원 물체들은 직육면체 볼륨의 3차원 클리핑 공간으로 정의되어있다.

그러나 최종적으로 필요한 것은 2차원 사각 영역이다.

 

3차원을 2차원 공간으로 변환이 필요한 것이다. (단순히 z값을 버린 면? 원근법이 적용이 불가)

 

바로 Z좌표로 모든 성분을 나눠버리면?

실제 Z값은 투영 변환행렬을 곱한 후 동차좌표계의(x,y,z,w)에서 w성분에 저장되어 있기 때문에 투영변환 이후 w에 저장된 값을 좌표를 나누는 것이다.

 

나눗셈을 하고 나면 (x, y, z, w)의 동차 좌표 게에서 (x, y, z)의 직교 좌표계로 변화하게 되는데 이를 NDC공간이라고 하는 것이다.(NDC공간은 좌표의 XY범위가 [-1,1]이고, Z범위가 [0,1]이다.)

 

 

 

뷰포트 변환

 

컴퓨터 화면 상의 윈도는 스크린 공간을 갖는데, 이 스크린 공간 내에 2차원 이미지가 그려질 뷰포트가 정의되는데 NDC공간의 물체들을 스크린 공간으로 이전시키는 변환을 뷰포트 변환이라 한다. 

 

 

 

 

 

  출처 http://www.songho.ca/opengl/gl_projectionmatrix.html

https://jidon333.github.io/blog/Rendering-pipeline

재귀호출



자기자신을 호출한다.

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



잘못된 재귀 호출

문제 해결에 변화없는 호출

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

+ Recent posts