게임프로그래밍고급 4~5주차
지난 시간의 복습
내적
두개의 벡터를 하나의 스칼라 값으로 표현한 연산.
- 두 벡터 사이의 사이각을 계산 가능하다.
- 플레이어가 바라보는 방향을 기준으로 상대방이 앞인지 뒤인지 판단 가능하다.
- 플레이어가 바라보는 시야각 안에 들어왔는지 판단 가능하다.
외적
두 개의 벡터에서 벡터 한 개를 만드는 연산.
두 개의 벡터 A, B에 수직이고, A, B로 이루어진 평행사변형의 넓이와 동일한 면적을 가진 벡터.
- A와 B벡터의 외적은 벡터 A와도 수직이고, 벡터 B와도 수직이다.
- 평면의 법선 벡터를 구할 때 사용된다.
- 플레이어를 기준으로 상대방이 오른쪽에 있는지 왼쪽에 있는지 판별한다.
- 삼각형 안에 점이 포함되어 있는지 판단할 때 사용된다.
행렬
- Transpose Matrix (전치 행렬)
- Identity Matrix (단위 행렬)
- Inverse Matrix (역행렬)
- 행렬의 덧셈 & 뺄셈
- 행렬의 곱셈
삼각함수
- 삼각형의 개념 : 세개의 점과 세개의 변으로 이루어진 다각형, 내각의 합은 180도. 평면상에 존재한다.
- 피타고라스의 정리 : 직각삼각형을 기준으로, 빗변의 제곱의 합이 다른 두 변의 제곱과 같다.
- sin = 높이/빗변
- cos = 밑변/빗변
- tan = 높이/밑변
- unit circle : 단위원, 반지름의 길이가 1인 원
- 삼각함수의 그래프
절차적 모델링의 의미
절차적 모델링 (Procedural Modeling)
모델러가 3D Max와 같은 툴을 사용해서 모델링을 만드는 것이 아니라, 프로그래머가 알고리즘을 사용하여 모델링을 만드는 것. 자동화시킨 알고리즘을 통해서 모델을 제작한다.
- 게임의 지형 생성, 식물의 조형, 도시의 구성을 구현한다.
- 게임 플레이 중 스테이지의 구조 변동을 통한 콘텐츠의 디자인이 가능하다.
컴퓨터를 이용한 디자인 처리
파라미터를 이용해서 특정 모델의 구조나 크기를 변경 가능하다.
유연하게 조작 가능한 모델을 콘텐츠에 삽입 가능하다.
- 나무 생성, 특정 위치에 빌딩이 늘어선 마을 구성.
- 다양한 형태의 모델을 미리 넣으면, 데이터의 용량이 커진다.
- 몇 가지 모델의 다양성을 통해서 늘리는 방법이 가능하다.
Mesh Class
모델 데이터를 관리하는 유니티 클래스
- 모든 정점은 하나의 배열로 저장된다.
- 개별 삼각형은 정점 배열의 인덱스로 저장된다.
- 모든 삼각형들은 인덱스(음이 아닌 정수)의 긴 배열로 저장된다.
- 정점 3개마다 하나의 삼각형 (0, 1, 2 & 3, 4, 5)
Model Data
삼각형들로 구성되어져 있는 형태, 정점, UV 좌표, 법선 벡터
Quad
4개의 정점, 2개의 삼각형으로 구성된 가장 기초적인 도형 중 하나.
- 정점 데이터 : 4개
- UV 데이터 : 4개
- 법선 데이터 : 4개
- 삼각형 : 2개
CCW (Counter Clock Wide)
3개의 점을 이은 직선의 방향을 알고자 할 때 유용한 기하 알고리즘.
경우의 수는 외적을 통하여 구할 수 있으며, 총 3가지가 있다. (시계, 반시계, 직선)
외적의 결과에 따라 의미하는 바가 다르다.
- 음수 : 시계 방향
- 0 : 직선
- 양수 : 반시계 방향
Quad 정의 순서
- Mesh 클래스를 이용한 인스턴스 생성
- Quad의 4개의 정점을 Vector3 배열로 생성
- UV 좌표 데이터
- 법선 데이터
- 인덱스 데이터
Mesh 인스턴스 생성
// mesh 인스턴스 생성
var mesh = new Mesh();
// 메쉬 크기
float size = 10.0f;
// 메쉬 크기의 절반
var hsize = size * 0.5f;
Quad의 4개의 정점을 Vector3 배열로 생성
var vertices = new Vector3[]
{
new Vector3( hsize, hsize, 0f), // 0번 정점, quad 오른쪽 위의 위치
new Vector3(-hsize, hsize, 0f), // 1번 정점, quad 왼쪽 위의 위치
new Vector3(-hsize, -hsize, 0f), // 2번 정점, quad 오른쪽 아래의 위치
new Vector3( hsize, -hsize, 0f) // 3번 정점, quad 왼쪽 아래의 위치
};
UV 좌표계
Texture를 적용하기 위해서는 텍스처 맵핑 좌표계를 설정해야 한다.
2D 텍스처 좌표계는 U, V값을 통해서 결정된다.
- 0~1 사이의 실수값이다.
- X축은 U, Y축은 V
UV 좌표 데이터
var uv = new Vector2[]
{
new Vector2(1f, 1f), // 0번 정점의 uv 좌표
new Vector2(0f, 1f), // 1번 정점의 uv 좌표
new Vector2(0f, 0f), // 2번 정점의 uv 좌표
new Vector2(1f, 0f) // 3번 정점의 uv 좌표
};
법선 데이터
var normals = new Vector3[]
{
new Vector3(0f, 0f, -1f), // 0번 정점의 법선
new Vector3(0f, 0f, -1f), // 1번 정점의 법선
new Vector3(0f, 0f, -1f), // 2번 정점의 법선
new Vector3(0f, 0f, -1f) // 3번 정점의 법선
};
인덱스 데이터
var triangles = new int[]
{
0, 3, 1, // 첫번째 삼각형
1, 3, 2 // 두번째 삼각형
}
최종 Mesh 인스턴스 설정
mesh.vertices = vertices;
mesh.uv = uv;
mesh.normals = normals;
mesh.triangles = triangles;
// mesh bounding box 다시 계산
mesh.RecalculateBounds();
// mesh filter에 설정
GetComponent<MeshFilter>().mesh = mesh;
Unity로 Mesh 만들기 실습
1. Quad 내용을 참고해 Unity를 이용해 triangle 한 개를 생성해 보세요.
2. 정점의 위치는 (3, 3, 0), (-3, 3, 0), (3, -3, 0).
- Project 생성
- GameObject 생성
- Mesh Filter 컴포넌트 추가 : Mesh 구성
https://docs.unity3d.com/kr/2020.3/Manual/class-MeshFilter.html
메시 필터 - Unity 매뉴얼
메시 필터(Mesh Filter) 는 에셋에서 메시를 가져와 화면에 렌더링하기 위해 메시 렌더러에 전달합니다.
docs.unity3d.com
- Mesh Renderer 컴포넌트 추가 : Mesh 렌더링
https://docs.unity3d.com/kr/2020.3/Manual/class-MeshRenderer.html
메시 렌더러 - Unity 매뉴얼
메시 렌더러는 메시 필터에서 지오메트리를 가져와 게임 오브젝트의 Transform 컴포넌트에서 정의된 위치에서 렌더링합니다.
docs.unity3d.com
절차적 모델링 정의 : Plane
여러개의 vertex와 UV가 모아져서 그려지게 된다.
- Quad를 나열하여서 만들어 놓은 형태
- 세로 방향의 정점의 수 : 3개
- 가로 방향의 정점의 수 : 3개
- 너비, 높이
Mesh 인스턴스 생성
// mesh 인스턴스 생성
var mesh = new Mesh();
// 메쉬 구성 요소 준비
var vertices = new List<Vector3>();
var uv = new List<Vector2>();
var normals = new List<Vector3>();
int widthSegments = 3;
int heightSegments = 3;
float width = 10.0f;
float height = 10.0f;
// 격자상에 정점을 배열할 위치의 비율(0,.0 ~ 0.1)을 산출하기 위한 행-열 개수의 역수
// widthSegments,heightSegments의 값이 이용되므로 유한 실수가 나오도록 조심
var winv = 1f / (widthSegments - 1);
var hinv = 1f / (heightSegments - 1);
정점 정보 구성
for (int y = 0; y < heightSegments; y++)
{
// 행의 위치 비율(0.0 ~ 1.0)
var ry = y * hinv;
for (int x = 0; x < widthSegments; x++)
{
// 열의 위치 비율(0.0 ~ 1.0)
var rx = x * winv;
vertices.Add(new Vector3((rx - 0.5f) * width, 0f, (0.5f - ry) * height));
uv.Add(new Vector2(rx, ry));
normals.Add(new Vector3(0f, 1f, 0f));
}
}
삼각형 정보 구성
var triangles = new List<int>();
for (int y = 0; y < heightSegments - 1; y++)
{
for (int x = 0; x < widthSegments - 1; x++)
{
int index = y * widthSegments + x;
var a = index;
var b = index + 1;
var c = index + 1 + widthSegments;
var d = index + widthSegments;
// 삼각형 2개는 Quad 1개임.
triangles.Add(a);
triangles.Add(b);
triangles.Add(c);
triangles.Add(c);
triangles.Add(d);
triangles.Add(a);
}
}
최종 Mesh 인스턴스 설정
mesh.vertices = vertices.ToArray();
mesh.uv = uv.ToArray();
mesh.normals = normals.ToArray();
mesh.triangles = triangles.ToArray();
// mesh bounding box 다시 계산
mesh.RecalculateBounds();
// mesh filter에 설정
GetComponent<MeshFilter>().mesh = mesh;