'Fixed Function Pipeline Stages - Vulkan Game Engine Tutorial 04' 정리

Youtube Link


​ 이번에 하고 싶은건 그래픽 파이프라인(VkPipeline)을 하나 만드는 것입니다. ​

vkCreateGraphicsPipelines() 함수를 호출하고 싶은 건데,
만들어진 파이프라인이 어떻게 동작할 건지 설정 값들을 미리 다 세팅해줘야 합니다. ​

이를 위해 VkGraphicsPipelineCreateInfo 구조체를 채워야 합니다.
‘Graphics Pipeline Overview - Vulkan Game Engine Tutorial 02’ 정리 에서 봤던 그래픽 파이프라인을 다시 한 번 봐봅시다. ​

triangle

VkGraphicsPipelineCreateInfo 구조체가 각 단계별 동작의 세팅 값 및 부가적으로 필요한 정보를 가지고 있습니다. 요걸 채워서 함수 호출할 때 넘겨줘야 합니다.
거대한 구조체네요ㅠㅜ ​

위 구조체에서 일부 세팅 값들을 PipelineConfigInfo로 따로 빼두었습니다.
아마도 일부 세팅 값을 바꿔서 비슷한 그래픽 파이프라인을 또 만들 때 사용할 예정인 듯 합니다.

​ 맨 처음 들어온 데이터 뭉치를 어떻게 해석할지 정합니다.
​세 개씩 끊어서 삼각형 하나로 볼지, ​ 두 번째 그림처럼 중첩해서 삼각형을 만들지 정합니다.

​ 참고로, 인풋 데이터는 위치 정보외에 다른 정보들도 포함될 수 있습니다. ​

79s

input assembly

124s

triangle strip

​ 이를 위해, VkPipelineInputAssemblyStateCreateInfo 를 세팅합니다. ​

​ Vertext Shader 까지는 [-1, -1] ~ [1, 1] 좌표계(NDC, Normalized Device Coordinates)를 사용합니다. ​ 요 좌표계를 가져다가 fragmentbuffer 의 어느 부분에 그릴 것인지를 정해야 합니다. ​ 이를 위해, VkPipelineViewportStateCreateInfo 를 세팅합니다. ​ 요 안에 viewport와 scissor 가 또 있습니다.

209s

viewport

​ 위 그림에서 어떻게 매핑할 것인지가 viewport 설정입니다.
​ NDC의 (-1, -1), (1,1)을 framebuffer 의 어디로 매핑할 것인지를 결정합니다.
framebuffer 중에서 특정 영역만 필요한 상황이 있을 수 있습니다. (다른 창에 의해서 가려지거나 해서 그릴 필요가 없는경우?)

이를 위해 scissor가 있습니다. ​ framebuffer의 특정 영역을 골라서, 그 부분만 그리면 된다고 알려 줍니다. ​ 아직은 빌드가 제대로 안되고, ​ Tutorial 5-1 까지 구현을 마치고 난 후에, 값을 바꿔서 테스트 해 봅시다.

viewport and scissor
​ (1) 번은 Tutorial 5-1 까지의 결과물 입니다.
​빨간색이 viewport, 파란색이 scissor 입니다.
​(2)번은 다음과 같이 viewport를 세팅했습니다.

// 시작점 NDC(-1,-1) -> (0, 300)
configInfo.viewport.x = 0.0f;
configInfo.viewport.y = 300.0f;

// offset (0, 300) 기준으로 (+400, +300) 위치로 NDC(1, 1)을 매핑
configInfo.viewport.width = static_cast<float>(width / 2);  // 400 
configInfo.viewport.height = static_cast<float>(height / 2);  // 300
configInfo.viewport.minDepth = 0.0f;
configInfo.viewport.maxDepth = 1.0f;

​ (3)번은 scissor 까지 세팅했습니다.

// offset (0,0) 정하고, 거기에서 (+200, +800) 위치 까지만 표시
configInfo.scissor.offset = { 0, 0 };
configInfo.scissor.extent = { width / 4, height };  // 200, 300

​ 좋은 그래픽 카드에서는 viewport, scissor 여러 개일 수도 있다고 합니다.

​ 그래픽 파이프라인에서 Raterization 은 vertex shader 가 넘겨 준 geometry 데이터들을 받아서
​fragment 들로 바꿉니다.

위에서 봤던 scissor 도 적용하고(scissor test), 카메라에서 너무 가깝거나 먼지도 확인하고(depth test), 삼각형의 앞면 뒷면 여부도 판단(face culling)도 여기서 합니다.
​ 요 동작들을 위한 세팅을 VkPipelineRasterizationStateCreateInfo 에다가 합니다.
​ 이중에 cullMode, frontFace는 삼각형의 뒷면을 런더링 하지 않기 위한 세팅입니다. (face culling)
​ 카메라에 삼각형의 뒷면이 보이는 경우에는 그리지 않음으로써, 성능 향상을 꾀하기 위함입니다.

391s

cull mode

​ 어떤 방향을 앞면으로 볼 건지 결정할 수 있습니다.

​ MSAA: Multisampling Anti-aliasing
​ Rasterization 과정에서 런더링 해야 하는 fragment 를 선정할 때,
​각 fragment의 중앙의 점이 삼각형에 포함되는지 여부로 판단하는 듯 합니다.
​삼각형 안쪽인지 바깥쪽인지가 true / false 로 정해지는 것이죠.
​ 이로 인해, 경계선 부분이 거칠게 보이는 계단 현상(aliasing)이 나타납니다.

478s

msaa no
​ 이를 해결하기 위해, Multisample Anti-aliasing을 사용할 수 있습니다.
​ fragment 를 선정할 때, 한 개 점이 아니라 여러 점을 사용해서, 몇 개의 점이 삼각형에 포함되는지에 따라, 색을 살짝 연하게 칠하던가 하는 듯 합니다.

488s

msaa yes

이를 위해, VkPipelineMultisampleStateCreateInfo 를 세팅합니다. ​

​ 모델 여러 개가 겹쳐 있는 경우, 특정 fragment (픽셀)을 생각해 봅시다.
​첫 번째 모델을 그리면, fragment shader 가 색을 결정해서 framebuffer를 채워 놓은 상태일 겁니다.
​여기서 다음 모델을 그리면, fragment shader 가 framebuffer를 채울 때, 해당 fragment에 기존에 채워져 있는 색깔이랑 같이 잘 표현해야 합니다.
​ 보통은 투명도에 따라 기존 색과 현재 색을 섞는 알파 블렌딩을 사용한다고 합니다.
​ 이를 위해, VkPipelineColorBlendStateCreateInfo 를 세팅합니다. (내부에 VkPipelineColorBlendAttachmentState) ​

​ framebuffer는 보통 color buffer 와 depth 버퍼를 가집니다.
​ depth 버퍼는 해당 픽셀에서 카메라와 가장 가까이 있는 object의 거리를 저장합니다. (어떤 object인지는 관심 없음)
​ 산이 그려 졌고 (depth 0.5), 이제 구름 depth 0.8에 을 그린다고 하면
​ 산과 겹치는 부분의 픽셀은 산에 가려질 테니(depth 버퍼에 저장되어 있는 값이, 현재 그릴려는 object의 depth 보다 작으므로) 그릴 필요가 없습니다.

593s

msaa yes
​ 이를 위해 VkPipelineDepthStencilStateCreateInfo 를 세팅합니다.

​ 앞 장에서 Vertex Shader, Fragment Shader를 작성해서 VkShaderModule 들을 만들었습니다.
​ 요 정보도 파이프라인 생성할 때, 넘겨 줘야 합니다.
​ 이를 위해, VkPipelineShaderStateCreateInfo 를 세팅합니다.

​ 그래픽 파이프라인에 맨 처음 들어오는 데이터 뭉치는 각 Vertex 별로 위치 뿐만 아니라 다른 여러 데이터를 포함할 수 있습니다.
​ 요 데이터들이 어떻게 정렬되어 있는지,
​ 인덱스 버퍼라는 게 따로 있는지 정보도 넘겨줘야 합니다.
​ 이를 위해, VkPipelineVertexInputStateCreateInfo 를 세팅합니다.

이제 지금까지 준비한 세팅 값들로 VkGraphicsPipelineCreateInfo 구조체를 채웁니다.
​요 구조체를 넘겨서, vkCreateGraphicsPipelines() 함수를 드디어 호출합니다.
(pipelineLayout, renderPass 를 아직 채우지 않아서, 정상 실행은 되지 않습니다.)


3. 다이어그램

클래스 다이어그램

Related Content