관리 메뉴

Kim's Programming

[다이렉트SDK 예제]DirectX Tutorial2 - Vertices 본문

Programming/DirectX

[다이렉트SDK 예제]DirectX Tutorial2 - Vertices

Programmer. 2016. 4. 15. 00:28

2번째 예제는 Vertex 버퍼에 대한 내용입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//-----------------------------------------------------------------------------
// File: Vertices.cpp
//
// Desc: In this tutorial, we are rendering some Vertices. This introduces the
//       concept of the vertex buffer, a Direct3D object used to store
//       Vertices. Vertices can be defined any way we want by defining a
//       custom structure and a custom FVF (flexible vertex format). In this
//       tutorial, we are using Vertices that are transformed (meaning they
//       are already in 2D window coordinates) and lit (meaning we are not
//       using Direct3D lighting, but are supplying our own colors).
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include <d3d9.h>
#pragma warning( disable : 4996 // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )
 
 
 
 
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D = NULL; // Used to create the D3DDevice        
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device        
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold Vertices
 
// A structure for our custom vertex type
struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw; // The transformed position for the vertex
    DWORD color;        // The vertex color
};
 
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
 
 
 
 
//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )                                //다이렉트 X 초기화
{
    // Create the D3D object.
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;
 
    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 
    // Create the D3DDevice
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }
 
    // Device state would normally be set here
 
    return S_OK;
}
 
 
 
 
//-----------------------------------------------------------------------------
// Name: InitVB()
// Desc: Creates a vertex buffer and fills it with our Vertices. The vertex
//       buffer is basically just a chuck of memory that holds Vertices. After
//       creating it, we must Lock()/Unlock() it to fill it. For indices, D3D
//       also uses index buffers. The special thing about vertex and index
//       buffers is that they can be created in device memory, allowing some
//       cards to process them in hardware, resulting in a dramatic
//       performance gain.
//-----------------------------------------------------------------------------
HRESULT InitVB()
{
    // Initialize three Vertices for rendering a triangle
    CUSTOMVERTEX Vertices[] =
    {
        
        { 0.0f,  40.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x0000000, },
        { 0.0f, 90.0f, 0.5f, 1.0f, 0xff00ffff, },
 
        { 50.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
        { 0.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x00000000, },
 
        { 50.0f,  90.0f, 0.5f, 1.0f, 0x00000000, }, // x, y, z, rhw, color
        { 50.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
        { 100.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 100.0f,  90.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x0000000, },
        { 100.0f, 40.0f, 0.5f, 1.0f, 0xff00ffff, },
 
        { 70.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 70.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
        { 100.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
 
        { 40.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 70.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 70.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 30.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 30.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
        { 60.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
 
        { 0.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 30.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 30.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 30.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 70.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 50.0f, 30.0f, 0.5f, 1.0f, 0x0000000, },
 
        { 30.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 0.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 0.0f, 40.0f, 0.5f, 1.0f, 0x0000000, },
 
        { 70.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 100.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 100.0f, 40.0f, 0.5f, 1.0f, 0x0000000, },
 
    };
 
    // Create the vertex buffer. Here we are allocating enough memory
    // (from the default pool) to hold all our 3 custom Vertices. We also
    // specify the FVF, so the vertex buffer knows what data it contains.
    if( FAILED( g_pd3dDevice->CreateVertexBuffer( 33 * sizeof( CUSTOMVERTEX ),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }
 
    // Now we fill the vertex buffer. To do this, we need to Lock() the VB to
    // gain access to the Vertices. This mechanism is required becuase vertex
    // buffers may be in device memory.
    VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0sizeof( Vertices ), ( void** )&pVertices, ) ) )
        return E_FAIL;
    memcpy( pVertices, Vertices, sizeof( Vertices ) );
    g_pVB->Unlock();
 
    return S_OK;
}
 
 
 
 
//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
    if( g_pVB != NULL )
        g_pVB->Release();
 
    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();
 
    if( g_pD3D != NULL )
        g_pD3D->Release();
}
 
 
 
 
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()        //매 프레임 마다 뿌림
{
    // Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 00255 ), 1.0f, );
 
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        // Draw the triangles in the vertex buffer. This is broken into a few
        // steps. We are passing the Vertices down a "stream", so first we need
        // to specify the source of that stream, which is our vertex buffer. Then
        // we need to let D3D know what vertex shader to use. Full, custom vertex
        // shaders are an advanced topic, but in most cases the vertex shader is
        // just the FVF, so that D3D knows what type of Vertices we are dealing
        // with. Finally, we call DrawPrimitive() which does the actual rendering
        // of our geometry (in this case, just one triangle).
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0sizeof( CUSTOMVERTEX ) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 033 );
 
        // End the scene
        g_pd3dDevice->EndScene();
    }
 
    // Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
 
 
 
 
//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( );
            return 0;
    }
 
    return DefWindowProc( hWnd, msg, wParam, lParam );
}
 
 
 
 
//-----------------------------------------------------------------------------
// Name: wWinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
{
    UNREFERENCED_PARAMETER( hInst );
 
    // Register the window class
    WNDCLASSEX wc =
    {
        sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
        L"D3D Tutorial", NULL
    };
    RegisterClassEx( &wc );
 
    // Create the application's window
    HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 02: Vertices",
                              WS_OVERLAPPEDWINDOW, 100100300300,
                              NULL, NULL, wc.hInstance, NULL );
 
    // Initialize Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
        // Create the vertex buffer
        if( SUCCEEDED( InitVB() ) )
        {
            // Show the window
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );
 
            // Enter the message loop
            MSG msg;
            ZeroMemory( &msg, sizeof( msg ) );
            while( msg.message != WM_QUIT )
            {
                if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                {
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }
                else
                    Render();
            }
        }
    }
 
    UnregisterClass( L"D3D Tutorial", wc.hInstance );
    return 0;
}
 
cs

이전 예제에서 등장한 부분은 제외해서 작성하겠습니다.


이번 예제는 Vertice들을 렌더링 합니다. 이번 예제에서는 버텍스 버퍼의 개념이 등장하며 Direct3D는 Vertices를 보관하기 위해서 사용됩니다. Vertices는 원하는 방향으로 정의할 수도 있고 FVF(Flexible Vertex 형식)으로  정의할 수도 있습니다. Vertices를 사용하여 변형되며(이미 2D윈도우 좌표에 들어가있고) 빛을 비출때 사용합니다.(여기서 빛을 비춘다는 것은 실제 DirectX Lighting과는 의미가 다릅니다.)



- 전역변수


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D = NULL; // Used to create the D3DDevice        
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device        
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold Vertices
 
// A structure for our custom vertex type
struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw; // The transformed position for the vertex
    DWORD color;        // The vertex color
};
 
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
cs


LPDIRECT3DVERTEXBUFFER9 구조체는 Vertices를 보관하기 위한 구조체입니다. 또한 CUSTOMVERTEX 구조체는 사용자 정의 구조체인데 vertex의 형태를 정의하는 구조체 입니다. 구조체 안에는 vertex의 좌표관련 변수들과 색에 대한 변수가 들어가있습니다. 제일 마지막에 있는 정의어는 vertex에 이용할 구조체의 형태가 어떻게 생겼는지 알려주는 정의입니다.



- InitVB()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
HRESULT InitVB()
{
    // Initialize three Vertices for rendering a triangle
    CUSTOMVERTEX Vertices[] =
    {
        
        { 0.0f,  40.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x0000000, },
        { 0.0f, 90.0f, 0.5f, 1.0f, 0xff00ffff, },
 
        { 50.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
        { 0.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x00000000, },
 
        { 50.0f,  90.0f, 0.5f, 1.0f, 0x00000000, }, // x, y, z, rhw, color
        { 50.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
        { 100.0f, 40.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 100.0f,  90.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 50.0f, 90.0f, 0.5f, 1.0f, 0x0000000, },
        { 100.0f, 40.0f, 0.5f, 1.0f, 0xff00ffff, },
 
        { 70.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 70.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
        { 100.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
 
        { 40.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 70.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 70.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 30.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 30.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
        { 60.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
 
        { 0.0f,  40.0f, 0.5f, 1.0f, 0x000050ff, }, // x, y, z, rhw, color
        { 30.0f, 40.0f, 0.5f, 1.0f, 0x00000000, },
        { 30.0f, 10.0f, 0.5f, 1.0f, 0x000050ff, },
 
        { 30.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 70.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 50.0f, 30.0f, 0.5f, 1.0f, 0x0000000, },
 
        { 30.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 0.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 0.0f, 40.0f, 0.5f, 1.0f, 0x0000000, },
 
        { 70.0f, 10.0f, 0.5f, 1.0f, 0xff00ffff, },
        { 100.0f,  10.0f, 0.5f, 1.0f, 0xff00ffff, }, // x, y, z, rhw, color
        { 100.0f, 40.0f, 0.5f, 1.0f, 0x0000000, },
 
    };
 
    // Create the vertex buffer. Here we are allocating enough memory
    // (from the default pool) to hold all our 3 custom Vertices. We also
    // specify the FVF, so the vertex buffer knows what data it contains.
    if( FAILED( g_pd3dDevice->CreateVertexBuffer( 33 * sizeof( CUSTOMVERTEX ),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }
 
    // Now we fill the vertex buffer. To do this, we need to Lock() the VB to
    // gain access to the Vertices. This mechanism is required becuase vertex
    // buffers may be in device memory.
    VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0sizeof( Vertices ), ( void** )&pVertices, ) ) )
        return E_FAIL;
    memcpy( pVertices, Vertices, sizeof( Vertices ) );
    g_pVB->Unlock();
 
    return S_OK;
}
cs



InitVB()함수는 Vertex buffer를 만들어서 그안에 Vertices를 채워 넣는 함수입니다. vertex 버퍼는 기본적으로 Vertice를 보관하는 메모리 덩어리입니다. 만들고 나서는 이걸을 채울때 Lock(), Unlock()을 해야합니다. indice에서는 D3D는 인덱스 버퍼들을 사용합니다. 이런 vetex와 index버퍼들이 그래픽카드 메모리에 만들어 지는 것은 엄청난 성능향상의 결과를 보이게됩니다.


CUSTOMVERTEX Vertices[]는 CUSTOMVERTEX 구조체의 배열을 이용하여 여러 점들을 저장합니다. 이를 이용하여 여러점들을 저장합니다. 원래 이 예제는 삼각형 하나만 출력하도록 3개의 점만 있는데 제가 공부하면서 직접 넣은 거라서 점이 좀 많아 졌습니다.

다음 조건문에서는 CreateVertexBuffer를 이용하여 D3DPOOL_DEFAULT메모리에서 몇 개의 vertices를 넣을 건지 할당할 량을 정합니다. 또한 FVF를 정의 합니다.  그 다음 조건문에서는 vertex buffer를 채우게 됩니다. 여기서는 Lock()이라는 과정을 거치게 되는데 Lock은 말그대로 잠그다. 즉 vertex buffer를 채우는 동안 메모리를 붙잡고있겠다는(독점) 것입니다. 만약 잠그지 않는 다면 원하는 대로 출력이 되지 않을 것입니다. 복사가 끝나고 나면 Unlock()을 통해서 풀어줍니다.

Render()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()        
{
    // Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 00255 ), 1.0f, );
 
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        // Draw the triangles in the vertex buffer. This is broken into a few
        // steps. We are passing the Vertices down a "stream", so first we need
        // to specify the source of that stream, which is our vertex buffer. Then
        // we need to let D3D know what vertex shader to use. Full, custom vertex
        // shaders are an advanced topic, but in most cases the vertex shader is
        // just the FVF, so that D3D knows what type of Vertices we are dealing
        // with. Finally, we call DrawPrimitive() which does the actual rendering
        // of our geometry (in this case, just one triangle).
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0sizeof( CUSTOMVERTEX ) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 033 );
 
        // End the scene
        g_pd3dDevice->EndScene();
    }
 
    // Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
cs

실제로 그리는 부분입니다. SetStreamSource를 통해서 vertex buffer의 출처가 g_pVB라고 밝히며 SetFVF함수를 통해서 Vertices의 형식을 밝히게 됩니다. 마지막으로 DrawPrimitive함수를 이용하여 어떤방식으로 몇개를 그릴것인지를 명시하게됩니다. DrawPrimitive에 있는 첫 파라메터는 다음과 같은 것들을 이용할 수 있습니다.

1
2
3
4
5
6
7
8
9
typedef enum _D3DPRIMITIVETYPE {
    D3DPT_POINTLIST             = 1,
    D3DPT_LINELIST              = 2,
    D3DPT_LINESTRIP             = 3,
    D3DPT_TRIANGLELIST          = 4,
    D3DPT_TRIANGLESTRIP         = 5,
    D3DPT_TRIANGLEFAN           = 6,
    D3DPT_FORCE_DWORD           = 0x7fffffff/* force 32-bit size enum */
} D3DPRIMITIVETYPE;
cs

다음에 들어가는 열거형의 각각의 작동 방식은 다음 주소를 통해서 알 수 있습니다. primitives(MSDN). 2번째 파라메터는 시작 vertex를 지정하며 3번째 쨰 파라메터는 vertex의 갯수를 지정해줍니다. 3번째 파라메터가 부족하면 에러가 나게됩니다.


이상으로 Vertices에 대한 포스팅을 마쳤습니다. 제일 위의 소스의 결과는 다음과 같이 나오게됩니다.


다음 포스팅에서는 Matrices에 대해서 포스팅하겠습니다.