Post Lists

2019년 7월 1일 월요일

1. Geometry

https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry

Geometry

목차
Points, Vectors and Normals
Coordinate Systems
Math Operations On Points and Vectors
Matrices
How Does Matrix Work : Part 1
How Does Matrix Work : Part 2
Transforming Points and Vectors
Row Major vs Column Major Vector
Matrix Operations
Spherical Coordinates and Trigonometric Functions
Creating an Orientation Matrix or Local Coordinate System
Transforming Normals
Source Code

============================================
Chapter 1 - Points, Vectors and Normals

경고의 말
~

Introduction to Geometry
~~ 수학에 편안함을 느끼지 않고, 행렬이 무엇인지 이해하지 않기 때문에, 컴퓨터 그래픽스 프로그래밍이 너를 위한 것이 아니라 생각한다면, 지금 포기해라.

~~ 이 수업은 또한 선형 대수에서와의 다른 conventions을 설명할 것인데, 그것은 CG 연구자들이 그들의 문제들을 해결하고 코드를 작성할 때 몇년 간 사용해왔던 것이다. 너는 이러한 conventions을 인지할 필요가 있는데, 그것들이 종종 책에서 언급되지 않기 때문이다 (그리고 웹에서 좋지 않게 문서화 되어있다). 이러한 convetions는 중요하다; 너가 또 다른 개발자의 코드 또는 기술을 읽거나 사용하기 전에, 너는 처음에 그들이 사용하는 컨벤션이 무엇인지를 체크해야 한다.


선형 대수가 무엇인가? Introduction to Vectors
a vector는 numbers의 한 배열로 나타내어질 수 있다.
어떤 요구되는 길이를 가정할 수 있는 숫자들의 이 배열은 또한 수하에서는 가끔씩 tuple이라고 불려진다. 만약 우리가 그 벡터의 길이에 대해 구체적이길 원한다면, 우리는 n-tuple이라고 말하기를 선택할지도 모른다. 거기에서 n은 벡터 원소들의 개수를 나타낸다.

~~ 한 벡터의 내용을 변환 (transfomring) 하는 과정은 linear transformation이라고 불려지는 것을 통해 이뤄진다.

Points and Vectors
point는삼차원 공간에서 position(위치)이다. 반면에, vector는 보통 삼차원 공간에서 direction(방향)을 나타낸다 (그리고 어떤 일치하는 크기 또는 사이즈).

A Quick Introduction to Transformations

생략

Normals
normal은 CG에서 (그리고 기하학에서) 한 기하학적 오브젝트의 표면의 방향을 그 표면에 있는 한 점에서 설명하기 위해 사용되는 기술적 용어이다. 기술적으로, 점 P에 있는 한 표면에 대한 surface normal은 점 P에 있는 surface에 접하는 평면과 수직인 벡터로서 보아진다. Normals은 shading에서 중요한 역할을 하는데, 거기에서 오브젝트들의 밝기를 연산하는데 사용된다.

Normals은 한 경고장이 있는 벡터로서 생각될 수 있다: 그것들은 벡터들이 하듯이 같은 방식으로 변환할 수 없다. 이것은 우리가 그것들을 미분하는데 시간이 걸리는 주된 이유 중 하나이다. 너는 Transforming Normals 챕터에서 이 주제에 대해 좀 더 많은 정보를 알게 될 것이다. 지금은, 그것들이 무엇인지 이해하는 것 만이 중요하다.

From Theory to C++
~

Summary
이 첫 챕터로 부터, 너는 수학적으로 한 벡터가 어떤 차원이든지 될 수 있다고 기억해야만 한다. 그러나 CG에서, 우리는 좀 더 구체적인 정의를 사용한다: 한 vector는 3D공간에서 한 방향이다 (그러므로, 세 개의 숫자들에 의해 나타내어 진다). 추가적으로, 우리는 points를 positions의 표현으로 말한다 (또한 3D 공간에서이고, 세 개의 숫자로 나타내어 진다). Homogeneous points는 4개의 숫자로 나타내어지지만, 우리가 나중에 공부할 특별한 경우이다.

점과 벡터들은 linear transformations을 사용하여 변환될 수 있다.
~~

What's Next?
우리가 아직 설명하지 않은 한 가지 중요한 것은 점과 벡터들을 정의하는 그 세 개의 숫자들이 무엇을 나타내는지 이다. 이 숫자들은 레퍼런스와 관련하여 (또한 가끔씩 원점으로불려진다) 한 점의 좌표들을 나타낸다 (2D와 3D 공간에서).  기술적으로 우리가 coordinate system(좌표계)라고 부르는, 이 레퍼런스는 우리의 다음 챕터의 주제이다.

============================================
Chatper 2 - Coordinate System

Introducing Coordinate Systems
points나 vectors에서 쓰이는 숫자들이 무엇은 의미하는가? 각 숫자는 한 line의 원점에서 그  라인에 있는 점의 위치까지의 signed distance(양의 거리)를 나타낸다.

~~~ "좌표"는 원점에서 그 점까지의 양의 거리라고 말하는 다른 방법이 될 수 있다.

Dimensions and Cartesian Coordinate Systems
~~~ 따라서, 두 개의 축들을 배치함으로써, 우리는 평면이라고 불려지는 2차원 공간을 정의했다.

그래서 만약 너가 좌표계 A에서의 P의 위치를 안다고 한다면, 또 다른 좌표계 B에서 같은 점의 위치를 찾기 위해 너가 해야 할 것은 무엇인가? 이것은 CG에서 (그리고 일반적으로 수학에서) 매우 중요한 연산을 나타낸다. 우리는 곧 한 좌표계의 한 점의 좌표를 다른 것으로 변환하는 map을 발견하는 방법을 따라서 그 이유를 알게 될 것이다 (Transforming Points and Vectors 챕터참조)

The Third Dimension
기하학에서, 이 3D 좌표계는 Euclidean space라고 좀 더 공식적으로 알려진 것을 정의한다.

우리는 이 챕터의 이 부분을 좌표계의 좀 더 공식적인 정의에 관심이 있는 너를 위해 이 문단을 결론 짓는다. 선형 대수에서, 세 개의 축 (1D와 2D의 경우 개별적으로 한 개 또는 두 개의 축)은 우리가 그 좌표계의 basis(기저)라고 부르는 것을 형성한다. 한 기저는 선형으로 독립적인 벡터들의 한 집합이고, 선형 결합에서, 주어진 벡터 공간에서 모든 벡터를 나타낼 수 있다. 한 집합으로부터의 벡터들은 그 집합에 있는 벡터들 중 어떠한 것도 다그 집합에 있는 다른 벡터들의 선형 결합으로 쓰여질 수 없는 다면 선형으로 독립이라고 말해진다. 기저 변환, 또는 좌표계의 변환은 수학과 그래픽스 파이프라인에서 흔한 연산이다.

Left-Handed vs Right-Handed Coordinate Systems

The Right, Up and Forward Vectors

The World Coordinate System

Things We need to Remember


============================================

Chatper 3 - Math Operations on Points and Vectors

Dot Product
~~ 두 개의 벡터가 서로 (A.B)에 수직일 때, 이러한 두 벡터 사이의 내적은 0이다. 두 벡터가 반대 방향 (A.C)을 가리키고 있을 때, 내적은 -1을 반환한다. 그것들이 정확히 같은 방향 (A.D)를 가리키고 있을 떄, 그것은 1을 반환한다. 그것은 또한 두 벡터 사이의 각도를 찾거나 한 벡터와 한 좌표계의 축 사이의 각도를 연산하는데 매우 잘 사용된다 (그리고 이것은 한 벡터의 좌표를 구면 좌표계로 변환할 때 유용하다. 이것은 trigonometric functions에서 설명된다).

Cross Product



============================================
Chapter 4 - Matrices









============================================





Scratchapixel 공부 계획

https://www.scratchapixel.com/

Scratchapixel 사이트를 통해, 그래픽스 공부를 다시 처음부터 해 갈것이다.
이 사이트 외에도 graphicscodex, Peter Shirley's resource, Real-Time Rendering, PBR, 등 좋은 리소스들은 많이 있다. 그러나, 그런 것들을 들어가기 전에,
이 사이트를 통해서 필요한 어려운 내용들을 알아 갈 것이다.

현재 올라와 있는 튜토리얼들 모두를 하고 싶기 때문에,
이미 알고 있는 내용들은 그래도 한 번더 읽어보도록 한다.
이미 알고 있는 내용들은 전체를 번역하지 않고, 알고 있는
내용만 번역하도록 한다.

초반의 주요 파트들은 이론 위주이기 때문에 결과물이 없을 것이지만,
뒤로 갈수록 결과물을 낼 수 있는 파트가 나오게 된다.
그 경우에, Vulkan API를 통해서 결과물을 만들어 낼 것이다.

2019년 4월 22일 월요일

Vulkan Tutorial (2)

https://vulkan-tutorial.com/Drawing_a_triangle

Drawing a triangle
Base code
General structure
Resource management
malloc으로 할당된 메모리 청크가 free에 대한 호출을 요구하듯이, 우리가 생성하는 모든 Vulkan object는 그것이 더 이상 필요하지 않을 때 explicitly하게 destroyed 되어질 필요가 있다. 현대 C++ 코드에서, <memory> 헤더에서 utilities를 통해 automatic resource management를 하는 것이 가능하다. 그러나 나는 이 튜토리얼에서 Vulkan objects의 할당과 해제에 대해 explicit하는 것을 선택했다. 결국 Vulkan의 niche는 실수를 피하기 위해 모든 연산에 대해 explicit하는 것이다. 그래서 그 API가 어떻게 작동하는지를 배우기 위해 objects의 lifetime에 대해 explicit하는 것은 좋다.

이 튜토리얼을 따르고나서, 예를들어 std::shared_ptr을 오버로드하여, 자동적인 resource management를 구현할 수 있다. 너의 이득을 위해 RAII를 사용하는 것은 더 큰 Vulkan programs을 위해 추천되는 접근법이지만, 학습 목적을 위해서, scenes 뒤에서 무엇이 일어나고 있는지를 아는 것은 항상 좋다.

Vulkan objects는 vkCreateXX같은 함수들로 직접 생성되거나, vkAllocateXXX같은 함수들로 또 다른 오브젝트를 통해서 할당된다. 한 오브젝트가 어느 곳에서 든지 더 이상 사용되지 않는 것을 확시랗게 한 후에,너는 그것에 대응되는 vkDestroyXXX와 vkFreeXXX로 그것을 destroy할 필요가 있다. 이러한 함수들에 대한 파라미터들은 일반적으로 오브젝트들의 유형에 따라 다양하지만,그것들은 모두 하나의 파라미터를 공유한다 : pAllocator. 이것은 너가 custom memory allocator에 대해 callbacks을 명시하게 해주는 optioanl parameter이다. 우리는 튜토리얼에서 이 파라미터를 무시하고, 항상 인자로 null로 넘길 것이다.

Instance

Creating an instance
- 너가 처음 해야 할 것은 instance를 만들어서 Vulkan 라이브러리를 초기화하는 것이다. 그 인스턴스는 너의 어플리케이션과 벌칸 라이브러리 사이의 연결이고, 그것을 생성하는 것은 너의 어플리케이션에 대해 드라이버에게 몇 가지 세부사항을 명시하는 것을 포함한다.

- 인스턴스를 생성하기 위해, 우리의 어플리케이션에 관한 몇 가지 정보로 struct를 채워야만 한다. 이 데이터는 기술적으로 부가적이지만, 그것은 우리의 구체적인 어플리케이션을 위해 최적화하기 위해 드라이버에게 몇 가지 유용한 정보를 제공할지도 모른다. 예를들어, 그 프로그램이 어떤 특별한 행동을 가진 잘 알려진 그래픽스 엔진을 사용하기 때문이다. 이 구조체는 VkApplicationInfo 라고 불려진다.

- 이전에 언급되었듯이, 벌칸에 있는 많은 구조체들은 너가 sType 멤버에서 그 type을 explicitly하게 명시하는 것을 요구한다. 이것은 또한 나중에 extension 정보를 가리킬 수 있는 pNext 멤버를 가진 많은 구조체중의 하나이다. 우리는 여기에서 그것을 여기에서 nullptr로 두어서 기본 초기화를 할 것이다.

- 벌칸에서 많은 정보는 함수 파라미터 대신에 구조체를 통해서 전달된다. 그래서 우리는 한 instance를 생성하는데 있어 충분한 정보를 제공하기 위해, 한 가지 더 구조체를 채워야만 할 것이다. 이 다음 구조체는 부가적이지 않고, 그 벌칸 드라이버에게 어떤 global extensions과 validation layers를 우리가 사용하고 싶은지를 말한다. Global 이란 것은 여기서 그것들이 특정 디바이스가 아닌 전체 프로그램에 적용될 것이라는 것을 의미하고, 이것들은 다음의 챕터들에서 명확해질 것이다.

- 너가 보게 되듯이, 벌칸에서 오브젝트 생성 함수 파라미터들이 따르는 일반적인 패턴은

  • creation info를 가진 구조체에 대한 포인터
  • custom allocator callbacks에 대한 포인터, 이 튜토리얼에서는 항상 nullptr
  • 그 새로운 오브젝트에 대한 handle를 저장하는 변수에 대한 포인터
- 만약 모든 것이 잘되었다면, instance에 대한 handle이 VkInstance에 저장된다.

Checking for extension support
- 한 instance를 생성하기전에, 지원되는extensions의리스트를 얻기 위해, vkEnumerateInstanceExtensionProperties함수가 있다. 그것은 extensions의 개수를 저장하는 변수에 대한 포인터와, extensions의 세부사항을 저장하는 vkExtensionsProperties의 배열엗 ㅐ한 포인터를 필요한다. 그것은 또한 우리가 특정한 validation layer에 의해 extensions을 필터링하게 해주는 한 가지 부가 파라미터를 필요한다. 그리고 우리는 지금은 이것을 무시한다.

Validation Layers

What are validation layers
- Validation layers는 부가적인 연산을 적용하기 위해 Vulkan 함수 호출에 걸려있는 부가적인 컴포넌트들이다. validation layers에서의 흔한 연산들은:

  • 잘못된 사용을 탐지하기 위해 명세에 대해 파라미터의 값을 체크
  • resource leaks를 찾기위해 오브젝트들의 생성과 파괴를 추적
  • 호출이 출발하는 쓰레드들을 추적하여 thread safety를 체크
  • standard output으로 모든 호출과 그것의 파라미터들을 로그화
  • 프로파일링과 replaying을 위한 Vulkan calls을 추적
이것은 diagnostics validation layer에서 한 함수의 구현이 어떻게 생겼는지에 대한 예시이다.


VkResult vkCreateInstance(
    const VkInstanceCreateInfo* pCreateInfo,
    const VkAllocationCallbacks* pAllocator,
    VkInstance* instance) {

    if (pCreateInfo == nullptr || instance == nullptr) {
        log("Null pointer passed to required parameter!");
        return VK_ERROR_INITIALIZATION_FAILED;
    }

    return real_vkCreateInstance(pCreateInfo, pAllocator, instance);
}

- 이러한 validation layers는 너가 관심있어 하는 모든 디버깅 기능들을 포함하도록 자유롭게 쌓아질 수 있다. 너는 간단히 디버그 빌드를 위해 validation layers를 활성화할 수 있고, release builds를 위해 그것들을 완전히 비활성화 할 수 있다. 그리고 이것은 너에게 두 세계에 대해 최상의 것을 준다.

- 벌칸에서 이전에 두 개의 다른 validation layers가 있었다 : instance and device specific. 그 아이디어는 instance layers는 오직 instances같은 global Vulkan objects와 관련된 호출만을 체크하고, device specific layers는 특정한 GPU와 관련된 호출들만을 체크하는 것이다. Device specific layers는 이제 구식이 되었고, instance validation layers가 모든 Vulkan 호출에 대해 적용된다는 것을 의미한다. 명세 문서는 여전히 너가 호환성을 위해 또한 device level에서 validation layers를 활성화 할 것을 추천한다. 그리고 이것은 어떤 구현에 의해 요구된다. 우리는 간단히 logical device level에서의 instance로서 같은 layers를 명시할 것이다. 이것은 나중에 우리가 볼 것이다.

Using validation layers
- explicitly하게 모든 유용한 layers를 명시하는 대신에, 그 SDK는 너가 implicitly하게 유용한 diagnostics layers의 전체를 활성화하는 VK_LAYER_LUNARG_standard_validation layer를 요청하도록 한다.


============================================
Swapchain

~
다음으로, 우리는 여러 개의 queue families에 걸쳐 사용될 swap chain images를 어떻게 처리해야할지를 명시할 필요가 있따. 만약 graphics queue family가 presentation queue와 다르다면 우리의 어플리케이션이 그러한 케이스가 될 것이다. 우리는 graphics queue로부터 swap chain에 있는 이미지에 그릴 것이고, 그러고나서 그것들을 presentation queue로 제출할 것이다. 여러 개의 queues로부터 접근ㄷ뇌는 이미지들을 처리하는 데에 두 가지 방법들이 있다:

  • VK_SHARING_MODE_EXCLUSIVE : 한 이미지는 한 time에 한 queue family에 의해 소유되고, 소유권은 다른 queue family에서 그것을 사용하기 전에 explicitly하게 전달되어야 한다. 이 옵션은 최상의 성능을 제공한다.
  • VK_SHARING_MODE_CONCURRENT : 이미지들은 explicit ownership transfers 없이 여러 개의 queue families에 걸쳐서 사용되어 질 수 있다.

만약 queue families가 다르다면, 그러면 우리는 이 튜토리얼에서 concurrent mode를 사용할 것인데, ownership chapters를 해야만 하는 것을 피하기 위해서이다. 왜냐하면 이러한 것들은 나중에 더 잘 설명되어지는 몇 가지 개념들을 포함하기 때문이다. Concurrent mode는 어떤 queue families가 ownership을 공유할 것인지를 미리 명시하는것을 요구하는데, queueFamilyIndexCount와 pQueueFamilyIndices 파라미터를 사용한다. 만약 graphics queue family와 presentation queue family가 같다면, 대부분의 하드웨어가 이 겨우일 것인데, 그러면 우리는 exclusive mode를 고수해야만한다. 왜냐하면 concurrent mode는 너가 적어도 두 개의 다른 queue families를 명시하기를 요구하기 때문이다.

우리는 어떤 transform이 swap chain의 이미지들에 적용되어야 하는지를 명시할 수 있는데, 그것이 지원된다면 가능하다 (capabilities에 있는 supportedTransforms). 이것들은 90 degree clockwise rotation 또는 horizontal flip같은 것을 말한다. 어떠한 transformation을 원하지 않는다는 것을 명시하기위해, 간단히 current transformation을 명시해라.

compositeAlpha field는 alpha channel이 window system에 있는 다른 windows와 함께 blending을 위해 사용되어야 하는지를 명시한다. 너는 거의 항상 alpha channel을 무시하기를 원할 것이고,그러므로 VK_COMPOSITE_ALPHA_OPQUE_BIT_KHR 를 써라.

마지막 한 field oldSwapChain이 남았다. Vulkan으로, 어플리케이션이 작동하는 동안 너의 swap chain이 무효해지고, 최적화되지 않는것이 가능한데, 예를들어서 그 window가 resized 되었기 때문이다. 그 경우에, 그 swap chain은 실제로 처음부터 재생성 될 필요가 있따. 그리고 오래된 것에 대한 참조가 이 필드에 명시되어야 한다. 이것은 나중의 챕터에서 우리가 배울 복잡한토픽이다. 지금은, 우리는 오직 하나의 swap chain만을 만들 것이라고 가정할 것이다.

=========================================
Image views
swap chain에 있는 것들을 포함해서, 어떤 VkImage든 사용하기 위해, 우리는 render pipeline에 VkImageView 오브젝트를 생성해야만 한다. 한 image view는 거의 글자 그대로 한 이미지에 대한 view이다. 그것은 그 이미지에 어떻게 접근하는지와, 그 이미지의 어떤 부분을 접근하는지를 설명한다. 예를들어, 만약 그것이 어떠한 mipmapping levels가 없는 2D texture depth texture로서 다뤄져야 한다면.

이 챕터에서, 우리는 swap chain에 있는 모든 이미지에 대해 basic image view를 만드는 createImageViews를 작성할 것인데, 우리는 그것들을 나중에 color targets으로서 사용할 수 있게 하기 위해서이다.

=========================================
Graphics Pipeline Basics - Introduction
다음 몇 챕터 동안, 우리는 우리의 첫 번째 삼각형을 그리기 위해 설정되는 그래픽스 파이프라인을 설정할 것이다. 그래픽스 파이프라인은 너의 meshes의 정점들과 텍스쳐들을 렌더 타겟에 있는 픽셀들로 가져오는 연산의 순서이다.

Input Assembler는 너가 명시한 버퍼로부터 raw vertex data를 모으고, 또한 vertex data 그 자체를 복사할 필요 없이 어떤 elements를 반복하기 위해 index buffer를 또한 사용할지도 모른다.

Vertex Shader는 모든 정점에 대해 작동하고, 일반적으로 model space에서 screen space로 정점 위치를 변환을 적용한다.그것은 또한 vertex마다의 data를 pipeline으로 보낸다.

tessellation shaders는 mesh quality를 증가시키기 위해 너가 어떤 규칙을 기반으로 geometry를 subdivide하게 해준다. 이것은 종종 brick walls같은 표면을 만들기 위해 사용되고, 계단들이 가까이 있을 때 덜 평평해 보인다.

geometry shader는 모든 primitive (triangle, line, point에 대해 작동하고, 그것을 버릴 수 있고, 들어오는 것보다 더 많은 primitives를 만들어 낼수 있다. 이것은 tessellation shader와 비슷하지만, 좀 더 유연성을가진다. 그러나, 오늘날의 어플리케이션에서 많이 사용되지 않는다. 왜냐하면 Intel의 내장 GPU를 제외하고 그 성능이 대부분의 그래픽 카드에서 좋지 않기 때문이다.

rasterization stage는 그 primitives를 fragments로 discretize한다. 이것들은 프레임버퍼에서 그것들이 채우는 pixel elements 이다. 스크린 바깥에 있는 어떤 fragments는 버려지고, vertex shader에 만들어지는 어떤 attributes는 fragments에 걸쳐서 interpolated된다.  보통 다른 primitive fragments 뒤에 있는 fragments는 또한 버려지는데, depth testing 때문이다.

fragment shader는 남아있는 모든 fragment에 대해 호출되고, 그리고 fragments가 어떤 framebuffer(s)에 쓰여졌는지를 결정하고, 어떤 color와 depth values가 쓰여졌는지를 결정한다. vertex shader로 부터의 interpolated data를 사용하여 이것을 할 수 있는데, 그 데이터는 텍스쳐좌표와 라이팅을 위한 normals같은 것들을 포함할 수 있다.

color blending stage는 framebuffer에 있는 같은 픽셀에 매핑되는 다른 fragments를 mix하는 연산을 적용한다. Fragments는 간단히 서로를 overwrite할 수 있고, 더해지거나 또는 transparency를 기반으로 섞여질 수 있다.

green color가 있는 stages들은 fixed-function stages라고 알려져있다. 이러한 단계들은 너가 파라미터를 사용하여 그것들의 연산을 조절할 수 있게 하지만, 그것들이 작동하는 방법은 이미 정의 되어 있다.

한편 orange color로 되어 있는 단계들은 programmable하다. 이것은 너가 너 자신의 코드를 너가 원하는 연산을 정확히 적용하기 위해 그래픽스 카드에 올릴 수 있다는 것을 의미한다. 이것은 예를들어 texturing과 lighting부터 ray tracers 까지의 것을 구현하도록, fragment shaders를사용하게 한다. 이러한 프로그램들은 정점과 fragments같은 많은 오브젝트들을 병렬로 처리하기 위해 많은 GPU cores에서 동시에 작동한다.

만약 너가 OpenGL과 Direct3D같은 더 오래된 API들을 전에 사용했다면, glBlendFunc와 OMSetBlendState 같은 호출들로 어떤 파이프라인 세팅을 변경할 수 있는 것에 이숙할 것이다. 벌칸에서의 그래픽스 파이프라인은 거의 완전히 변경될 수 없다. 그래서 만약 너가 쉐이더를 변경하고, 다른 프레임버퍼를 bind하고 또는 blend function을 바꾸기를 원한다면, 너는 파이프라인을 처음부터 다시 만들어야만 한다. 그 불이점은 너는 너의 렌더링 연산에서 너가 사용하길 원하는 다른 states의 조합 모두를 나타내는 많은 pipelines을 만ㄷ르어야만 할 것이다. 그러나, 너가 그 파이프라인에서 할 모든 연산들이 미리 알려져 있기 때문에, 그 드라이버는 그것에 대해 더 잘 최적화 할 수 있다.

프로그래밍 가능한 몇 단계들은 너가 의도하는 것에 따라 optional하다. 예를들어, tessellation과 geometry 단계들은 만약 너가 단순한 geometry를 그리려한다면 비활성화 될 수 있다. 만약너가 depth values에만 관심이 있다면, 너는 그 fragment shader stage를 비활성화 할 수 있다. 이것은 shadow map generation에 유용하다.

다음 챕터에서, 삼각형을 스크린에 넣는데 요구되는 두 개의 프로그래밍 간으한 단계를 만들 것이다: vertex shader and fragmenet shader. blending mode, viewport, rasterization같은 fixed function 설정은 그 이후의 챕터에서 설정될 것이다. Vulkan에서 그래픽스 파이프라인을 설정하는 최종 부분은 input과 output framebuffers의 명세를 포함한다.

=========================================
Graphics pipeline basics - Shader modules

이전 API들과 다르게, 벌칸의 쉐이더 코드는 GLSL와 HLSL같은 사람이 읽을 수 있는 문장과 반대로 bytecoe format으로 명시되어야만 한다. 이 bytecode format은 SPIR-V라고 불려지고, Vulkan과 OpenCL(둘 다 Khronos APIs이다)과 함께 사용되도록 설계되었다. 그것은 그래픽스 쉐이더와 컴퓨트 쉐이더를 쓰기 위해 사용될 수 있는 포맷이지만, 우리는 이 튜토리얼에서 Vulkan의 graphics pipelines에 사용되는 쉐이더에 집중할 것이다.

bytecode format을 사용하는 이점은, 쉐이더 코드를 native code로 변경하는 GPU vendors에 의해 쓰여진 컴파일러가 상당히 덜 복잡하다는 것이다. 과거에, GLSL 같은 사람이 읽을 수 있는 문장으로, 몇 가지 GPU 회사들은 표준에 대한 자신들만의 해석으로 유S연성을 가지고 있었다는 것을 보여주었다. 만약 너가 이러한 회사들중 하나의 GPU로 중요한 쉐이더를 잘성하려고 한다면, 그러면 너는 synta errors 때문에 너의 코드를 거부하는 다른 회사의 드라이버의 위험을 감수할 것이다. 또는 더 안좋게, 컴파일러 버그 때문에 너의 쉐이더가 다르게 작동할 것이다. SPIR-V 같은 간단한 bytecode format으로, 그것이 바라건대, 피해질 것이다.

그러나, 그것은 우리가 이 바이트 코드를 손으로 써야 할 필요가 있다는 것을의미하지 않는다. Khronos는 GLSL에서 SPIR-V로 컴파일하는 그들 자신의 vendor-independent compiler를 출시했다.  이 컴파일러는 너의 쉐이더 코드가 표준을 완전히 준수하고, 너가 너의 프로그램과 함께 보낼 수 있는 SPIR-V binary를 만들도록 설계되어 있다. 너는 또한 이 컴파일러를 라이브러리 로서, runtime에 SPIR-V를 만들어내도록 포함시킬 수 있지만, 우리는 이 튜토리얼에서 하지 않을 것이다. 그 컴파일러는 이미 LunarG SDK에 glslanValidator.exe로 포함되어 있다. 그래서 너는 추가적을 어떤 것을 다운로드 할 필요가 없다.


Vertex shader
~~ 만약 너가 이전에 OpenGL을 사용했더라면, 그러면 너는 Y 좌표의 기호가 (NDC Space) 이제 flipped 된 것을 눈치챌 것이다. 그 Z 좌표는 이제 Direct3D에서 그렇듯이 같은 범위인 0 ~ 1을 사용한다.



















2019년 4월 17일 수요일

Vulkan Tutorial (1)

https://vulkan-tutorial.com/Introduction
https://vulkan-tutorial.com/Overview

공부하면서 기록할 것들을 적어보자.. 완전 번역하기엔 시간이 없으니, 빠르게 요점만 정리하자. Introduction 부분은 그냥 쉽게 그냥 읽으면 된다.

Overview

Origin of Vulkan
- 벌칸은 GPU에 대한 크로스 플랫폼 추상화로서 설계되었다.
- 벌칸은 프로그래머가 좀 더 장황한 API를 사용하여 그것들의 의도를 명확하게 명시하도록 하여 드라이버 오버헤드를 줄이고, 여러 쓰레드들이 병렬로 명령어를 생성하고 제출하도록 한다.
- 벌칸은 단일 컴파일러로 표준화된 바이트 코드 포맷으로 바꾸어서 쉐이더 컴파일에서 비일관성을 줄인다.
- 마지막으로, 벌칸은 그래픽스 와 연산 기능을 단일의 API로 통합하여 현대 그래픽 카드의 일반 목적 처리 능력을 수용한다.

What it takes to draw a triangle
Step1 - Instance and Physical Device Selection
- 벌칸 프로그램은 VkInstance를 통해 Vulkan API를 설정하여 시작한다.
- 한 instance는 너의 프로그램과, 너가 사용하려는 API Extensions을 설명하여 생성된다.
- 인스턴스를 생성한 후에, 너는 벌칸을 지원하는 하드웨어를 쿼리할 수 있고, 연산을 위해 사용될 한 개 이상의 VkPhysicalDevice를 선택할 수 있다.
- 너는 바람직한 장치를 선택하기 위해 VRAM size와 device capabilities같은 특성들을 쿼리할 수 있는데, 예를들어, 전용 그래픽스 카드를 사용하는 것을 선호하기 위해서이다.

Step2 - Logical device and queue families
- 사용할 올바른 하드웨어 장치를 선택한 후에, 너는 VkDevice(logical device)를 생성할 필요가 있다.
- VkDevice에서, 너는 너가 어떤 VkPhysicalDeviceFeatures를 사용할지를 좀 더 구체적으로 설명하게 된다. 그러한 특징들로는 multi viewport rendering과 64 bit floats 같은 것들이 있다.
- 또한 너는 어떤 queue families를 사용하고 싶은지를 명시할 필요가 있다.
- 왜냐하면, draw commands와 memory operations 같은 벌칸으로 수행되는 대부분의 연산들은 그것들을 VkQueue에 제출하여 비동기적으로 실행된다.
- Queues는 queue families로부터 할당되는데, 거기에서 각 queue family는 그것의 queues 안에서 특정한 연산들의 집합을 지원한다.
- 예를들어, 그래픽스, compute and memory transfer operations에 대해 별개의 queue families가 있을 수 있다.
- queue families의 이용가능성은 또한 physical device 선택시에 구분되는 요소로 사용될 수 있다.
- 벌칸 지원을 하는 한 장치가 어떠한 그래픽스 기능을 제공하지 않는 것이 가능하지만, 그러나 벌칸 지원을 가진 모든 그래픽 카드는 오늘날 일반적으로 우리가 관심있어 하는 모든 queue operations를 지원한다.

Step3 - Window surface and swap chain
-우리는 한 window에 실제로 렌더링할 두 개 이상의 컴포넌트들이 필요하다 : window surface(VkSurfaceKHR) 과 swap chain (VkSwapchainKHR). KHR 후위 표기에 주목해라. 그것은 이러한 오브젝트들이 Vulkan extension의 일부라는 것을 의미한다.
- 벌칸 API는 완전히 platform-agnostic인데, 우리가 표준화된 WSI(Window System Interface) extension을 window manager와 상호작용하기 우해 사용할 필요가 있는 이유이다. 그 surface는 렌더링할 windows에 대해 크로스 플랫폼 추상화이고, 일반적으로 native window handle에 대한 reference를 제공하여 instantiated 된다. 윈도우즈에서 예를들어 HWND이다. 운 좋게도, GLFW 라이브러리는 이 것에 대해 플랫폼 특정한 세부사항을 다루는 내장 함수를 가진다.
- swap chain은 렌더 타겟의 집합이다. 그것의 기본 목적은 우리가 현재 렌더링하고 있는 이미지를 혀냊 스크린에 있는 것과 다르게 하도록 하는 것이다.완전한 이미지들만 보이게 하는 것이 중요하다.
- 완성된 이미지를 스크린에 보이기 위해 렌더 타겟과 조건에 대한 개수는 present mode에 달려있다. 보통의 present modes는 double buffering (vsync)와 triple buffering이다.
- 어떤 플랫폼은 VH_KHR_display와 VK_KHR_display_swapchain을 통해 어떤 윈도우 매니저들과도 상호작용 하는 것 없이 디스플레이에 직접 렌더링하게 해준다.

Step4 - Image views and framebuffers
- swap chain으로부터 얻어진 이미지를 그리기 위해, 우리는 그것을 VkImageView와 VkFramebuffer에 넣어야 한다. 한 image view는 사용될 한 이미지의 특정한 부분을 참조하고, 한 프레임버퍼는 color, depth and stencil targets을 위해 사용될 image views를 참조한다. swap chain에 많은 다른 이미지들이 있을 수 있기 때문에, 우리는 우선적으로 한 image view와 그것들 각각에 대해 프레임버퍼를 만들 것이고, draw time에 올바른 것을 선택할 것이다.

Step5 - Render passes
벌칸에서의 Render passes는 렌더링 연산동안에 상요되는 이미지들의 유형들을 설명하고, 그것들이 어떻게 사용되는지와, 그것들의 내용이 어떻게 다뤄져야 하는지를 설명한다. 우리의 초기 삼각형 렌더링 어플리케이션에서, 우리는 벌카네엑 우리가 단일의 이미지를 color target으로 상요할 것이라고 말할 것이고, 우리가 그것이 drawing operation 직전에, solid color로 cleared되기를 원한다고 말할 것이다. 반면에 한 render pass는 오직 이미지들의 유형을 설명하지만, VkFramebuffer는 실제로 특정한 이미지를 이러한 slots에 바인드한다.

Step6 - Graphics pipeline
벌칸에서의 그래픽스 파이프라인은 VkPipeline object를 생성하여 설정된다. 그것은 그래픽카드의 설정가능한 상태를 설명하는데, viewport size와 depth buffer operation, VkShaderModule objects를 사용하여 programmable state 같은 상태들을 말한다. VkShaderModule objects는 shader byte code로부터 생성된다. 그 드라이버는 또한 어떤 렌더타겟들이 파이프라인에서 사용될지를 알 필요가 있다. 그래서 우리는 그 렌더패스를 참조하여 명시한다.

현존하는 API들과 비교해서 벌칸의 가장 구분되는 특징들 중 하나는 그래픽스 파이프라인의 거의 모든 환경설정이 미리 설정되어야 할 필요가 있다. 그것은 만약 너가 다른 쉐이더로 바꾸길 원하거나 약간 너의 vertex layout을 바꾸고 싶다면, 너는 전적으로 그래픽스 파이프라인을 재생성해야할 필요가 있다는 것을 의미한다.  그것은 너의 렌더링 연산을 위해 필요한 모드 ㄴ다른 조합을 위해 미리 많은 VkPipeline objects를 만들어야 한다는 것을 의미한다. viewport size와 clear color같은 오직 몇 가지 기본 설정만이 동적으로 바뀔 수 있다. 또한 모든 상태는 explicitly하게 설명되어질 필요가 있고, 예를들어, 어떠한 default color blend state가 없다.

좋은 소식은 너가 just in-time compilation에 대비하여 ahead-of-time compilation과 같은 것을 하기 떄문에, 드라이버에 대해 좀 더 최적화 기회가 있고, 런타임 성능은 좀 더 예측가능하다. 왜냐하면 다른 그래픽스 파이프라인으로 바꾼는 것 같은 많은 state changes가 매우 explicit하게 만들어지기 때문이다.

Step7 - Command pools and command buffers
이전에 언급되었듯이, drawing operations 같이 벌칸에서 우리가 실행하길 원하는 많은 연산들은 한 queue에 제출되어질 필요가 있다. 이러한 연산들은 그것들이 제출될 수 있기전에, VkCommandBuffer에 처음에 기록될 필요가 있다. 이러한 command buffers는 특정한 queue family와 연관된 VkCommonPool로 부터 할당된다. 간단한 삼각형을 그리기 위해, 우리는 다음의 연산들로 한 command buffer를 기록할 필요가 있다:

  • Begin render pass 
  • 그래픽스 파이프라인 바인드
  • 3개의 정점 그리기
  • End render pass
프레임버퍼에서의 이미지는  swap chain이 우리에게 어떤 특정한 이미지를 줄 지에 달려있기 때문에, 우리는 각 가능한 이미지에 대해 command buffer를 기록할 필요가 있고, draw time에 올바른 것을 선택할 필요가 있다. 대안은 매 프레임마다 그 command buffer를 다시 기록 하는 것인데, 효율적이지 않다.

Step8 - Main loop
drawing commands가 한 command buffer에 들어갔으니, 그 main loop는 꽤 간단하다. 우리는 처음에 swap chain으로부터 vkAcquireNextImageKHR로 한 이미지를 얻는다. 우리는 그러고나서 그 이미지에 대해 적절한 command buffer를 선택할 수 있고, vkQueueSubmit 으로 그것을 실행한다. 마지막으로, 우리는 스크린에 보이기위해 swap chain에 그 이미지를 vkQueuePresentKHR로 반환한다.

queues에 제출되는 연산들은 비동기적으로 실행된다. 그러므로, 우리는 정확한 실행순서를 보장하기 위해 세마포어 같은 동기화 오브젝트들을 사용해야만 한다.  draw command buffer의 실행은 image 획득이 마무리 되는 것을 기다리도록 설정되어야 한다. 만약 그렇지 않다면, 우리가 스크린에 보이기위해 여전히 읽혀지고 있는 이미지를 렌더링하기 시작하는 것이 발생할지도 모른다. vkQueuePresentKHR 호출은 차례대로 렌더링이 끝나기를 기다릴 필요가 있다. 왜냐하면 우리가 렌더링이 완료된 후에 signaled 되는 두 번째 세마포어를 사용할 것이기 때문이다.

Summary
- 요악해서, 첫 번째 삼각형을 그리기 위해서, 우리는 다음을 할 필요가 있다:
  • Create a VkInstance
  • 지원되는 그래픽 카드 선택 (VkPhysicalDevice)
  • drawing과 presentation을 위해 VkDevice와 VkQueue 생성
  • window, window surface, swap chain 생성
  • swap chain 이미지들을 VkImageView에 넣기
  • 렌더 타겟과 사용법을 명시하는 render pass 생성
  • 그래픽스 파이프라인 설정
  • 커맨드 버퍼를 할당하고, 모든 가능한 swap chain image에 대해 draw commands로 기록
  • 이미지를 획득하여 프레임들을 그리기, 그리고 올바른 draw command buffer를 제출하고, 그 이미지를 다시 swap chain에 반환하기.


Coding Conventions
- 한 오브젝트를 생성하거나 파괴하는 함수들은 드라이버 메모리에 대해 custom allocator를 사용하게 해주는 VkAllocationCallbacks 파라미터를 가질 것이고, 이것은 또한 이 튜토리얼에서 nullptr로 남아질 것이다.
- 거의 모든 함수들은 VK_SUCCESS or error code 둘 중 하나를 반환하는 VkResult를 반환한다. 명세 문서가 각 함수가 어떤 에러코드를 반환하고 그것들이 무엇을 의미하는지를 설명한다.

Validation layers
- 이전에 언급했듯이, 벌칸은 고성능과 낮은 드라이버 오버헤드를 위해 설계되었다. 그러므로, 그것은 기본적으로 매우 제한된 에러 체크와 디버깅 능력을 퐇마할 것이다. 그 드라이버는 만약 너가 잘못한다면 종종 에러코드를 반환하는 대신에 충돌이 날 것이다. 그것은 너의 그래픽카드에서는 작동하는 것처럼 보일 것이지만 다른 것에서 완전히 실패할 것이다.

벌칸은 너가 validation layers라고 알려진 한 기능을 통해서 광범위한 체크를 할 수 있게 해준다. Validation layers는 API와 그래픽스 드라이버 사이에 삽입될 수 있는 코드 조각들인데, 이것은 함수 파라미터에 대해 추가 체크를 하고, 메모리 관리 문제를 추적하는 것 같은 것을 하기 위해서이다. 좋은 것은, 너가 개발하는 동안 그것들을 활성화할 수 있고, 그러므로 너의 어플리케이션을 zero overhead를 위해 출시할 때 완전히 그것들을 비활성화할 수 있다. 누구든지 자신만의 validation layers를 쓸 수 있지만, LunarG가 만든 Vulkan SDK는 우리가 이 튜토리얼에서 사용할 validation layers의 표준 세트를 제공한다. 너는 또한 그 layers로 부터 debug messages를 받는 callback function을 등록할 필요가 있다.

벌칸은 모든 연산에 대해 매우 explicit하고, validation layers가 매우 광범위하기 때문에, 너의 스크린이 OpenGL과 Direct3D에 비해 왜 검정색이 되었는지를 알아내는 것이 훨씬 더 쉽다.










2019년 4월 1일 월요일

unity scene manager 사용하는 방법

http://myriadgamesstudio.com/how-to-use-the-unity-scenemanager/

여기자료 번역

========================================
유니티 SceneManager 사용방법
이 블로그 포스트에서, 나는 모든 강력한 single scene에 대한 대안의 방법을 보여줄 것이다. synchronous and asynchronous scene loading, transition(변환) effects, additive(부가) scenes같은 기법들은 Unity의 SceneManager로 모두 가능하다. 놀랍게도, 이 기능의 어떠한 것을 구현하는데 있어서 복잡한 Scene 관리 스크립트가 요구되지 않는다. Additive scenes는 또한 매우 adaptive(조정할 수 있는)한데, 여러가지 게임 scenes, configuration scenes, debugging scenes, 그리고 많은 다른 유용한 대안들을 불러오는데 사용될 수 있다.

나는 원래 플레이어가 게임 월드를 탐험하고 있을 때 실시간으로 scenes을 로딩하는 것을 얼마나 잘 처리할지를 보기위해 Unity에서의 scene management를 조사했었다. 몇 가지 실험후에, 우리는 이제 모든 우리의 게임 잼에서 SceneLoader script를 사용할 뿐만 아니라, 우리의 main title에서도 사용한다. 그리고 윌는 그 결과에 매우 만족한다. 나는 이 글이 너에게 scene manager에 대한 유용한 입문을 주고, 그것의 가장 기본적인 기능의 사용방법을 주기를 바란다.

나는 모든 예제에 대한 깃헙 저장소를 만들었다. 그래서 편하게 끝으로 가서, 그 데모 프로젝트를 다운받아라. 여기에 내가 만든 (Alisha가 아트 작업을 한) 프로토타입의 좋지 않은 퀄리티의 GIF가 있다. 이것은 나의 첫 번째 scene loading 구현에 대해 작업한 것이다.

The Why
여기 Myriad Games Studio에서, 우리는 많은 게임 잼들을 한다. 아마도, 2달마다 잼을 한다. Myriad에서 하는 내부적인 것 또는 Tasjam events 같은 구성된 이벤트 둘 중 하나이다. 내부 잼은 일반적으로 우리가 좀 더 배우길 원하는 어떤 기능을 구현하는 게임을 만드는 것에 집중한다. TasJam 이벤트는 우리가 우리의 design muscles을 뽐내고, 특정한 제약 내에서 흥미로운 게임을 만들려고 하는 곳이다.

게임잼들은 일반적으로 우리가 어떤 분야에서 좀 더 지식을 얻어야 할 필요가 있는지 보는 것을 도와준다. 나에게 있어서 가장 주요한 분야 중의 하나는 game의 scenes을 어떻게 설정하는지와 그러한 scenes들을 부드럽게 load하는 것이다. 팀과 토론한 후에, 나는 scene loading solution을 연구하고 불러올 몇 주의 시간을 확보했다. 그 최종 scripts는 간단하고 우리가 만드는 어떤 게임에서 든지 추가하기에 간단하다. 모든 게임 잼은 이제 엄청난 절약된 시간을 갖는데, 우리가 유연한 scene setup과 pre-made(미리 만들어진) scene loading solution을 가지기 때문이다. 이것은 우리가 설계 단계에서 좀 더 많은 시간을 쓸 수 있다는 것을 의미한다 - 게임 개발의 가장 어려운 부분!

SceneManager를 사용하는 것의 다른 좀 더 구체적인 이유들 중의 몇몇과 너의 게임을 여러 개의 scenes으로 나누는 것의 장점은 :

  • loading times과 memory 사용이 줄어든다.
  • 팀 멤버들은 별개의 scenes에 작업할 수 있고, 그러므로 source control 갈등의 개수를 줄인다.
  • 실제로 거대한 open-world games이 가능하다.
  • scene 변환과 screens을 불러오는 것이 만들어서 추가하는 것이 매우 간단하다.
  • 쉽게 "manager" type scripts와 objects를 너의 게임에 추가할 수 있다.
The Process
여기에서, 나는 menu scene, loading scene, game scene을 가지고 Unity의 SceneManager class의 간단한 어플리케이션을 설명할 것이다. 나중에, 나는 asynchronous scene transitions을 포함하는 좀 더 고급 버전을 다룰 것이다.

scene manager를 사용하여 작업하는 프로토타입을 얻기 위해서, 우리는 다음을 할 필요가 있다:
  1. scenes을 설정하고, 그것들에 build settings을 추가한다.
  2. project에 Singleton script를 추가하고, "SceneLoader.cs"라고 불려지는 새로운 script를 생성한다.
  3. 새로운 scenes을 불러올 필요가 있는 scripts를 SceneLoader를 사용하여 추가해라. GUI buttons, in-game scenes, 또는 어디든지에서 처리될 수 있다.
이것은 어떤 순서에서 든지 한 scene or scenes들을 불러오기 위해 SceneLoader class에 대해 간단하고 쉬운 호출로 불러와질 수 있는 scenes들을 만들어낼 것이다.

Scene Setup
scenes의 setup은 매우 간단하다. 우리는 다음에 대한 유사한 포맷을 가진 3개의 scenes이 필요하다;

- Camera, UI button(s)을 포함하는 Menu Scene
- Camera, UI (background) image, Animated sprites/loading bar를 포함하는 Loading        Scene
- Camera, Playable character, UI button (Menu scene으로 돌아가기 위한)를 포함하는 Game Scene

Note : 너는 너의 scenes들을 scene hierarchy에 drag and drop할 수 있고, 한 번에 여러개의 scenes을 보일 수 있다! 이것이 내가 여기에서 한 것이다 - 테스팅을 위해 매우 유용하다.

SceneManager Namespace
Unity의 오래된 버전에서, 어떤 scene management는 Application class를 사용해서 처리됐는데, 이것은 generic static methods의 뒤범벅된 것을 가지고 있다. 고맙게도, Unity는 Scene과 관련된 methods들을 그것들 자신의 namespace로 강화시켰다 - UnityEngine.SceneManagement. 이 namespace는 scenes을 loading하고 unloading하는데 대부분의 유용한 methods들을 포함한다.

Note : Application class에 있는 모든 scene과 관련된 메소드들은 이제 구식이다.

SceneManager는 scenes을 동기적으로/비동기적으로 불러오는 메소드들을 포함한다. 그래서 만약 우리가 추가적으로 노력을 한다면, 우리는 게임이 멈추게 하지 않고 background에서 scenes loading을 할 수 있다. 그래서 코드로 들어가서, SceneLoader라고 불려지는 우리 자신의 클래스를 작성해보자. 우리는 synchronous versions을 작성하여 시작할 것인데, 그것들이 어떻게 작성하고, async versions을 구현하는 것으로 부터 얼마나 많은 이익들을 얻을 수 있는지를 보기 위해서이다.

나는 singleton으로 나의 SceneLoader class를 작성했고, 이것은 MonoBehaviour로부터 상속받는다. 이것은 많은 이유들이 때문에 하게 되었는다; 첫 째로, singletons은 그들 스스로 manager-type classes들에 빌려준다(?), 왜냐하면 너는 일반적으로 한 개의 인스턴스만을 원하기 때문이다. 둘 째로, 내가 사용하는 singleton implementation은 게임 오브젝트가 scene이 unloaded될 때 결코 파괴되지 않는 것을 보장한다. 마지막으로, MonoBehaviour 상속은 그 singleton이 Awake, Start, and Update같은 상속된 메소드들에 대한 접근을 허용하게 한다. 이것은 또한 너가 다른 게임 오브젝트들과 스크립트들을 매니저에 붙이도록 한다. 만약 필요하다면.

그 singleton implementation은 깃헙 저장소에 포함되어있지만, 여기에서 볼 수 있다. 그것은 Unity community에 의해 쓰인 클래스인다 (어떤 다른 좋은 코드를 거기에서 또 참고해라).


using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoader : Singleton<SceneLoader>
{
 // guarantee this will be always a singleton only - can't use the constructor!
 protected SceneLoader() { }

 public void LoadeScene()
 {
  SceneManager.LoadScene("Loading");

  StartCoroutine(LoadAfterTimer());
 }

 private IEnumerator LoadAfterTimer()
 {
  // the reason we use a coroutine is simply to avoid a quick "flash" of the
  // loading screen by introducing an artificial minimum load time
  yield return new WaitForSeconds(2.0f);

  LoadScene("Game");
 }

 private void LoadScene(string sceneName)
 {
  SceneManager.LoadScene(sceneName);
 }
}

이것은 매우 기본적인 구현이지만, 그것은 우리가 원하는 것을 성취하고, 우리는 이제 구성할 기반을 가지게된다. 또한, 우리는 이러한 manager methods들을 singleton Instance를 참조하여 한 script로부터 접근할 수 있다, 이렇게 : SceneLoader.Instance.DisplayLoadingScene();

Note : 너가 모든 scenes들을 build settings에 추가하도록 해라.

LoadScene (line 10)을 호출하는 것은 Loading scene을 load하고, 그러고나서 Game scene을 불러올 것이다. 코루틴이 이 경우에 사용되는 이유는, 간단한 추가될 휴식시간을 허용하게 하기 위해서이다. 이것이 없다면, 그 Game Scene은 불러와질 것이고, 그 플레이어가 적절히 Loading scene을 볼 기회를 갖기전에 보여진다. Unity는 작은 scenes들을 매우 빠르게 불러온다.

너는 아마도 이 코드에 또 다른 문제를 발견할 수 잇다. 우리는 우리의 scenes들의 loading을 추적할 방법이 없다 (적어도, 어떤 tracker/subscriber system의 종류를 만드는 문제들을 하지 않느 이상). 그 게임 scene이 실제로 완료되었는지 알 방법이 없다. 만약 그것이 준비되지 않았다면, 그러면 그것을 불러오는 것은 우리의 loading screen을 얼린것 처럼 보일 것이다. Synchronous methods는 그 main thread를 lock할 것이다 - 즉, 모든 것이 scene이 loading 되는 걸 멈출 때 까지 반응하는 것을 멈출 것이다. 만약 너가 animated loading scene을 가지고 있따면, 이것은 큰 문제이다 (그러나 static image를 보여주는 것은 괜찮을 것이다).

fix? 우리는 물론 asynchronous methods로 넘어간다. 이것은 우리의 코드를 좀 더 복잡하게 만들 것이지만, 그것은 확실히 장기간으로 보상을 해준다.



using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoaderAsync : Singleton<SceneLoader>
{
 // Loading Progreess : private setter, public getter
 private float _loadingProgress;
 public float LoadingProgress { get {return _loadingProgress;} }

 public void LoadScene()
 {
  // kick-off the one co-routine to rule them all
  StartCoroutine(LoadScenesInOrder());
 }

 private IEnumerator LoadScenesInOrder()
 {
  // LoadSceneAsync() returns an AsyncOperation,
  // so will only continue past this point when the Operation has finished
  yield return SceneManager.LoadSceneAsync("Loading");

  // as soon as we've finished loading the loading screen, start loading the game scene
  yield return StartCoroutine(LoadScene("Game");
 }

 private IEnumerator LoadScene(string sceneName)
 {
  var asyncScene = SceneManager.LoadSceneAsync(sceneName);
  
  // this value stops the scene from displaying when it's finished loading
  asyncScene.allowSceneActivation = false;

  while(!asyncScene.isDone)
  {
   // loading bar progress
   _loadingProgress = Mathf.Clamp01(asyncScene.progress / 0.9f) * 100;

   // scene has loaded as much as possible, the last 10% can't be multi-threaded
   if(asyncScene.progress >= 0.9f)
   {
    // we finally show the scene
    asynScene.allowSceneActivation = true;
   }

   yield return null;
  }
 }
}

클래스를 위에서 밑에까지 보아서, 우리는 
























2019년 3월 13일 수요일

GDC 2010 Erin Catto - Computing Distance (GJK Algorithm)

Computing Distance

* Convex polygons
- 우리가 한 쌍의 convex polygons(볼록 다각형)을 가지고 있다고 가정하자.

* Closest points
- 우리는 가장 가까운 점을 어떻게 연산하는가?
- 가장 가까운 점을 아는 것은 우리에게 거리를 준다.

* Overlap
- 우리는 overlap을 어떻게 탐지하는가?
- 이 경우에 그 거리는 0이다.

* Goal
- convex polygons 사이의 distance를 연산하자
- 이 발표의 목표는 convex polygons 사이의 거리를 연산하는 한 알고리즘을 설명하는 것이다.

* Keep in mind
- 2D이다.
- 최적화되지 않은 코드이다.
- 나는 이 발표에서 오직 2D만을 다룰 것이다. 그러나 대부분의 개념들은 3D로 확장된다.
- 너는 최적화되지 않은 알고리즘들과 코드를 볼지도 모른다. 이것은 좋은 신호인데, 너가 그 재료들을 이해한다는 의미이다.

* Approach
- 이 발표에서 bottom-up 접근법을 사용할 것이다.
- 나는 간단한 솔루션을 가진 간단한 문제로 시작할 것이고, 그러고나서 좀 더 복잡한 문제들과 솔루션 쪽으로 이동할 것이다.
- 각 level은 이전의 levels로부터 구성된다.

* Geometry
- 나는 그 알고리즘들을 위해 너의 geometric intuition(기하학적 직관)을 구성하려고 할 것이다.
- 결국, 우리는 한 기하학적 문제를 해결하려고 할 것이다.
- 추가 보너스로, 너는 많은 좋은 파스텔 컬러들을 보게 될 것이다.

* If all else fails ...
- 발표를 보충하기 위해, 나는 소스코드가 있는 알고리즘의 데모를 만들었다.
- 나는 나중에 링크를 올릴 것이다.

* Outline
1. Point to line segment
2. Point to triangle
3. Point to convex polygon
4. Convex polygon to convex polygon
- 이 발표의 나머지의 개요이다.
- 각 주제는 이전의 것으로부터 구성되고, 연산의 대부분은 재활용 될 것이다. 

* Concepts
1. Voronoi regions
2. Barycentric coordinates
3. GJK distance algorithm
4. Minkowski Difference
- 그 과정에서, 나는 몇 가지 중요한 개념들을 소개할 것이다.
- 그 첫 번째 개념은 Voronoi regions인데, 이것은 우리가 그 평면을 가장 가까운 feature regions으로 분할하게 해준다.
- 그 두 번째 개념은 barycentric coordinates이고, 이것은 점들의 가중치 합을 기반으로 오브젝트 기반의 좌표 체계를 제공한다.
- 세 번째 개념은 GJK 알고리즘이고, 점과 convex polygon problem에 대해 반복적으로 sovle하기 위해 사용할 것이다.
- 그 네 번째 개념은 Minkowski difference인데, polygon to polygon의 문제를 point to polygon으로 바꾸게 해준다.

* Section 1 : Point to Line Segment
- point to line segment를 다루는 첫 번째 주제로 시작해보자.

* A line segment
- 우리가 정점 A와 B를 가진 선분을 가지고 있다고 하자.

* Query point
- 이제 우리가 query point Q를 가지고 있다고 하자.

* Closest Point
- 우리는 선분 위에 있는 가장 가까운 점 P를 찾길 원한다.

* Proejction : region A
- 우리는 Q를 A와 B를 지나는 직선에 사영하여 가장 가까운 점을 찾을 수 있다.
- Q가 그 직선에 사영될 수 있는 3 개의 영역이 있다.
- 이 슬라이드에서, Q는 side A의 선분 바깥으로 사영된다.

*  Projection : region AB
- 이 슬라이드에서, Q는 그 선분 안으로 사영된다.

* Projection : region B
- 마지막으로, 이 경우에 Q는 side B에 선분 밖으로 상여된다.
- 그래서 그것은 3 개의 경우들을 보여준다.

* Voronoi regions
- 그래서 이것은 Voronoi regions의 개념을 꺼내게 한다.
- 우리는 그 평면을 closest feature regions으로 나눌 수 있다.
- region A에 있는 모든 점들은 정점 A와 가장 가깝다.
- region B에 있는 모든 점들은 정점 B와 가장 가깝다.
- region AB에 있는 모든 점들은 AB의 내부와 가장 가깝다.
- 이러한 것들이 Voronoi regions이라고 불려진다.
- 가장 가까운 점을 연산하는 것은 Q의 Voronoi region을 결정하는 것의 문제이다.

* Barycentric coordinates
- 우리는 선분 AB를 지나는 직선에 대해 Q의 사영을 연산하기 위해 barycentric coordinates를 사용할 것이다.
- barycentric coordinates가 무엇인가?
- AB를 지나는 직선 위에 있는 어떤 점 G는 A와 B의 가중치 합으로 나타내어질 수 있다.
- 여기에서 우리는 그 가중치를 u와 v로 이름을 붙인다.
- 이러한 가중치는 합해서 1이 되어야 한다.
- 이러한 가중치들은 선분 AB와 관련하여 G의 barycentric coordinates이다.

* Fractional lengths
- 우리는 barycentric coordinates를 partial segments의 fractional lengths로 볼 수 있다.
- 이 슬라이드에서 partial segments는 균형을 이룬다. 그래서 u와 v는 0.5이다.
- 이 슬라이드에서 partial segments는 균형을 이루지 않는다. G가 A에 다가갈 때, u는 더 커지고, v는 균등하게 더 작아진다.

{
(1) u = 0.5, v = 0.5
(2) u = 0.75, v = 0.25
(3) u = 1.25, v = -0.25
A와 B가 일차원에 있는 값이라고 하자.
10과 100으로
그리고 각 케이스에 대해 G(u,v) 에 대한 값은, 즉 G의 위치는
(1) 0.5 * 10 + 100 * 0.5 = 5 + 50 = 55
(2) 0.75 * 10 + 0.25 * 100 = 7.5 + 25 = 32.5
(3) 1.25 * 10 - 0.25 * 100 = 12.5 - 25 = -12.5
}

* Unit Vector
- A에서 B로 향하는 단위 벡터 n을 정의하자.

* (u,v) from G
- 이제 우리는 G의 위치를 기반으로 (u, v)를 결정할 수 있다.
- (u, v)는 적절한 n에 대해 sub-segment를 내적하고, 그러고나서 AB의 전체 길이로 나누어서 결정된다.
- u와 v가 개별적으로 음수가 될 수 있다는 것에 주의해라.
- 또한 u와 v가 합해서 1이 된다는 것에 주의해라.
 
- 우리는 query point Q로부터 직접 (u, v)를 얻을 수 있다.
- 우리가 G 대신에 Q를 사용한다면 그 내적이 변하지 않는다는 것에 주의해라.
- 이것은 내적의 사영 특성에 의한 것이다.

* Voronoi region from (u, v)
- AB를 지나는 어떤 점 G는 A와 B의 가중치로 나타내어질 수 있다.
- 그 가중치는 합해서 1이 되어야 한다.
- 이러한 가중치는 선분 AB와 관련하여 G의 barycentric coordinates이다.

u > 0 && v > 0 : region AB
v <= 0 : region A
u <= 0 : region B

* Closest Point Algorithm
- 이제 우리는 우리의 closest point algorithm을 쓸 수 있다.
- 우리는 line segment AB와 query point Q가 주어진다.
- 처음에 우리는 barycentric coordinates를 계산한다.
- 그것으로 부터, 우리는 Voronoi region을 결정하고, 가장 가까운 점 P를 결정한다.
input : A, B, Q
compute u and v

if (u <= 0)
   P = B
else if( v <= 0)
   P = A
else
   P = u * A + v * B

* Section 2 : Point to Triangle
- 우리는 상세히 point to line segment를 다루었다.
- 이제 우리가 좀 더 어려운 문제로 이동하도록 하자 : point to triangle.

* Triangle
- 여기에 2D의 삼각형이 있다.
- 선분들 처럼, 우리는 주어진 query point에 대해 Voronoi regions을 확인할 수 있다.

* Closest feature : vertex
- 여기에서 closest feature는 vertex B이다.

* Closeset feature : edge
- 여기에서 closest feature는 edge BC이다.

* Closest feature : interior
- 이 경우에, closest feature는 triangle's interior이다.

* Voronoi regions
- 그 평면을 다시 Voronoi regions으로 나누자.
- 이러한 것들이 closest feature regions이라는 것을 기억해라.
- 예를들어, region AB에 있는 한 점은 edge AB의 내부와 가장 가깝다.
- dashed lines이 인접한 edges와 수직이라는 것을 주목해라.
- 우리는 3개의 vertex regions, 3개의 edge regions, 그리고 1개의 내부 region을 갖는다.

* 3 line segments
- 우리는 그 삼각형의 3개의 edges를 3개의 line segments로 다룰 수 있다.
- 우리는 각 선분과 관련하여 Q의 uv들을 결정할 수 있다.
- 우리는 우리가 triangle의 Voronoi regions을 결정하는 것을 돕기 위해 line segment barycentric coordinates를 결합할 수 있다.

* Vertex regions
- 선분 uv들을 결합하여, 우리는 Q가 vertex region에 있는지를 결정할 수 있다.
- 예를들어, region A는 선분 CA의 u와 선분 AB의 v로부터 결정된다.
- 그것은 subscripts가 가리키는 것이다.

region A : u_CA <= 0 && v_AB <= 0
region B : u_AB <= 0 && v_BC <= 0
region C : u_BC <= 0 && v_CA <= 0

* Edge regions
- 우리는 the line segment barycentric coordinates를 사용하여 한 점이 edge region에 있는지를 부분적으로(partially) 결정할 수 있다.
- 그러나 그것들이 우리에게 한 점이 AB의 위 또는 아래에 있는지를 말해주지 않기 때문에 어떤 정보를 놓치는 중이다.

ex) u_AB > 0 && v_AB >0 인데, 그 점이 edge 위에 있는지 아래에 있는지 모른다.
Line segment uv가 충분하지 않다는 것이다.

* Interior region
- 또한, 우리는 한 점이 내부에 있는지를 결정할 방법을 가지고 있지 않다.

* Triangle barycentric coordinates
- 우리는 삼각형에 대해 Q의 barycentric coordinates를 연산하여 우리가 필요한 추가 정보를 얻을 수 있다.
- 우리가 Q를 그 삼각형들의 정점의 가중된 합으로 표현하자.
- 선분에서 처럼, 우리는 barycentric coordinates가 더해서 1이 되기를 요구한다. 이제는 2개 대신에 3개의 좌표를 요구하는 것을 제외하고.
- 우리는 여전히 부호에 어떠한 제한을 두고있지 않다. 그래서 그 좌표들은 개별적으로 음수가 될 수 있다.
- 이러한 새로운 barycentric coordinates는 (u,v,w)의 관점에서 평면위의 어떤 점을 나타내게 해준다.

* Linear algebra solution
- 만약 우리가 이전의 방정식을 결합한다면, 우리는 선형대수를 사용하여 Q의 barycentric coordinates를 연산할 수 있다.
- 우리는 이 방식으로 그것들을 연산하지 않을 것이다. 왜냐하면 그것은 우리의 기하학적 이해를 줄이기 때문이다.

* Fractional areas
- 우리가 삼각형에 대해 barycentric coordinates를 기하학적으로 이해하게 해보자. 이제, 우리는 Q가 내부에 있다고 가정한다.
- 그 point to line segment problem을 다시 떠올리자.
- 그 경우에, 우리는 barycentric coordinates를 partial segments의 fractional lengths에 연관시켰었다.
- 비슷한 방식으로, 우리는 그 삼각형의 barycentric coordinates를 partial triangles의 fractional areas에 연관시킬 수 있다.
- 이것은 barycentric coordinates가 안에 들어있는(insribed) 삼각형들의 면적에(areas) 비례하다는 것을 가리킨다.

* The barycentric coordinates are the fractional areas
- 그래서 u는 BCQ의 면적에 비례한다.
- u가 A에 대한 barycentric coordinate 이지만, 그것이 오직 A를 포함하지 않는 sub-triangle 만의 area에 비례한다는 것에 주의해라.
- 비슷한 규칙들이 v와 w에 적용된다.

u ~ Area(BCQ)
v ~ Area(CAQ)
w ~ Area(ABQ)

* Barycentric coordinates
- 만약 우리가 Q를 A 쪽으로 움직인다고 상상해라
- 그러고나서, u는 전체 삼각형을 덮게 되고, 반면에 v와 w는 사라진다.
- 그러므로, u는 명백히 A에 대한 가중치이다.
- 안에있는 삼각형들을 기반으로, 우리는 u,v,w에 대한 이러한 공식들에 도달하게 된다.

* Barycentric coordinates are fractional
- 너는 여기에서 한 경향성을 알아차렸을지도 모른다.
- line segments에 대해 우리는 barycentric coordinates는 fractional lengths이다.
- triangles에 대해, barycentric coordinates는 fractional areas 이다.
- tetrahedrons에 대해 너는 barycentric coordinates가 fractional volumes이라는 것을 추측할지도 모른다. 그리고 그게 맞다.

line segment : fractional length
triangles : fractional area
tetrahedrons : fractional volume

* Computing Area
- 그래서 우리는 어떻게 한 삼각형의 면적을 연산하는가?
- 평행사변형의 넓이가 그 삼각형의 두 개의 변의 외적으로부터 구해진다는 것을 상기해라.
- 2D에서 cross product는 scalar이다.
- 삼각형 ABC의 넓이는 관련된 평행사변형의 넓이의 절반이다.
- ABC의 감기는 순서(winding order)에 따라서, 그 area가 음수일 수 있다는 것에 주의해라.

* Q outside the triangle
- 우리가 삼각형에 대해 signed areas를 쓰기 때문에, 우리는 Q가 ABC의 밖에 있는 경우를 처리할 수 있다.
- Q가 edge BC의 밖에 있을 때, v와 w는 양수이고, 그것들의 합은 1보다 더 크다.
- 반면에, BCQ의 감기는 순서는 반대가 된다. 그래서 U는 음수가 된다.
- 그럼에도 불구하고, 그 합은 u + v + w == 1이 된다.

* Voronoi versus Barycentric
- Voronoi regios != barycentric coordinate regions
- The barycentric regions는 여전히 유용하다.
- 우리가 계속하기전에, 나는 중요한 구분을 할 필요가 있다.
- 우리의 접근법은 Voronoi regions을 사용하여 가장 가까운 점을 연산하는 것이다.
- 우리는 그러한 영역을 찾는 것을 돕기 위해 barycentric coordinates를 연산한다.
- line segment의 경우에, 우리는 barycentric coordinates가 즉시 우리에게 Voronoi regions을 준다는 것을 알았다.
- 그러나 삼각형에 대해서, Voronoi regions는 barycentric coordinates에 의해 결정되는 regions과 같지 않다.
- 우리는 이것을 다룰 수 있지만, 처음에 한 삼각혀에 대해 barycentric regions에 대해 보자.

* Barycentric regions of a triangle
- 여기에 우리의 삼각혀이 있다.
- barycentric regions가 무엇인가?

* Interior
- 우리는 모든 좌표들이 양수일 때, interior region을 갖는다.
u > 0 && v >0 && w> 0

* Negative u
- 만약 u가 음수라면 (u <= 0), 그러면 Q는 edge BC 밖에 있다.

* Negative v
- 만약 v가 음수라면, 그러면 Q는 edge CA 밖에 있다.

* Negative w
- 마지막으로, 만약 w가 음수라면, 그러면 Q는 edge AB 밖에 있다.

* The uv regions are not exclusive
- barycentric regions는 상호배제하지 않는다.
- 두 개의 barycentric coordinates가 동시에 음수일 수 있다.
- 이 경우에, 그 query point는 동시에 두 개의 edges의 밖에 있다.
- 일반적으로, 이것은 Q가 vertex region에 있다는 것을 가리키지 않는다.
- 이 슬라이드에서, 우리는 Q가 BC와 CA 밖에 있는 것을 보지만, BC에 가장 가깝다.

* Finding the Voronoi region
- Voronoi region을 인식하기 위해 barycentric coordinates를 사용해라
- 3개의 line segments와 그 삼각형에 대한 Coordinates
- Regions는 올바른 순서로 고려되어야 한다.

- 우리는 이제 triangle의 Voronoi regions을 결정할 충분한 정보를 가지고 있다.
- 한 query point P는 삼각형과 그리고 3개의 선분들 독립적으로 barycentric coodinates를 가질 수 있다.
- 이것은 총 9개의 barycentric coordinates를 준다. 
- 각 line segment에 대해 두 개 그리고 그 삼각형에 대해 3개
- 우리는 Voronoi region을 결정하기 위해 이러한 9개의 스칼라값을 사용할 수 있다.
- 우리는 우리의 logic을 올바르게 구성하기 위해 주의해야만 한다.
-  올바른 접근법은 처음에 가장 낮은 차원의 features를 고려하는 것이다: vertices, 그러고나서 edges, 그러고나서 triangle's interior

* First : vertex regions
- 우리는 처음에 vertex regions을 테스트한다.
- 여기에 우리는 3개의 line segments로부터 6개의 coordinates를 사용한다.

region vertex A : u_CA <= 0 && v_AB <= 0
region vertex B : u_AB <= 0 && v_BC <= 0
region vertex C : u_BC <= 0 && v_CA <= 0

* Second : edge regions
- line segment uv들이 query point가 edge Voronoi region 안에 있는지를 결정하는데 충분하지 않다는 것을 상기해라.
- 편리하게도, 삼각형의 uv는 놓친 정보를 제공한다.

* Second : edge regions solved
- 우리는 line segment uv들을 triangle uv와 결합할 수 있는데, query point가 edge region 안에 있는지를 결정하기 위해서이다.
- 우리는 그 query point가 A와 B 사이에 있는지를 가리키기 위해 두 개의 line segment uv를 갖는다.
- 우리는 이제 그 query point가 AB 위에 있는지를 가리키기 위해 triangle w coordinate를 가진다.

region edge AB : u_AB > 0 && v_AB > 0 && w_ABC <= 0
region edge BC : u_BC > 0 && v_BC > 0 && u_ABC <= 0
region edge CA : u_CA > 0 && v_CA > 0 && v_ABC <= 0

* Third : interior region
- 모든 다른 테스트들이 실패한 후에, 우리는 Q가 interior region에 있다고 결정한다.
- 어떠한 다른 경우가 없기 때문에, 우리는 삼각형 uv들이 모두 양수라고 주장한다.
region interior ABC : u_ABC > 0 && v_ABC > 0 && w_ABC > 0

* Closest point 
- Find the Voronoi region for point Q
- Use the barycentric coordinates to compute the closest point P
- 마침내 우리는 삼각형에서 가장 가까운 점을 연산할 준비가 되었다.
- 처음에, 우리는 점 Q에 대해 Voronoi region을 찾는다.
- 그러고나서, 우리는 가장 가까운 점을 연산하기 위해 적절한 barycentric coordinates를 사용한다.

* Example 1
- 여기에 가장 가까운 점을 어떻게 연산하는지에 대한 예제가 있다.
- 우리는 삼각형 ABC와 query point Q로 시작한다.
- 우리는 u_AB가 음수라는 것을 결정한다.
- 우리는 v_BC가 또한 음수라는 것을 결정한다.
- 우리는 Q가 vertex region B에 있다고 결론짓는다.
- 그래서 P = B이다.
- A와 C는 P에 기여하지 않는다.

* Example 2
- 여기에 다른 위치를 가진 Q의 또 다른 예시이다.
- 처음에, Q는 어떠한 vertex region에 들어있지 않다고 결정한다.
- 우리는 Q가 B의 오른쪽에 있다는 것을 결정한다 (u_AB > 0)
- 그리고 A의 왼쪽에 있다는 것을 결정한다. (v_AB > 0)
- 그래서 Q는 A와 B 사이에 있다.
- 우리는 Q가 AB의 밖에 있는지를 결정하기 위해 삼각형의 barycentric coordinate를 사용한다.
- Q는 edge region AB에 있다. (u_AB > 0 && v_AB > 0 && w_ABC <= 0)
- 가장 가까운 점 P는 AB 위에 있다.
- 그래서 우리는 P를 결정하기 위해 AB의 barycentric coordinates를 사용한다
   P = u_AB * A + v_AB * B

* Implementation
input: A, B, C, Q
compute uAB, vAB, uBC, vBC, uCA, vCA
compute uABC, vABC, wABC

// Test vertex regions
...

// Test edge regions
...

// Else interior region
...

- 우리는 입력으로 삼각형의 정점들과 query point Q를 갖는다.
- 모든 line segment barycentric coordinates를 연산해라.
- 그러고나서 triangular barycentric coordinates를 연산해라.
- 그러고나서 우리는 Voronoi regions을 연산하기 시작한다.

* Testing the vertex regions
// Region A
if ( vAB <= 0 && uCA <= 0)
     P = A
     return

// Similar tests for Region B and C

- 처음에 3 개의 vertex regions을 테스트한다.
- 우리는 여기에서 early return을 얻을 수 있다.

* Testing the edge regions
// Region AB
if (uAB > 0 && vAB > 0 && wABC <= 0)
    P = uAB * A + vAB * B
    return

// Similar for Regions BC and CA

- 다음에 우리는 edge regions를 테스트한다.

* Testing the interior region
// Region ABC
assert(uABC > 0 && vABC > 0 && wABC > 0)
P = Q
return

이 점에서, Q는 그 삼각형의 내부에 있음에 틀림없다. 그래서 우리는 이것이 사실이라고 주장한다.
- 그러고나서 barycentric coordinates는 triangular barycentric coordinates이고, 모든 삼각형 정점들은 P에 기여한다.

* Section 3 : Point to Convex Polygon
- 그래서 우리는 point vs triangle을 정복했다.
- 이제, point to convex polygon을 봐서 우리의 목표에 더 가까이 가자.

* Convex polygon
- 여기에 임의의 convex polygon (볼록 다각형) ABCDE가 있다.
- 우리는 그 다각형이 한 변(edge)를 가로지르는 내부의 점들 사이의 선분이 존재하지 않기 때문에 convex라고 말한다.

* Polygon structure
struct Polygon
{
     Vec2* points;
     int count;
};

- 여기에 polygon structure 코드가 있다.
- 이것은 2D points의 배열이다.

* Convex polygon : closest point
- 그래서 이제 우리는 query point Q를 가지고 있다.
- 우리는 가장 가까운 점 P를 결정하기를 원한다.
- 우리는 이것을 쉽게 시각화할 수 있다. 그러나, 컴퓨터가 이것을 정확하고 효율적으로 하게 하는 것은 또 다른 문제이다.
- How do we compute P?

* What do we know?
- Closest point to point
- Closest point to line segment
- Closest point to triangle
- 우리가 이미 어떻게 할 지를 아는 것들이 위의 것이다.

* Simplex
- 편리성을 위해서, 우리는 points, line segments, triangles를 하나의 공통된 주제 아래에서 그룹화 한다.
- A simplex is a point, a line segment, a triangle, or a tetrahedron 이다.
- 우리는 0차원 simplex에 대해 0-simplex라고 말한다 (a point), 1차원 simplex에 대해 1-simplex(a line segment), 등등

* Idea : inscribe a simplex
- 그래서 여기에 P를 연산하는 것에 대한 아이디어가 있다.
- 우리는 그 polygon에 triangle을 inscribe한다.
- 왜 그렇게 하는가?
- 왜냐하면 우리는 한 삼각형에서 가장 가까운 점을 어떻게 연산할지를 알고 있기 때문이다.

* Idea : closest point on simplex
- Vertex C가 Q에 대한 simplex에서 가장 가까운 점이다.

* Idea : evolve the simplex
- 이제 Q에 대해 simplex를 어느정도 진화시키기 위해 그 simplex에서 가장 가까운 점을 사용한다.

* Simplex vertex
struct SimplexVertex
{
     Vec2 point;
     int index;
     float u;
};

- 여기에 simplex vertex structure가 있다.
- 그 점은 한 polygon vertex로부터 복사된다.
- 우리는 또한 그 index를 저장한다.
- 우리 closest point calculations를 위해 barycentric coordinate u를 포함한다.

* Simplex
struct Simplex
{
     SimplexVertex vertexA;
     SimplexVertex vertexB;
     SimplexVertex vertexC;
     int count;
};

- 여기에 simplex data structure가 있다.
- 그것은 a point, a line segment, or a triangle를 나타낼 수 있다.

* We are onto a winner!
- 그래서 나는 우리가 성공가능한 것을 찾아냈다고 생각한다.
- 우리는 처리할 어떤 세부사항을 가진다.
- 주로, 우리는 그 simplex를 어떻게 진화시킬지를 이해할 필요가 있다.

* GJK distance algorithm
- Computes the closest point on a convex polygon
- Invented by Gilbert, Johnson, and Keerthi
- 운 좋게도, Gilbert, Johnson, and Keerthi가 우리를 위해 GJK라고 불려지는 한 알고리즘의 세부사항을 작업했다.
- Gilbert와 회사는 1980년대 후반에 알고리즘에 대한 몇 가지 논문들을 썼었다.
- 그것들은 모두 매우 수학적이고 이해하기에 어렵다.
- 그것들은 또한 좋은 pastel colors가 부족하다.

* The GJK distance algorithm
- Inscribed simplexes
- Simplex evolution
- 그것의 깊은 수학적 뿌리에도 불구하고, GJK는 직관적 기하학 해석이다.
- GJK는 iterative algorithm이다.
- 우리는 우리의 convex polygon에 새겨진 한 임의의 simplex로 시작한다.
- 우리는 그 simplex에서 가장 가까운 점을 연산하고, 그 polygon에서 가장 가까운 점 쪽으로 simplex를 진화시키기위해 그 결과를 사용한다.
- 원리적으로, 우리는 유한한 개수의 반복으로 정확한 솔루션을 얻는다. 현실적으로, 우리가 처리해야 할 numerical problems가 있다.
- GJK 알고리즘을 더 섦여하기 보다, 이 시점에서, 그것이 어떻게 작동하는지에 대한 예제를 보는 것이 더 좋다.

* Starting simplex
- Start with arbitrary vertex. Pick E.
- This is our starting simplex
- 우리는 우리의 convex polygon과 query point Q를 가지고 있다.
- 우리는 한 임의의 simplex를 골라서 GJK 알고리즘을 시작한다.
- 이 경우에, 우리는 vertex E를 가진 0-simplex를 선택한다.

* Closest point on simplex
- P is the closest point
- 다음으로 우리는 Q에 대해 우리의 simplex에서 가장 가까운 점을 결정한다.
- 이 경우에 P = E이다.

* Search vector
- Draw a vector from P to Q. And call this vector d.
- 이제 우리는 search vector를 구성한다.
- 이 벡터는 우리의 simplex에 있는 가장 가까운점에서 Q를 가리킨다.
- 우리가 0-simplex를 가지고 있기 때문에, 그 가장 가까운 점은 E이다.

* Find the support point
- Find the vertex on polygon furthest in direction d.
- This is the support point.
- 우리는 이제 search direction에서 가장 멀리있는 정점을 결정한다.
- 이것은 support point라고 불려진다.

* Support point code

int Support(const Polygon& poly, const Vec2& d)
{
   int index = 0;
   float maxValue = Dot(d, poly.points[index]);
   for (int i = 1; i < poly.count; ++i)
   {
      float value = Dot(d, poly.points[i]);
      if(value > maxValue)
      {
         index = i;
         maxValue = value;
      }
   }
   return index;
}

- 여기에 그 support point를 연산하는 코드가 있다.
- 그 거리는 간단한 dot product로 결정된다.
- 그 코드는 모든 polygon vertices에 대해 반복하고, 가장 큰 내적 값을 추적한다.
- 그 비교가 모두 상대적이기 때문에, search direction d는 표준화 될 필요가 없다.

* Support point found
- C is the support point.
- 이 경우에, 시각적으로 C가 명백히 support point이다.
- 그래서 우리는 C로 무엇을 하는가?

* Evolve the simplex
- Create a line segment CE
- We now have a 1-simplex.
- 우리는 C를 현재의 simplex와 합친다.
- 우리가 E를 가진 0-simplex를 가졌기 때문에, 우리는 이제 CE를가진 1-simplex를 가진다.

* Repeat the process
- Find closest point P on CE
- 우리는 그 프로세스를 이제 반복한다.
- 우리는 우리의 현재 simplex CE에서 가장 가까운 점을 찾는다.

* New search direction
- Build d as a line pointing from P to Q
- 우리는 P에서 Q를 가리키는 새로운 search direction을 구성한다.

* New support point
- D is the support point
- 다음으로, 우리는 방향 d에서 새로운 support point를 연산한다.
- 우리는 support point로서 D를 찾는다.

* Evolve the simplex
- Create triangle CDE.
- This is a 2-simplex.
- 우리는 우리의 simplex에 D를 합치고, 2-simplex CDE를 만든다.

* Closest point
- Compute closest point on CDE to Q
- 우리는 그 simplex에 대해 가장 가까운 점으로서 P를 연산한다.

* E is worthless
- Closest point is on CD
- E does not contribute
- 우리는 E가 P에 기여하지 않는 다는 것을 알게 된다.
- 그래서 우리는 simplex에서 E를 제외한다.
- 우리는 simplex가 결코 3개 이상의 정점을 갖지 않도록 기여하지 않는 정점을 cull해야한다.

* Reduced simplex
- We dropped E, so we now have a 1-simplex
- 현재 simplex는 이제 CD 이다.
- 우리가 2-simplex에서 1 or 0-simplex로 갈 수 있다는 것을 주목해라.
- 그래서 우리는 동시에 두 개 까지의 simplex vertices를 제거할 수 있다.

* Termination
- Compute support point in direction d.
- We find either C or D. Since this is a repeat, we are done.
- 우리는 P에서 Q를 가리키는 새로운 search vector를 형성한다.
- 이제 그 새로운 support point는 C 또는 D 둘 중 하나이다.
- 우리는 그 점을 유일하게 결정할 수 없다.
- 그러나 그것은 중요하지 않다.
- 이러한 정점들은 이미 simplex에 있기 때문에, 우리는 어떠한 진행을 할 수 없다.
- 그러므로, 우리는 P가 가장 가까운 점으로 그 알고리즘을 종료시킨다.

* GJK algorithm
Input : polygon and point Q
pick arbitrary initial simplex S
loop
   compute closest point P on S
   cull non-contributing vertices from S
   build vector d pointing from P to Q
   compute support point in direction d
   add support point to S
end
- 여기에 GJK 알고리즘에 대한 pseudo code가 있다.
- 이것은 iterative algorithm이고, 그래서 우리는 loop해야만 한다.
- 이 코드는 종료 조건을 포함하지 않는다.
- 나는 이것을 잠시 뒤에 다룰 것이다.

* Numerical Issues
- Search Direction
- Termination
- Poorly formed polygons
- 내가 너에게 경고했었듯이, 우리가 다룰 필요가 있는 많은 numerical problems가 있다.
- 내 경험에서, 이러한 문제들은 search direction과 loop termination에 영향을 미친다.

* A bad direction
- d can be built from PQ
- Due to round-off : dot(Q - P, C- E) != 0
- 이제까지, 우리는 P에서 Q로가는 벡터로서 search direction을 연산했었다.
- 이것은 항상 가장 좋은 접근법이 아니다.
- 이 예제에서, P는 the line segment CE에서 barycentric coordinates로부터 연산된다.
- Barycentric coordinates는 round off error를 겪는다. 그래서 PQ는 segment CE에 대해 수직이 아닐지도 모른다.

* A real example in single precision
Line Segment
A = [0.021119118, 79.584320]
B = [0.020964622, -31.515678]

Query Point
Q = [0.0 0.0]

Barycentric Coordinates
(u, v) = (0.28366947, 0.71633047)

Search Direction
d = Q - P = [-0.021008447, 0.0]

dot(d, B - A) = 3.2457051e-006

- 여기에 내가 겪었던 실제 세계 예제가 있다.
- 나는 선분 AB와 query point Q를 가지고있다.
- 나는 가장 가까운 점 P를 연산하고, search direction d를 만들기 위해 그것을 사용한다.
- 내가 segment direction과 search direction을 내적을 연산했을때, 0이 아닌 값을 얻는다.
- 그렇다, 그것은 작언 에러지만, search direction에서 작은에러들은 커질 수 있다.

* Small errors matter
- 부정확한 search directions는 나쁘다. 왜냐하면 그것들은 부정확한 support points를 이끌 수 있기 때문이다.
- 이것은 괃환 반복 또는 심지어 부정확한 결과를 이끌지도 모른다.

* An accurate search direction
- Directly compute a vector perpendicular to CE
- d = cross(C - E, z)
- where z is normal to the plane.
- 우리가 어려움없이 정확한 search direction을 얻을 수 있다는 것이 밝혀졌다.
- 가장 가까운 점이 한 edge위에 있을 때, 너는 search direction을 연산하기 위해 closest point를 사용해선 안된다.
- 대신에, 그 edge에 수직인 벡터를 형성하여 search direction을 연산하는게 더 좋다.
- 우리는 edge vector와 plane normal를 외적하여 perpendicular vector를 얻을 수 있다.

* The dot product is exactly zero
- edge direction : e = (x y)
- search direction : d = (-y x)
- dot product : dot(e, d) = -xy+ xy = 0
- 이것은 search direction이 그 edge에 수직이라는 것을 의미한다.
- 그 내적은 numerically하게 zero이다. 심지어 floating point arithmetic으로 할지라도.

* Fixing the sign
- Flip the sign of d so that : dot(d, Q - C) > 0
- Perk : no divides
- 그러고나서 우리는 그것이 Q를 가리키도록하기 위해 d의 부호를 바꿀 필요가 있다.

* Termination conditions
- 부정확한 search directions으로, termination conditions는 많은 슬픔을 만들어 낼 수 있다.
- 우리는 너무 일찍 종료하길 원하지 않고, 전혀 종료하지 않길 원하지 않는다.
- infinite loop는 너의 frame-rate를 죽일 모든 것이다.
- 그래서 우리는 termination conditions에 좋은 일들을 하려고 해야 한다.

* Case 1 : repeated support point
- 우리의 첫 번째 케이스는 반복된 support point이다.
- 이 경우에, 우리는 우리의 현재 simplex에 속하는 support point를 연산한다.
- 이것은 우리가 어떠한 진행을 할 수 없다는 것을 가리킨다.그래서, 우리는 가장 가까운 점 P로 종료해야 한다.

* Case 2 : containment
- We find a 2-simplex and all vertices contribute
- 우리의 GJK 반복에서 어떤 시점에서, 우리는 2-simplex를 발견할지도 모르는데, 거기에서 Q에 대한 모든 triangular barycentric coordinates가 양수일 수 있다.
- 이것은 Q가 2-simplex 안에 있다는 것을 의미한다. 그러므로, parent polygon 안에 있다는 것이다.

* Case 3a : vertex overlap
- We will compute d = Q - P as zero
- So we terminate if d = 0
- 만약 Q가 한 정점과 중첩된다면, 그러면 P = Q이고, 우리는 zero search direction을 연산하게 될 것이다.
- 그래서 우리는 d가 zero라면 종료해야 한다.

* Case 3b : edge overlap
- d will have an arbitrary sign
- Q가 edge와 중첩되는 것이 가능하다.
- 우리는 d를 perpendicular vector로서 연산할 수 있다.
- 그러나, 우리는 그것의 부호를 결정할 수 없다.
- d의 부호가 임의로 될 것이기 때문에, 우리는 두 경우를 조사해야 한다.

* Case 3b : d points left
- If we search left, we get a duplicate support point. In this case we terminate.
- 만약 d가 왼쪽을 가리킨다면, 우리는 Case 1 : a duplicate support point를 얻는다.

* Case 3b : d points right
- If we search right, we get a new support point (A).
- 만약 d가 오른쪽을 가리킨다면, 그러면 우리는 새로운 support point를 얻는다.

- But then we get back the same P, and then the same d.
- Soon, we detect a repeated support point or detect containment.
- round-off error에 따라, 우리는 P가 edge CD 위에 있거나 P가 triangle ACD안에 있다는 것을 알게 될 것이다.
- 그래서 우리는 duplicate vertex를 얻거나 또는 containment를 탐지할 것이다.

* Case 4 : interior edge
- d will have an arbitrary sign
- 우리가 고려하는 마지막 경우는 Q가 우리의 simplex의 interior edge와 중첩될 때 이다.
- 이 경우에, 우리의 simplex는 an interior line segment이다.
- Q가 그 line segment와 중첩되기 때문에, 우리는 d를 유일하게 결정할 수 없다.

- Similar to Case 3b
- 이것은 궁극적으로 Case 3b와 같다.
- 우리가 어떤 d의 부호를 결정하든, 우리는 repeated vertex를 탐지할 수 있을것이다.

* Termination in 3D
- May require new/different conditions
- Check for distance progression
- 3D에서 GJK는 다른 termination conditions이 필요하다.
- 예를들어, 너는 P에서 Q로 가는 거리가 매 반복마다 줄어드는지를 테스트 할 필요가 있을지도 모른다.

* Non-convex polygon
- Vertex B is non-convex
- 만약 우리가 non-convex polygon을 만난다면 무슨일이 발생하는가?

* Non-convex polygon
- B is never a support point
- GJK는 non-convex vertex는 보지 않을 것이다. 왜냐하면 , 그것은 결코 support point로서 나타나지 않을 것이기 때문이다.

* Collinear vertices
- B, C, and D are collinear
- collinear points로는 무엇이 발생하는가?

- 2-simplex BCD
- 우리는 degenerate triangular simplex로 끝날 수 있다.

- area(BCD) = 0
- 그 삼각형 넓이는 zero이다. 그래서 그 triangular barycentric coordinates는 무한이다.
- 너는 area로 나누는 것을 피할 수 있다.
- 너는 너의 triangle solver가 이 경우에 vertex or edge region을 선택하도록 보장해야만 한다.
- 세부사항에 대해 데모코드를 보아라.

* Section 4 : Convex Polygon to Convex Polygon
- 그래서 그것이 GJK를 위한 것이다.
- 우리는 이제 우리의 최종 목표를 다룰 준비가 되어있다: 한 쌍의 convex polygons 사이의 closest point.

* Closest point between convex polygons
- 그래서 우리는 두 개의 convex polygons 사이의 가장 가까운 점을 연산하기를 원한다.
- 이것은 a single polygon에서 가장 가까운 점 보다 더 복잡한 것처럼 보일지도 모른다.
- 그러나, 그 두 문제를 연관짓는 방법이 있다.

* What do we know?
- 우리는 GJK 반복을 사용하여 point to polygon을 풀기위해 point to simplex problem에 대한 지식을 적용했었다.

* What do we need to know?
- 이제, 우리는 어떻게 point to polygon의 지식을 polygon to polygon에 적용할 수 있는가?

* Idea
- Convert polygon to polygon into point to polygon
- Use GJK to solve point to polygon
- 그 기본 아이디어는 우리는 polygon to polygon을 point to polygon으로 바꾼다.
- 그러고나서 우리는 point to polygon problem을 풀기위해 GJK를 사용할 수 있다.

* Minkowski difference
- 우리에게 정확히 우리가 필요한 것을 주는 Minkowski difference라고 불려지는 geometrical construction이 있다고 밝혀졌다.
- Minkowski difference는 우리가 두 개의 polygons을 단일의 convex polygon으로 합치도록 해준다.

* Minkowski difference definition

- 여기에, Minkowski difference에 대한 수학적 정의가 있다.

* Building the Minkowski difference
Input : polygon X and Y
array points
for all xi in X
    for all yj in Y
        points.push_back(yj - xi)
    end
end

polygon Z = ConvexHull(points)

- 우리는 단순한 기법을 사용하여 super polygon을 구성 할 수 있다.
- 우리는 두 폴리곤의 점들 사이의 차를 연산하기 위해 두 개의 nested loops를 사용한다.
- 그러고나서 우리는 그 point cloud의 convex hull를 연산한다.
- 물론,이것은 하기에 싸지 않지만, 인내심을 가져라
- 무지개 끝에 황금이 있다.

* Example point cloud
- 여기에 그 point cloud가 이 삼각형과 사각형에 대해 어떻게 보이는지가 있다.
- 그렇다, 나는 실제로 그러한 점들을 연산한다.

* Building the convex hull
- 나는 convex hull algorithms을 다루지 않을 것이지만, 개념적으로 우리는 convex hull algorithm을 a shrink wrapping process로서 상상할 수 있다.

- 우리는 extreme point로 시작하고, 둘레를 따라 우리의 길을 만든다.

* The final polygon
- 우리는 이제 Minkowski difference polygon에 도착했다.

* Property 1 : distances are equal
- distance(X, Y) == distance(O, Y - X)
- Minkowski difference는 몇 가지 놀랄만한 특성들을 가지고 있다. (유레카적)
- 처음에, X와 Y 사이의 거리는 원점과 super polygon 사이의 거리와 가탇.

* Property 2 : support points
- support(Z, d) = support(Y, d) - support(X, -d)
- 둘 째로, 우리는 X와 Y의 support points를 결합하여 Z의 한 support point를 결정할 수 있다.
- 우리는 Y에서 d 방향에 있는 support point와 X에서 -d방향의 support point를 사용한다.
- 이러한 두 개의 support points의 차는 Z에서의 support point와 동일하다.
- 우리는 explicitly하게 Z를 구성하지 않고 Z의 support point를 연산할 방법을 안다.

- 이것은 우리가 Z를 explicitly하게 구성할 필요가 없다는 것을 의미한다.
- 그래서 우리는 convex hull을 연산할 필요가 없다.
- 이것은 우리의 어플리케이션에서 많은 양의 시간과 공간을 절약시킨다.

* Modifying GJK
- Change the support function
- Simplex vertices hold two indices
- 우리는 Minkowski difference를 다루기 위 쉽게 GJK를 수정할 수 있다.
- 우리는 그냥 support function을 바꾸고 좀 더 bookkeeping을 추가할 필요가 있다.
- 세부사항을 위해 데모 코드를 보아라.

* Closest point on polygons
- Use the barycentric coordinates to compute the closest point on X and Y
- See the demo code for details
- 우리는 또한 원래의 폴리곤에 대해 closest points를연산하기 위해 GJK를 수정할 수 있다.
- 그러고나서 우리는 the closest points를 결정하기위해 original vertices에 대해 barycentric coordinates를 적용한다.
- 또 다시, 세부사항에 대해서는 데모 코드를 보아라.

2019년 3월 12일 화요일

지금까지 만든 것 (2019년 1월 기준)

1월에 프로젝트를 끝내고 이것저것 하다보니 업데이트를 해놓지 않았다.
이런 기록을 남겨두는 것은 이전으로부터 어떻게 발전해왔는지를 보여주기 때문에
종종 남겨놓는 것이 좋다고 생각한다.

1. 현재 만들어놓은 3D 게임 엔진의 최종 상황이다.
Sponza 모델과 Nanosuit를 통해 렌더링 했다.
영상은 최종 Scene을 만들기 위해 Light의 여러 Property를 수정하는 것을 보여준다.
아직 Culling 기능을 개발하지 않고 최적화가 이루어지지 않아 한 프레임 연산 속도는 느리다. Object Picking With Broad Phase(Dynamic AABB Tree), Model Loading (using Assimp) and Rendering + Model Diffuse/Specular/Emissive/Normal/Height Map (Normal Mapping, Parallax Occlusion Mapping), Light Property 편집, Light Shadow 설정 등이 들어갔다. learnopengl.com에서 배웠던 것을 Deferred Rendering 기반으로 바꾸면서 나의 이해를 증진시켰다. Post-Processing으로 Gamma Correction, Bloom, SSAO가 들어갔다.

2. 물리엔진 기반을 Box2D로 바꾸기로 결심하면서 Box2D-lite 코드를 이해하고 DirectX11 로 포팅해서 시뮬레이션 세팅하여 찍은 영상이다. 현재 Box2D 관련 자료들을 공부하며 이 기반을 다시 철저히 이해하기 위해 공부중이다. 2D를 이해하고 3D로 넘어갈 예정이다.

3. Terrain Rendering + Physics
Terrain을 Triangle Strip으로 한 번의 draw call로 렌더링했다.
그리고 Mesh vs Sphere의 Collision Detection and Response를 특히, Collision Detection 부분을 Bullet Physics를 참고해서 따라했다. 그 Bullet Physics의 코드를 이해하고 내 엔진에 적용할 수 있어서 매우 도전적이고 재미있는 경험이였다.
여기에서 BroadPhase(Dynamic AABB Tree) -> Mesh Quantization -> Early Exit (AABB vs AABB(of sphere) -> Narrow Phase(Triangle vs Sphere, Closest point on triangle using barycentric coordinate from Real Time Collision Detection book). 등의 많은 것들이 적용되었다. Mesh vs Box를 구현하려다 포기하고 Box2D로 돌아가게 되는 계기였는데, 어쨋든 가치있는 작업이였다.