C++ (15) 썸네일형 리스트형 렌더링 파이프라인 렌더링 파이프라인은 게임 상의 3D 월드를 2D 화면으로 출력하는 방법이다. 과정 로컬 스페이스 → 월드 스페이스 → 뷰 스페이스 → 후면 추려내기 → 조명 → 클리핑 → 투영 → 뷰포트 → 래스터라이즈 로컬 스페이스 : 물체를 표현할 때 기준이 되는 좌표계이다. 자체 좌표계를 이용한다. (월드 고려 X) 월드 스페이스 : 물체의 로컬 좌표를 월드상의 좌표로 변환한다. 뷰 스페이스 : 카메라를 월드 상의 원점으로 삼고, 카메라를 기준으로 물체들이 회전하는 영역이다. 후면 추려내기 : 일반적으로 카메라는 폴리곤의 후면을 볼 수 없기 때문에 불필요한 후면 폴리곤을 잘라내서 렌더링에서 제외한다. 조명 : 광원은 월드 스페이스 내에 정의되지만 뷰 스페이스 변환 시 변환된다. 물체에 명암을 추가한다. 클리핑 : .. 더블 버퍼링과 스왑 체인 더블 버퍼링에 대해 질문을 받았는데 스왑 체인 썼던 것밖에 기억이 안나서 스왑 체인에 대해서 설명해도 되겠냐고 물어보고 설명을 했었다. 지금 생각해보니 API 때는 (스왑체인이 아닌) 더블 버퍼링, DirectX 때는 스왑 체인 방식을 썼었는데 API 배워놓고서 까먹었던 것 같다. 더블 버퍼링 화면이 갱신될 때마다 깜빡거리는 현상을 방지하기 위해 back buffer(오프 스크린)에 먼저 이미지를 그리고, 이미지가 완성되면 기존 화면(실제 화면)에 옮겨그리는 방법이다. 스왑 체인 스왑 체인도 더블 버퍼링의 일종이다. front buffer와 back buffer 두 개를 가지면서 실제 화면에는 front buffer를 출력하고 이미지는 back buffer에 그리는 방식이다. 포인터를 통해 접근하며 ba.. 게임에 디자인 패턴 적용하기 1. 싱글톤 패턴 (Singleton Pattern) 가장 많이 사용한 패턴이다. 주로 키 입력 매니저, 타임 매니저 등 하나만 있으면 되는 매니저 클래스를 만들 때 사용했다. 예를 들어 싱글톤으로 사운드 매니저를 만든 경우, 플레이어나 몬스터 등의 클래스에서 효과음이 필요할 때 마다 사운드 매니저로부터 인스턴스를 얻어와 소리를 재생하는 방식을 썼다. 장점 : 편한 글로벌 액세스 제공, 단일 인스턴스 보장, 처음 액세스 시 생성되므로 객체 생성 시간이 적음 단점 : 개방 - 폐쇄 원칙에 어긋남, 높은 결합도를 가짐, 멀티 쓰레드 환경에서 동기화 처리를 하지 않으면 인스턴스가 두 개 이상 만들어질 가능성이 있음 2. 추상 팩토리 패턴 (Abstract Factory Pattern) API 배울 때 다양한 .. 정적 바인딩, 동적 바인딩, 가상 함수 테이블 컴파일을 하면 코드에서 함수를 호출하는 부분에 해당 함수가 저장된 메모리 주소 값이 저장된다. 프로그램 실행 → 함수 호출 → 함수가 저장된 주소로 점프 → 함수 실행 → 원래 위치 노란 표시를 한 부분이 바인딩(Binding)이다. 바인딩은 함수 호출 시 함수가 저장된 메모리 주소로 연결시켜주는 것이다. 정적 바인딩 동적 바인딩 바인딩이 결정되는 시점 컴파일 컴파일 시 주소를 저장할 메모리 공간(4bytes)만 가지고 있다가 런타임에 결정 할당하는 것 일반 함수 가상 함수 장점 속도가 비교적 빠름, 안정적 실행 도중 호출하므로 유연함 단점 유연하지 않음 예상치 못한 타입의 경우 안정적 가상 함수 테이블 클래스에 가상 함수가 하나라도 있다면 컴파일러는 메모리 어딘가에 가상 함수 테이블을 만들고, 해당 클.. BFS 구현 (큐) 그래프는 이 블로그에서 가져온 그림을 참고했다. #include #include #include // To measure execution time #define SIZE 9 using namespace std; void BFS(vector _graph[], bool _visit[]) { queue bfsQueue; bfsQueue.push(1); int currentIndex = 0; while(!bfsQueue.empty()) { currentIndex = bfsQueue.front(); _visit[currentIndex] = true; cout DFS 구현 (재귀, 스택) 그래프는 이 블로그에서 가져온 그림을 참고했다. 1. 재귀 함수를 이용한 방식 #include #include #include // To measure execution time #define SIZE 9 using namespace std; void DFS(vector _graph[], bool _visit[], int _startIndex) { if (_visit[_startIndex]) return; _visit[_startIndex] = true; cout A* 알고리즘 설명 A* 는 BFS 방식을 이용해 최단 경로를 구하는 알고리즘이다. 공식 F = G + H G는 시작점으로부터 해당 노드까지의 이동 비용, H는 해당 노드로부터 도착지까지의 예상 이동 비용이다. (휴리스틱에 대한 설명) 단점 맵의 크기가 커지면 Open Queue와 Closed Queue에 수 많은 노드들이 들어갈 수 있어서 메모리를 많이 차지하게 된다. 또한 시작점부터 도착지까지의 경로가 존재하지 않는 경우 모든 노드를 검색하므로 비효율적이다. + 맵을 어떤 자료구조로 구현하는게 좋을까 생각해봤는데 굳이 노드를 더 추가할 계획이 아니라면 배열을 사용해 인덱스 접근으로 탐색하는게 좋을 것 같다. - vector : 나중에 노드를 추가하는 경우가 아니라면 굳이? - list : 인덱스가 없어서.. 인접한 노드.. A* 알고리즘에서 어떤 휴리스틱 함수를 사용해야할까 내가 만든 길찾기에서는 그냥 피타고라스 정리로 휴리스틱을 사용했는데 검색해보니 다양한 방법들이 있다는 것을 알게 되었다. A* 의 성능에는 휴리스틱이 많은 영향을 끼치므로 좋은 휴리스틱을 사용해야한다. 휴리스틱 정확도가 낮을 수록 최단거리를 얻을 수 있지만 A*의 전체 속도가 느려지고, 휴리스틱 정확도가 높을 수록 A*의 전체 속도가 빨라지지만 최단거리를 포기해야한다. (출처) 따라서 속도와 정확도의 균형이 중요하다. 두 점 좌표 a(x1, y1, z1), b(x2, y2, z2)이고 a와 b의 휴리스틱 거리값이 distance라고 친다. 1. 맨하탄 방식 distance= fabs(x2 - x1) + fabs(y2 - y1) 목표까지 도달하는 가로와 세로의 길이만을 고려한다. 장애물은 고려하지 않는다... 이전 1 2 다음