ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [23] - Pipeline experiments
    Graphics 2021. 7. 30. 20:24

    [22]에서 드디어 삼각형을 만들 수 있게 되었습니다. 기본적인 디버그, 파이프 라인의 구성까지 구현했습니다. 이번엔 여태 만든 것을 토대로 다양한 것들을 해보도록 합니다. 가장 먼저 삼각형을 하나 더 그려보는 작업을 해보도록 합니다.

    이전에 Graphics.h의 DrawTestTriangle 함수를 만들 때, 정점[Vertex/Vertices]을 함께 만들었습니다. 거기에 삼각형을 하나 더 만들 것이니 다른 정점을 추가해줍니다. 될 것 같으나 막상 실행을 해보면 화면에는 똑같이 단 하나의 삼각형만이 보입니다.

    호오

    출력이 되지 않는 이유 중 하나는 두 번째로 정의한 정점들은 방향이 반시계 방향이라는 점입니다. 그것부터 고쳐봅시다.

    두 개의 삼각형이 보입니다. 한 가지 뿌듯한 것은 코드를 상당 Reactive하게 짰기 때문에 Vertices에 Mesh를 추가해주는 것만으로도 이렇게 화면에 출력을 할 수 있게 되었다는 점입니다. 

    이번에는 삼각형 말고 어떤 선을 그을 수 있는지 테스트 해보도록 합시다. 어디서 하면 좋을까요? OpenGL ES와 D3D의 Graphics Pipeline의 차이를 살펴보면서 정점의 정보를 Bind해주는 섹션이 각각 있었습니다. D3D의 경우는 IA Stage의 Topology를 통해서 정점의 성질 정보를 넘겨주었습니다. 따라서 Topology에 다른 값을 넘겨주면 분명 변화를 찾아볼 수 있을 것입니다.

    바로 찾았습니다

    D3D_PRIMITIVE_TOPOLOGY (d3dcommon.h) - Win32 apps | Microsoft Docs이 페이지를 참조하면 알겠으나 다양한 값이 존재합니다. Linelist를 넣으면 어떤 변화를 확인할 수 있을까요?

    띠용

    결과가 그럴 듯 합니다. 

    const Vertex vertices[] =
    {
    { 0.0f,0.5f },   line1
    { 0.5f,-0.5f },  line1
    { -0.5f,-0.5f }, line2

    { 0.5f, 1.0f },  line2
    { 1.0f, 0.5f },  line3
    { 0.5f, 0.5f },  line3
    };

    세 줄이 보이는 것이 합리적입니다. 만약, 삼각형 하나를 그리는 정점을 Linelist로 넘겨줬다면 한 줄만 보이는 것이 정상입니다. 선은 두 개의 정점으로 구성되기 때문입니다.

    이렇게 됩니다

    그러면 이 선으로 삼각형을 그리려면 총 여섯 개의 정점이 필요합니다. 대신 안에는 색이 없을 겁니다.

    이러면 될 겁니다
    잘 됩니다

    이번에는 Strip을 넣어봅시다. D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP으로 정의되어 있습니다. 방금 LINELIST에서 정의한 정점을 일단 그대로 실행해보면 같은 결과를 얻습니다. 하지만 여기서 그치면 따로 정의되어 있을 이유가 없습니다. 중복되는 정점을 제거하면 이 LINESTRIP의 역할을 알 수 있습니다.

    시계방향으로 돌되, 출발과 도착 정점이 같습니다

    같은 삼각형을 얻었습니다. 라인을 따라서 그림을 그릴 생각이라면 이 LINESTRIP을 사용하는 편이 나을 것 같습니다. 다시 TRIANGLELIST옵션을 주고 정점을 세 개로 돌립니다. 이번에는 각 정점마다 고유한 색을 주고, Pipeline이 Rasterize할 때 이 세 가지 색을 Blend하는 모습을 보도록 합시다. 그렇게 하기 위해서는 약간의 Shader 작업이 필요합니다. 

    가장 먼저 InputLayout의 정의부터 다시 짚어보도록 합시다.

    MSDN의 CreateInputLayout의 머리글입니다

    전에 자세히 들여다 보았습니다. Input Layout은 IA Stage에 정점에 대한 정보를 Bind하는 역할을 했습니다. 따라서 이번엔 정점에 위치 정보 이외에 Colour정보도 넘겨야 하니 이것부터 손을 보겠다는 겁니다. 또, Pixel Shader에 왜 색에 대한 정보가 없는지 이 부분에서 알 수 있습니다. D3D는 색에 대한 정보를 Pixel Shdaer가 아니라 IA Stage에서 처리합니다.

    여기를 손 볼 겁니다

    "Position"말고 우리는 "Colour"에 대한 정보가 필요합니다. 

    이것이 D3D11_INPUT_ELEMENT_DESC를 구성하는 방법입니다

    무엇이 바뀌어야 하는가 하면, 가장 먼저 Semantic Name이 Position이 아니고 Colour여야 합니다. 그리고 Input Slot도 바뀌어야 합니다. Format은 같든 다르든 크게 상관이 없습니다. 일단은 가장 무난한 R32G32B32 값을 넣어줍니다.  Aligned Byte Offset은 8u를 넣어줄 것입니다. 왜냐하면 Colour값 이전에 Position이 두 개의 실수(Floating Point)값을 가지기 때문입니다.

    IEEE Format을 사용하며 하나가 4 bytes라고 명시되어 있습니다. 잘 모르겠으면 D3D가 알아서 처리할 수 있도록 옵션을 줄 수도 있습니다. (D3D11_APPEND_ALIGNED_ELEMENT) 나머지는 건드리지 않아도 좋습니다. 

    이제 정점은 단순히 Position의 정보만 갖는 구조체가 아닙니다. 따라서 정의했던 Vertex Structure도 변해야 합니다. Vertex의 구조가 바뀌었으면 삼각형을 그리기 위해 만든 정점의 버퍼의 값도 바뀌어야 합니다.

    이제 Shader를 조정해야 합니다. 이제 Vertex Shader는 두 개의 Input을 받게 됩니다. Position과 Colour값입니다. 또한 Output의 값도 바뀌어야 합니다. 다음과 같이 Vertex Shader가 변했습니다.

    이제 VS[Vertex Shader]는 두 개의 값을 받고 반환합니다. Position, Colour. 그래서 따로따로 반환하는 대신에 모든 값을 갖는 VSOut이라는 구조체를 만든 것입니다. 기존에는 input으로 받은 값을 직접 반환했지만 이제는 구조체를 반환하기로 합니다. VSOut 구조체 안에 위치와 색에 대한 정보를 집어 넣습니다.

    이제 이 구조체에 싣은 값들은 Rasterizer에서 Interpolated 될 것입니다. 또, Pixel Shader에 Input으로 나타날 것입니다. 따라서, 이전에 Pixel Shader에서 아무것도 Input으로 받지 않았지만 이제는 뭔가를 받아야 합니다.

    한 가지 또 실험을 해볼 수 있는 요소는 RGB값을 표현하기 위해서 꼭 Floating Point를 사용해야만 하는가입니다. 당연히 다른 값을 사용해도 됩니다. 하지만 그런 경우엔 파이프 라인의 모든 요소를 그에 걸맞게 조정해야 할 필요가 있습니다. 대표적으로 Input Layout의 Description이 있겠습니다. 

    여기서 한 가지 꼭 알아가야 할 Takeaway가 있습니다.

    Shader의 input으로 float을 명시했으면서 실질적으로 Layout에는 integer 값을 Bind하여 전달하면 나름의 규칙에 따라 해당 값을 변환합니다. 그리고 그것을 어떻게 변환할 지 결정하는 규칙이 바로 저 마지막 Suffix입니다. UNORM의 경우, 꼭 알아두는 것이 좋습니다. 가령, integer로 255을 넘겨주면 Shader 단계에서 이것을 Normalise하여 1.0f로 변환합니다. 마찬가지로 128을 넘겨주면 0.5f의 값으로 변환하게 됩니다. 

    Vertex의 구조를 다른 방식으로 개편할 수 있습니다. 이제는 단순히 위치의 값을 갖는 것이 아니라 색의 값도 갖기 때문에 다음과 같은 구조로 바꿔보는 것도 괜찮겠습니다.

    한 가지 궁금할 수 있는 것은 어떤 요소를 바꾸었음에도 불구하고 Layout에 아무런 변화를 주지 않았다는 점입니다. Vertex의 구성은 바꾸었으나 이것을 byte 단위로 쪼개어 생각해보면 바뀐 것이 없기 때문입니다. 여전히 x다음엔 y, r다음엔 g, b가 옵니다. 그저 보기 좋게 바꾼 것 뿐입니다.

     

     이번에는 삼각형 말고 육각형을 그려보도록 합니다. 육각형을 그릴 수 있는 방법은 다양합니다. 삼각형을 여러 개 이어 붙여 만들 수도 있고 라인을 이용해 그릴 수도 있습니다. 하지만 라인을 이용해서 그리면 내부를 채울 수가 없으니 삼각형을 여럿 이어서 만들어보기로 합니다. 그런데 이 방법을 차용하면 귀찮은 문제가 생깁니다. Hexagon 즉 육각형을 만드는데 우리가 실제 코드에 찍어야 할 Vertex는 무려 12개입니다. 조금 더 편하게 구현하는 방법이 있는데 바로 Index를 사용하여 그리는 것입니다.

    먼저, Index를 사용하여 무언가를 그리기 위해서는 다른 Draw 함수를 사용해야 합니다. 굉장히 직관적인 이름을 갖고 있습니다.

    당연히 다른 Parameters를 요구할 것입니다. Vertex를 사용하여 무언가를 그릴 때, Vertex Buffer를 구성했었습니다. 같은 원리로 Index를 사용하여 무언가를 그리려면 Index Buffer를 구성해야 합니다. Buffer Description도 필요합니다. 똑같이 index buffer를 pipeline 즉 IA Stage에 Bind하면 됩니다.

    이제 이 정점을 놓고 마치 점을 찍는 것처럼 vertex의 index를 사용하여 그림을 그릴 겁니다. 육각형을 그린다고 했습니다. 그리고 반드시 시계방향으로 그림을 그리는 것을 잊지 말아야 합니다.

    이것이 index입니다. Vertices의 index라고 생각하면 얼추 어떤 그림을 그린 것인지 보입니다. 이제 이에 걸맞는 작업을 준비하면 됩니다.

    Vertex Buffer를 구성했던 것과 큰 차이가 없습니다. 천천히 필요한 대로 MSDN을 참조하면 어려울 것이 없습니다.

    정점들을 미리 정리해 두고 필요한대로 index를 사용하면 훨씬 다채로운 도형을 찍을 수가 있습니다.

    이번에는 Viewport에 대한 몇 가지 작업을 해보겠습니다. Viewport에 대한 이론적인 내용은 짚어보았습니다.

    이것이 현재 구성한 Viewport입니다.

    만약 Width와 Height을 반으로 줄이면 다음과 같은 결과를 얻을 수 있습니다.

     

    'Graphics' 카테고리의 다른 글

    [25] - DirectXMath  (0) 2021.07.31
    [24] - Constant Buffer  (0) 2021.07.30
    [22] - Viewport and clip space  (0) 2021.07.23
    [21] - My First Triangle - 2  (0) 2021.07.22
    [20] - My First Triangle - 1  (0) 2021.07.22
Designed by Tistory.