Thinking Different




Tutorial 7 - 3D 모델 렌더링



원문 : http://www.rastertek.com/dx11tut07.html



이 듀토리얼에서는 HLSL을 사용하여 DirectX 11에서 3D 모델을 렌더링하는 방법에 대해 설명합니다. 이 듀토리얼은 이전 듀토리얼인 [DirectX11] Tutorial 6 - 조명 의 코드를 기반으로합니다.


우리는 이미 이전 튜토리얼에서 3D 모델을 렌더링했지만, 단순한 2D 삼각형으로 구성되어 별로 재미있지 않았습니다. 이제 기초에 대한 부분을 학습하였기 때문에 좀 더 복잡한 모델을 렌더링하기 위해 앞으로 나아갈 것입니다. 이번에는 육면체(큐브)를 렌더링 할것입니다. 좀 더 복잡한 모델을 렌더링하는 방법에 대해 알아보기 전에 먼저 모델 형식에 대해 알아 보겠습니다.


사용자가 3D 모델을 만들 수 있는 툴은 많이 있습니다. Maya와 3D Studio Max는 가장 널리 사용되는 3D 모델링 프로그램 중 두 가지입니다. 기능이 적은 다른 많은 도구가 있지만 기본적인 3D 모델링의 기본적인 것들은 모두 사용할 수 있습니다.


어떤 도구를 사용하든 관계없이 모든 모델을 다양한 파일 포멧 형식으로 저장할 수 있습니다. 제가 추천해드리는 것은 여러분만의 모델 포맷을 만들고 툴에서 만들어낸 포맷을 여러분의 포맷으로 바꿔주는 파서를 만드는 것을 추천합니다. 그 이유는 사용하는 3D 모델링 패키지가 시간이 지남에 따라 변경 될 수 있으며 모델 형식도 변경되기 때문입니다. 또한 하나 이상의 3D 모델링 패키지를 사용하고 있으므로 여러 형식으로 처리 할 수 ​​있습니다. 따라서 자신의 형식을 가지고 자신의 형식으로 변환하면 코드를 변경할 필요가 없습니다. 파서 프로그램을 변경하여 사용자가 직접 포맷을 변경해야합니다. 또한 대부분의 3D 모델링 패키지들은 그 프로그램에게는 필요하겠지만 여러분의 프로그램에는 전혀 필요가 없는 수많은 쓰레기값들을 담고 있습니다.


여러분만의 포맷을 만드는 것이 좋은 가장 큰 이유는 바로 여러분이 필요한 모든 값을 갖고 있고 (여러분에게) 사용하기 쉽다는 것입니다. 또한 애니메이션이나, 정적인 것이나 그외에 것들을 각기 저장하는 여러 포맷을 만드는 것을 생각해 볼 수 있을 것입니다.


제가 앞으로 보여드릴 포맷은 정말로 기본적인 것만 담고 있습니다. 우선 그 모델의 정점들을 연결하는 선을 담고 있습니다. 각 선분들은 위치벡터(x, y, z)와 텍스쳐 좌표(tu, tv), 그리고 법선 벡터(nx, ny, nz)를 가지는 정점 포맷과 일치합니다. 이 포맷은 또한 가장 위에 정점의 개수가 있어서 첫번째 라인을 읽고 데이터를 읽기 위한 준비로 구조체들을 위한 메모리들을 미리 할당할 수 있습니다. 또한 세 개의 선분이 삼각형을 만들며, 각 삼각형의 정점들은 시계 방향으로 배열되어 있어야 합니다. 아래에 앞으로 렌더링에 사용할 육면체의 모델이 있습니다.




Cube.txt


Vertex Count: 36


Data:


-1.0  1.0 -1.0 0.0 0.0  0.0  0.0 -1.0

 1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0

-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0

-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0

 1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0

 1.0 -1.0 -1.0 1.0 1.0  0.0  0.0 -1.0

 1.0  1.0 -1.0 0.0 0.0  1.0  0.0  0.0

 1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0

 1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0

 1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0

 1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0

 1.0 -1.0  1.0 1.0 1.0  1.0  0.0  0.0

 1.0  1.0  1.0 0.0 0.0  0.0  0.0  1.0

-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0

 1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0

 1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0

-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0

-1.0 -1.0  1.0 1.0 1.0  0.0  0.0  1.0

-1.0  1.0  1.0 0.0 0.0 -1.0  0.0  0.0

-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0

-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0

-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0

-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0

-1.0 -1.0 -1.0 1.0 1.0 -1.0  0.0  0.0

-1.0  1.0  1.0 0.0 0.0  0.0  1.0  0.0

 1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0

-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0

-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0

 1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0

 1.0  1.0 -1.0 1.0 1.0  0.0  1.0  0.0

-1.0 -1.0 -1.0 0.0 0.0  0.0 -1.0  0.0

 1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0

-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0

-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0

 1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0

 1.0 -1.0  1.0 1.0 1.0  0.0 -1.0  0.0




위 내용에서 볼 수 있듯이 x, y, z, tu, tv, nx, ny, nz로 이루어진 36줄을 볼 수 있습니다. 3줄마다 삼각형이 하나 만들어지므로 12개의 삼각형으로 이루어진 큐브(육면체)를 볼 수 있게 됩니다. 이 포맷은 굉장히 직관적이고 아무런 수정 없이 정점 버퍼에 넣어 그려낼 수 있습니다.


한 가지 더 살펴보아야 할 것은 3D 모델링 프로그램이 어떤 좌표계를 쓰는지, 오른손 좌표계인지 왼손 좌표계인지를 알아야 합니다. DirectX 11에서는 기본값으로 왼손 좌표계를 쓰므로 모델 데이터도 왼손 좌표계에 맞게 되어 있어야 합니다. 그 차이를 계속 주시하고 파서 프로그램이 그런 좌표계를 올바르게 다루는지 확인해야 할 것입니다.




이 튜토리얼에서는 3D 모델을 텍스트 파일에서 불러와 렌더링하기 위해 ModelClass를 약간 변경했습니다.


Modelclass.h


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
#pragma once
 
class TextureClass;
 
class ModelClass : public AlignedAllocationPolicy<16>
{
private:
    struct VertexType
    {
        XMFLOAT3 position;
        XMFLOAT2 texture;
        XMFLOAT3 normal;
    };
 
    struct ModelType
    {
        float x, y, z;
        float tu, tv;
        float nx, ny, nz;
    };
 
public:
    ModelClass();
    ModelClass(const ModelClass&);
    ~ModelClass();
 
    bool Initialize(ID3D11Device*char*, WCHAR*);
    void Shutdown();
    void Render(ID3D11DeviceContext*);
 
    int GetIndexCount();
    ID3D11ShaderResourceView* GetTexture();
 
private:
    bool InitializeBuffers(ID3D11Device*);
    void ShutdownBuffers();
    void RenderBuffers(ID3D11DeviceContext*);
 
    bool LoadTexture(ID3D11Device*, WCHAR*);
    void ReleaseTexture();
 
    bool LoadModel(char*);
    void ReleaseModel();
 
private:
    ID3D11Buffer* m_vertexBuffer = nullptr;
    ID3D11Buffer* m_indexBuffer = nullptr;
    int m_vertexCount = 0;
    int m_indexCount = 0;
    TextureClass* m_Texture = nullptr;
    ModelType* m_model = nullptr;
};
cs



Modelclass.cpp


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
#include "stdafx.h"
#include "TextureClass.h"
#include "modelclass.h"
 
#include <fstream>
using namespace std;
 
 
ModelClass::ModelClass()
{
}
 
 
ModelClass::ModelClass(const ModelClass& other)
{
}
 
 
ModelClass::~ModelClass()
{
}
 
 
bool ModelClass::Initialize(ID3D11Device* device, char* modelFilename, WCHAR* textureFilename)
{
    // 모델 데이터를 로드합니다.
    if(!LoadModel(modelFilename))
    {
        return false;
    }
 
    // 정점 및 인덱스 버퍼를 초기화합니다.
    if (!InitializeBuffers(device))
    {
        return false;
    }
 
    // 이 모델의 텍스처를 로드합니다.
    return LoadTexture(device, textureFilename);
}
 
 
void ModelClass::Shutdown()
{
    // 모델 텍스쳐를 반환합니다.
    ReleaseTexture();
 
    // 버텍스 및 인덱스 버퍼를 종료합니다.
    ShutdownBuffers();
 
    // 모델 데이터 반환
    ReleaseModel();
}
 
 
void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
    // 그리기를 준비하기 위해 그래픽 파이프 라인에 꼭지점과 인덱스 버퍼를 놓습니다.
    RenderBuffers(deviceContext);
}
 
 
int ModelClass::GetIndexCount()
{
    return m_indexCount;
}
 
 
ID3D11ShaderResourceView* ModelClass::GetTexture()
{
    return m_Texture->GetTexture();
}
 
 
bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
    // 정점 배열을 만듭니다.
    VertexType* vertices = new VertexType[m_vertexCount];
    if (!vertices)
    {
        return false;
    }
 
    // 인덱스 배열을 만듭니다.
    unsigned long* indices = new unsigned long[m_indexCount];
    if (!indices)
    {
        return false;
    }
 
    // 정점 배열과 인덱스 배열을 데이터로 읽어옵니다.
    for (int i = 0; i < m_vertexCount; i++)
    {
        vertices[i].position = XMFLOAT3(m_model[i].x, m_model[i].y, m_model[i].z);
        vertices[i].texture = XMFLOAT2(m_model[i].tu, m_model[i].tv);
        vertices[i].normal = XMFLOAT3(m_model[i].nx, m_model[i].ny, m_model[i].nz);
 
        indices[i] = i;
    }
 
    // 정적 정점 버퍼의 구조체를 설정합니다.
    D3D11_BUFFER_DESC vertexBufferDesc;
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;
 
    // subresource 구조에 정점 데이터에 대한 포인터를 제공합니다.
    D3D11_SUBRESOURCE_DATA vertexData;
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;
 
    // 이제 정점 버퍼를 만듭니다.
    if (FAILED(device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer)))
    {
        return false;
    }
 
    // 정적 인덱스 버퍼의 구조체를 설정합니다.
    D3D11_BUFFER_DESC indexBufferDesc;
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long* m_indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;
 
    // 인덱스 데이터를 가리키는 보조 리소스 구조체를 작성합니다.
    D3D11_SUBRESOURCE_DATA indexData;
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;
 
    // 인덱스 버퍼를 생성합니다.
    if (FAILED(device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer)))
    {
        return false;
    }
 
    // 생성되고 값이 할당된 정점 버퍼와 인덱스 버퍼를 해제합니다.
    delete[] vertices;
    vertices = 0;
 
    delete[] indices;
    indices = 0;
 
    return true;
}
 
 
void ModelClass::ShutdownBuffers()
{
    // 인덱스 버퍼를 해제합니다.
    if (m_indexBuffer)
    {
        m_indexBuffer->Release();
        m_indexBuffer = 0;
    }
 
    // 정점 버퍼를 해제합니다.
    if (m_vertexBuffer)
    {
        m_vertexBuffer->Release();
        m_vertexBuffer = 0;
    }
}
 
 
void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
    // 정점 버퍼의 단위와 오프셋을 설정합니다.
    UINT stride = sizeof(VertexType);
    UINT offset = 0;
 
    // 렌더링 할 수 있도록 입력 어셈블러에서 정점 버퍼를 활성으로 설정합니다.
    deviceContext->IASetVertexBuffers(01&m_vertexBuffer, &stride, &offset);
 
    // 렌더링 할 수 있도록 입력 어셈블러에서 인덱스 버퍼를 활성으로 설정합니다.
    deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
 
    // 정점 버퍼로 그릴 기본형을 설정합니다. 여기서는 삼각형으로 설정합니다.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
 
 
bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
    // 텍스처 오브젝트를 생성한다.
    m_Texture = new TextureClass;
    if (!m_Texture)
    {
        return false;
    }
 
    // 텍스처 오브젝트를 초기화한다.
    return m_Texture->Initialize(device, filename);
}
 
 
void ModelClass::ReleaseTexture()
{
    // 텍스처 오브젝트를 릴리즈한다.
    if (m_Texture)
    {
        m_Texture->Shutdown();
        delete m_Texture;
        m_Texture = 0;
    }
}
 
bool ModelClass::LoadModel(char* filename)
{
    // 모델 파일을 엽니다.
    ifstream fin;
    fin.open(filename);
    
    // 파일을 열 수 없으면 종료합니다.
    if(fin.fail())
    {
        return false;
    }
 
    // 버텍스 카운트의 값까지 읽는다.
    char input = 0;
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }
 
    // 버텍스 카운트를 읽는다.
    fin >> m_vertexCount;
 
    // 인덱스의 수를 정점 수와 같게 설정합니다.
    m_indexCount = m_vertexCount;
 
    // 읽어 들인 정점 개수를 사용하여 모델을 만듭니다.
    m_model = new ModelType[m_vertexCount];
    if(!m_model)
    {
        return false;
    }
 
    // 데이터의 시작 부분까지 읽는다.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }
    fin.get(input);
    fin.get(input);
 
    // 버텍스 데이터를 읽습니다.
    for (int i = 0; i < m_vertexCount; i++)
    {
        fin >> m_model[i].x >> m_model[i].y >> m_model[i].z;
        fin >> m_model[i].tu >> m_model[i].tv;
        fin >> m_model[i].nx >> m_model[i].ny >> m_model[i].nz;
    }
 
    // 모델 파일을 닫는다.
    fin.close();
 
    return true;
}
 
 
void ModelClass::ReleaseModel()
{
    if(m_model)
    {
        delete [] m_model;
        m_model = 0;
    }
}
cs




Graphicsclass.h

GraphicsClass의 헤더는 이전 튜토리얼 이후로 변경되지 않았습니다. 여기서는 생략하도록 하겠습니다.



모델 객체를 생성한 후 초기화시에 cube.txt 모델 렌더링 파일을 읽어오도록 하였습니다.


Graphicsclass.cpp


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
#include "stdafx.h"
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "lightclass.h"
#include "lightshaderclass.h"
#include "graphicsclass.h"
 
 
GraphicsClass::GraphicsClass()
{
}
 
 
GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}
 
 
GraphicsClass::~GraphicsClass()
{
}
 
 
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
    // Direct3D 객체 생성
    m_Direct3D = new D3DClass;
    if(!m_Direct3D)
    {
        return false;
    }
 
    // Direct3D 객체 초기화
    if(!m_Direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR))
    {
        MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
        return false;
    }
 
    // m_Camera 객체 생성
    m_Camera = new CameraClass;
    if (!m_Camera)
    {
        return false;
    }
 
    // 카메라 포지션 설정
    m_Camera->SetPosition(0.0f, 0.0f, -5.0f);
 
    // m_Model 객체 생성
    m_Model = new ModelClass;
    if (!m_Model)
    {
        return false;
    }
 
    // m_Model 객체 초기화
    if (!m_Model->Initialize(m_Direct3D->GetDevice(), "../Dx11Demo_07/data/cube.txt", L"../Dx11Demo_07/data/seafloor.dds"))
    {
        MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
        return false;
    }
 
    // m_LightShader 객체 생성
    m_LightShader = new LightShaderClass;
    if (!m_LightShader)
    {
        return false;
    }
 
    // m_LightShader 객체 초기화
    if (!m_LightShader->Initialize(m_Direct3D->GetDevice(), hwnd))
    {
        MessageBox(hwnd, L"Could not initialize the light shader object.", L"Error", MB_OK);
        return false;
    }
 
    // m_Light 객체 생성
    m_Light = new LightClass;
    if(!m_Light)
    {
        return false;
    }
 
    // m_Light 객체 초기화
    m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
    m_Light->SetDirection(0.0f, 0.0f, 1.0f);
 
    return true;
}
 
 
void GraphicsClass::Shutdown()
{
    // m_Light 객체 반환
    if(m_Light)
    {
        delete m_Light;
        m_Light = 0;
    }
 
    // m_LightShader 객체 반환
    if (m_LightShader)
    {
        m_LightShader->Shutdown();
        delete m_LightShader;
        m_LightShader = 0;
    }
 
    // m_Model 객체 반환
    if (m_Model)
    {
        m_Model->Shutdown();
        delete m_Model;
        m_Model = 0;
    }
 
    // m_Camera 객체 반환
    if (m_Camera)
    {
        delete m_Camera;
        m_Camera = 0;
    }
 
    // Direct3D 객체 반환
    if (m_Direct3D)
    {
        m_Direct3D->Shutdown();
        delete m_Direct3D;
        m_Direct3D = 0;
    }
}
 
 
bool GraphicsClass::Frame()
{
    static float rotation = 0.0f;
 
    // 각 프레임의 rotation 변수를 업데이트합니다.
    rotation += (float)XM_PI * 0.01f;
    if(rotation > 360.0f)
    {
        rotation -= 360.0f;
    }
    
    // 그래픽 랜더링 처리
    return Render(rotation);
}
 
 
bool GraphicsClass::Render(float rotation)
{
    // 씬을 그리기 위해 버퍼를 지웁니다
    m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
 
    // 카메라의 위치에 따라 뷰 행렬을 생성합니다
    m_Camera->Render();
 
    // 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다
    XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
    m_Direct3D->GetWorldMatrix(worldMatrix);
    m_Camera->GetViewMatrix(viewMatrix);
    m_Direct3D->GetProjectionMatrix(projectionMatrix);
 
    // 삼각형이 회전 할 수 있도록 회전 값으로 월드 행렬을 회전합니다.
    worldMatrix = XMMatrixRotationY(rotation);
 
    // 모델 버텍스와 인덱스 버퍼를 그래픽 파이프 라인에 배치하여 드로잉을 준비합니다.
    m_Model->Render(m_Direct3D->GetDeviceContext());
 
    // Light 쉐이더를 사용하여 모델을 렌더링합니다.
    if (!m_LightShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, 
projectionMatrix, m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetDiffuseColor()))
    {
        return false;
    }
 
    // 버퍼의 내용을 화면에 출력합니다
    m_Direct3D->EndScene();
 
    return true;
}

cs



출력 화면




마치면서


ModelClass를 조금 바꾸어서 이제 3D 모델들을 불러와서 그릴 수 있게 되었습니다. 여기서 이용된 포맷은 단지 기본적인 객체에 조명을 더한 것이지만, 모델 포맷이 어떻게 동작하는 지 이해하는 데는 좋은 시작일 것입니다.



연습문제


1. 소스를 다시 컴파일하고 프로그램을 실행해 보십시오. seafloore.dds 텍스쳐가 씌워진 육면체가 돌고 있는 것이 보일 것입니다.  esc 버튼을 눌러 나갈 수 있습니다.


2. 괜찮은 3D 모델링 패키지를 찾고(웬만하면 무료인 것을 이용하세요) 그것으로 간단한 모델을 만들고 내보내십시오(export). 그 포맷을 한번 적용해 보시기 바랍니다.


3. 해당 패키지에서 만든 모델 포맷을 여기서 사용한 모델 포맷으로 바꿔주는 파서(parser) 프로그램을 작성해 보십시오. cube.txt 대신 직접 만든 모델 파일을 가지고 프로그램을 실행해 보십시오.



소스코드


소스코드 : Dx11Demo_07.zip