Thinking Different




Tutorial 12 - 글꼴 엔진



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



텍스트를 화면에 쓰는 것은 모든 응용 프로그램에서 매우 중요한 기능입니다. DirectX 11에서 텍스트를 렌더링하려면 2D 이미지를 렌더링하는 방법을 먼저 알아야합니다. 이전 튜토리얼에서 다룬 내용을 기반으로 본 듀토리얼이 작성되었습니다.


가장 먼저해야 할 일은 자신 만의 글꼴 이미지입니다. 여기에 필요한 글꼴들을 작성하여 1024x16 DDS 텍스처에 넣었습니다.



보이는 것처럼 하나의 텍스쳐 파일 안에 필요한 기본 글자가 모두 들어있습니다. 이것을 이용해 필요한 글자의 텍스쳐를 인덱스를 이용해 그려내는 단순한 글꼴 엔진을 만들 수 있습니다. DirectX 11에서는 2개의 삼각형으로 이루어진 사각형을 만들고 그 사각형에 원하는 글자의 텍스쳐를 그리면 됩니다. 따라서 문장이 있으면 문장에 있는 각 문자별로 사각형을 만들어 주고 그 위에 글자를 그립니다. 이렇게 모든 사각형이 화면에 그려지면 문장이 완성됩니다. 이 과정은 지난 튜토리얼의 2D 이미지를 화면에 그리는 것과 같습니다.


 텍스쳐에 인덱스를 먹일 때 필요한 것은 각 글자가 실제로 텍스쳐의 어느 부분에 있는가 하는 정보가 담긴 텍스트 파일입니다. 이 텍스트 파일은 글꼴 엔진에게 재빨리 그릴 수 있는 픽셀 위치를 잡아줍니다. 아래는 앞의 폰트 텍스쳐의 인덱스입니다.


이 파일의 포맷은 다음과 같습니다.


[글자의 아스키코드] [글자] [왼쪽 U좌표] [오른쪽 U좌표] [글자의 픽셀 너비]


fontdata.txt


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
32   0.0        0.0         0
33 ! 0.0        0.000976563 1
34 " 0.00195313 0.00488281  3
35 # 0.00585938 0.0136719   8
36 $ 0.0146484  0.0195313   5
37 % 0.0205078  0.0302734   10
38 & 0.03125    0.0390625   8
39 ' 0.0400391  0.0410156   1
40 ( 0.0419922  0.0449219   3
41 ) 0.0458984  0.0488281   3
42 * 0.0498047  0.0546875   5
43 + 0.0556641  0.0625      7
44 , 0.0634766  0.0644531   1
45 - 0.0654297  0.0683594   3
46 . 0.0693359  0.0703125   1
47 / 0.0712891  0.0751953   4
48 0 0.0761719  0.0820313   6
49 1 0.0830078  0.0859375   3
50 2 0.0869141  0.0927734   6
51 3 0.09375    0.0996094   6
52 4 0.100586   0.106445    6
53 5 0.107422   0.113281    6
54 6 0.114258   0.120117    6
55 7 0.121094   0.126953    6
56 8 0.12793    0.133789    6
57 9 0.134766   0.140625    6
58 : 0.141602   0.142578    1
59 ; 0.143555   0.144531    1
60 < 0.145508   0.151367    6
61 = 0.152344   0.15918     7
62 > 0.160156   0.166016    6
63 ? 0.166992   0.171875    5
64 @ 0.172852   0.18457     12
65 A 0.185547   0.194336    9
66 B 0.195313   0.202148    7
67 C 0.203125   0.209961    7
68 D 0.210938   0.217773    7
69 E 0.21875    0.225586    7
70 F 0.226563   0.232422    6
71 G 0.233398   0.241211    8
72 H 0.242188   0.249023    7
73 I 0.25       0.250977    1
74 J 0.251953   0.256836    5
75 K 0.257813   0.265625    8
76 L 0.266602   0.272461    6
77 M 0.273438   0.282227    9
78 N 0.283203   0.290039    7
79 O 0.291016   0.298828    8
80 P 0.299805   0.306641    7
81 Q 0.307617   0.31543     8
82 R 0.316406   0.323242    7
83 S 0.324219   0.331055    7
84 T 0.332031   0.338867    7
85 U 0.339844   0.34668     7
86 V 0.347656   0.356445    9
87 W 0.357422   0.370117    13
88 X 0.371094   0.37793     7
89 Y 0.378906   0.385742    7
90 Z 0.386719   0.393555    7
91 [ 0.394531   0.396484    2
92 \ 0.397461   0.401367    4
93 ] 0.402344   0.404297    2
94 ^ 0.405273   0.410156    5
95 _ 0.411133   0.417969    7
96 ` 0.418945   0.420898    2
97 a 0.421875   0.426758    5
98 b 0.427734   0.432617    5
99 c 0.433594   0.438477    5
100 d 0.439453  0.444336    5
101 e 0.445313  0.450195    5
102 f 0.451172  0.455078    4
103 g 0.456055  0.460938    5
104 h 0.461914  0.466797    5
105 i 0.467773  0.46875     1
106 j 0.469727  0.472656    3
107 k 0.473633  0.478516    5
108 l 0.479492  0.480469    1
109 m 0.481445  0.490234    9
110 n 0.491211  0.496094    5
111 o 0.49707   0.501953    5
112 p 0.50293   0.507813    5
113 q 0.508789  0.513672    5
114 r 0.514648  0.517578    3
115 s 0.518555  0.523438    5
116 t 0.524414  0.527344    3
117 u 0.52832   0.533203    5
118 v 0.53418   0.539063    5
119 w 0.540039  0.548828    9
120 x 0.549805  0.554688    5
121 y 0.555664  0.560547    5
122 z 0.561523  0.566406    5
123 { 0.567383  0.570313    3
124 | 0.571289  0.572266    1
125 } 0.573242  0.576172    3
126 ~ 0.577148  0.583984    7
cs





인덱스 파일과 텍스쳐 파일이 있으면 글꼴 엔진을 만들 수 있습니다. 만약 자신만의 인덱스 파일을 만들고 싶다면 각 글자가 공백으로 분리되도록 해야 하고 빈공간이 없다면 스스로 TU,TV좌표를 만다는 비트맵 파서를 작할 수 있어야 합니다. (역자주: 비트맵 폰트를 쉽게 만들 수 있도록 해 주는 BMFont라는 툴이 있습니다.) 


 많은 유저들이 서로 다른 해상도에서 프로그램을 실행한다는 사실을 기억하십시오. 한 가지 크기의 글꼴은 모든 해상도에서 깔끔하게 읽히지 않습니다. 따라서 3~4개의 다른 글꼴 사이즈를 각 해상도에 적용하여 문제를 해결할 수 있습니다.




프레임워크




이 튜토리얼에서는 TextClass, FontClass, FontShader 클래스가 추가되었습니다. FontShaderClass는 글씨를 그리는 셰이더에 해당하는 클래스인데, 그 과정은 이전 튜토리얼의 비트맵 이미지를 그리는 데 사용했던 TextureShaderClass와 비슷합니다. FontClass는 글꼴 데이터를 가지고 문자열을 그리기 위한 정점 버퍼들을 만들어냅니다. TextClass는 화면에 그릴 문자열들에 해당하는 정점/인덱스 버퍼들을 가지고 있습니다. 이 버퍼들은 FontClass에 의해 성성되고 FontShaderClass를 통해 화면에 그려집니다.




우선 FontClass부터 보겠습니다. 이 클래스는 글꼴 텍스쳐, 텍스트 파일로부터 읽어들인 글꼴 데이터, 그리고 이 데이터로부터 정점 버퍼를 생성하는 함수를 포합합니다. 각각의 글꼴 데이터를 가진 정점 버퍼는 이 클래스가 아니라 TextClass에서 관리합니다.



Fontclass.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
#pragma once
 
class TextureClass;
 
class FontClass
{
private:
    struct FontType
    {
        float left, right;
        int size;
    };
 
    struct VertexType
    {
        XMFLOAT3 position;
        XMFLOAT2 texture;
    };
 
public:
    FontClass();
    FontClass(const FontClass&);
    ~FontClass();
 
    bool Initialize(ID3D11Device*char*, WCHAR*);
    void Shutdown();
 
    ID3D11ShaderResourceView* GetTexture();
 
    void BuildVertexArray(void*char*floatfloat);
 
private:
    bool LoadFontData(char*);
    void ReleaseFontData();
    bool LoadTexture(ID3D11Device*, WCHAR*);
    void ReleaseTexture();
 
private:
    FontType* m_Font = nullptr;
    TextureClass* m_Texture = nullptr;
};
cs



Fontclass.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
#include "stdafx.h"
#include "TextureClass.h"
#include "FontClass.h"
 
#include <fstream>
using namespace std;
 
 
FontClass::FontClass()
{
}
 
FontClass::FontClass(const FontClass& other)
{
}
 
FontClass::~FontClass()
{
}
 
bool FontClass::Initialize(ID3D11Device* device, char* fontFilename, WCHAR* textureFilename)
{
    // Load in the text file containing the font data.
    if (!LoadFontData(fontFilename))
    {
        return false;
    }
 
    // Load the texture that has the font characters on it.
    return LoadTexture(device, textureFilename);
}
 
 
void FontClass::Shutdown()
{
    // Release the font texture.
    ReleaseTexture();
 
    // Release the font data.
    ReleaseFontData();
}
 
 
bool FontClass::LoadFontData(char* filename)
{
    ifstream fin;
    int i;
    char temp;
 
 
    // Create the font spacing buffer.
    m_Font = new FontType[95];
    if (!m_Font)
    {
        return false;
    }
 
    // Read in the font size and spacing between chars.
    fin.open(filename);
    if (fin.fail())
    {
        return false;
    }
 
    // Read in the 95 used ascii characters for text.
    for (i = 0; i<95; i++)
    {
        fin.get(temp);
        while (temp != ' ')
        {
            fin.get(temp);
        }
        fin.get(temp);
        while (temp != ' ')
        {
            fin.get(temp);
        }
 
        fin >> m_Font[i].left;
        fin >> m_Font[i].right;
        fin >> m_Font[i].size;
    }
 
    // Close the file.
    fin.close();
 
    return true;
}
 
 
void FontClass::ReleaseFontData()
{
    // Release the font data array.
    if (m_Font)
    {
        delete[] m_Font;
        m_Font = 0;
    }
}
 
 
bool FontClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
    // Create the texture object.
    m_Texture = new TextureClass;
    if (!m_Texture)
    {
        return false;
    }
 
    // Initialize the texture object.
    return m_Texture->Initialize(device, filename);
}
 
 
void FontClass::ReleaseTexture()
{
    // Release the texture object.
    if (m_Texture)
    {
        m_Texture->Shutdown();
        delete m_Texture;
        m_Texture = 0;
    }
}
 
 
ID3D11ShaderResourceView* FontClass::GetTexture()
{
    return m_Texture->GetTexture();
}
 
 
void FontClass::BuildVertexArray(void* vertices, char* sentence, float drawX, float drawY)
{
    VertexType* vertexPtr;
    int numLetters, index, i, letter;
 
 
    // Coerce the input vertices into a VertexType structure.
    vertexPtr = (VertexType*)vertices;
 
    // Get the number of letters in the sentence.
    numLetters = (int)strlen(sentence);
 
    // Initialize the index to the vertex array.
    index = 0;
 
    // Draw each letter onto a quad.
    for (i = 0; i<numLetters; i++)
    {
        letter = ((int)sentence[i]) - 32;
 
        // If the letter is a space then just move over three pixels.
        if (letter == 0)
        {
            drawX = drawX + 3.0f;
        }
        else
        {
            // First triangle in quad.
            vertexPtr[index].position = XMFLOAT3(drawX, drawY, 0.0f);  // Top left.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].left, 0.0f);
            index++;
 
            vertexPtr[index].position = XMFLOAT3((drawX + m_Font[letter].size), (drawY - 16), 0.0f);  // Bottom right.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].right, 1.0f);
            index++;
 
            vertexPtr[index].position = XMFLOAT3(drawX, (drawY - 16), 0.0f);  // Bottom left.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].left, 1.0f);
            index++;
 
            // Second triangle in quad.
            vertexPtr[index].position = XMFLOAT3(drawX, drawY, 0.0f);  // Top left.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].left, 0.0f);
            index++;
 
            vertexPtr[index].position = XMFLOAT3(drawX + m_Font[letter].size, drawY, 0.0f);  // Top right.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].right, 0.0f);
            index++;
 
            vertexPtr[index].position = XMFLOAT3((drawX + m_Font[letter].size), (drawY - 16), 0.0f);  // Bottom right.
            vertexPtr[index].texture = XMFLOAT2(m_Font[letter].right, 1.0f);
            index++;
 
            // Update the x location for drawing by the size of the letter and one pixel.
            drawX = drawX + m_Font[letter].size + 1.0f;
        }
    }
}
cs






글꼴 정점 셰이더는 이전 튜토리얼의 2D 이미지를 그리는데 사용했던 텍스쳐 정점 셰이더의 수정된 버전입니다. 수정된 것이라고는 셰이더 이름뿐입니다


Font.vs


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
////////////////////////////////////////////////////////////////////////////////
// Filename: font.vs
////////////////////////////////////////////////////////////////////////////////
 
 
/////////////
// GLOBALS //
/////////////
cbuffer PerFrameBuffer
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projectionMatrix;
};
 
 
//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
};
 
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
};
 
 
////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType FontVertexShader(VertexInputType input)
{
    PixelInputType output;
    
 
    // 적절한 행렬 계산을 위해 위치 벡터를 4 단위로 변경합니다.
    input.position.w = 1.0f;
 
    // 월드, 뷰 및 투영 행렬에 대한 정점의 위치를 ​​계산합니다.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // 픽셀 쉐이더의 텍스처 좌표를 저장한다.
    output.tex = input.tex;
    
    return output;
}
cs


Font.ps


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
////////////////////////////////////////////////////////////////////////////////
// Filename: font.ps
////////////////////////////////////////////////////////////////////////////////
 
 
/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;
 
cbuffer PixelBuffer
{
    float4 pixelColor;
};
 
 
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
};
 
 
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 FontPixelShader(PixelInputType input) : SV_TARGET
{
    float4 color;
    
    
    // 이 위치에서 텍스처 픽셀을 샘플링합니다.
    color = shaderTexture.Sample(SampleType, input.tex);
    
    // 텍스처의 색상이 검은 색이면이 픽셀을 투명으로 처리합니다.
    if(color.r == 0.0f)
    {
        color.a = 0.0f;
    }
    
    // 텍스처의 검은 색이 아닌 경우 글꼴의 픽셀이므로 글꼴 픽셀 색상을 사용하여 그립니다.
    else
    {
        color.rgb = pixelColor.rgb;
        color.a = 1.0f;
    }
 
    return color;
}
cs




FontShaderClass는 이전 듀토리얼의 TextureShaderClass가 글꼴 렌더링을 위해 몇가지 코드가 변경되었습니다.


Fontshaderclass.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
#pragma once
 
class FontShaderClass
{
private:
    struct ConstantBufferType
    {
        XMMATRIX world;
        XMMATRIX view;
        XMMATRIX projection;
    };
 
    struct PixelBufferType
    {
        XMFLOAT4 pixelColor;
    };
 
public:
    FontShaderClass();
    FontShaderClass(const FontShaderClass&);
    ~FontShaderClass();
 
    bool Initialize(ID3D11Device*, HWND);
    void Shutdown();
    bool Render(ID3D11DeviceContext*int, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, XMFLOAT4);
 
private:
    bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
    void ShutdownShader();
    void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
 
    bool SetShaderParameters(ID3D11DeviceContext*, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, XMFLOAT4);
    void RenderShader(ID3D11DeviceContext*int);
 
private:
    ID3D11VertexShader* m_vertexShader = nullptr;
    ID3D11PixelShader* m_pixelShader = nullptr;
    ID3D11InputLayout* m_layout = nullptr;
    ID3D11Buffer* m_constantBuffer = nullptr;
    ID3D11SamplerState* m_sampleState = nullptr;
    ID3D11Buffer* m_pixelBuffer = nullptr;
};
cs



Fontshaderclass.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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#include "stdafx.h"
#include "FontShaderClass.h"
 
 
FontShaderClass::FontShaderClass()
{
}
 
 
FontShaderClass::FontShaderClass(const FontShaderClass& other)
{
}
 
 
FontShaderClass::~FontShaderClass()
{
}
 
 
bool FontShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
    // 정점 및 픽셀 쉐이더를 초기화합니다.
    return InitializeShader(device, hwnd, L"../Dx11Demo_12/font.vs", L"../Dx11Demo_12/font.ps");
}
 
 
void FontShaderClass::Shutdown()
{
    // 버텍스 및 픽셀 쉐이더와 관련된 객체를 종료합니다.
    ShutdownShader();
}
 
 
bool FontShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix,
    XMMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, XMFLOAT4 pixelColor)
{
    // 렌더링에 사용할 셰이더 매개 변수를 설정합니다.
    if (!SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, pixelColor))
    {
        return false;
    }
 
    // 설정된 버퍼를 셰이더로 렌더링한다.
    RenderShader(deviceContext, indexCount);
 
    return true;
}
 
 
bool FontShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
    HRESULT result;
    ID3D10Blob* errorMessage = nullptr;
 
    // 버텍스 쉐이더 코드를 컴파일한다.
    ID3D10Blob* vertexShaderBuffer = nullptr;
    result = D3DCompileFromFile(vsFilename, NULLNULL"FontVertexShader""vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0,
    &vertexShaderBuffer, &errorMessage);
    if (FAILED(result))
    {
        // 셰이더 컴파일 실패시 오류메시지를 출력합니다.
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
        }
        // 컴파일 오류가 아니라면 셰이더 파일을 찾을 수 없는 경우입니다.
        else
        {
            MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
        }
 
        return false;
    }
 
    // 픽셀 쉐이더 코드를 컴파일한다.
    ID3D10Blob* pixelShaderBuffer = nullptr;
    result = D3DCompileFromFile(psFilename, NULLNULL"FontPixelShader""ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0,
 &pixelShaderBuffer, &errorMessage);
    if (FAILED(result))
    {
        // 셰이더 컴파일 실패시 오류메시지를 출력합니다.
        if (errorMessage)
        {
            OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
        }
        // 컴파일 오류가 아니라면 셰이더 파일을 찾을 수 없는 경우입니다.
        else
        {
            MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
        }
 
        return false;
    }
 
    // 버퍼로부터 정점 셰이더를 생성한다.
    result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL,
 &m_vertexShader);
    if (FAILED(result))
    {
        return false;
    }
 
    // 버퍼에서 픽셀 쉐이더를 생성합니다.
    result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL,
 &m_pixelShader);
    if (FAILED(result))
    {
        return false;
    }
 
    // 정점 입력 레이아웃 구조체를 설정합니다.
    // 이 설정은 ModelClass와 셰이더의 VertexType 구조와 일치해야합니다.
    D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
    polygonLayout[0].SemanticName = "POSITION";
    polygonLayout[0].SemanticIndex = 0;
    polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    polygonLayout[0].InputSlot = 0;
    polygonLayout[0].AlignedByteOffset = 0;
    polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[0].InstanceDataStepRate = 0;
 
    polygonLayout[1].SemanticName = "TEXCOORD";
    polygonLayout[1].SemanticIndex = 0;
    polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
    polygonLayout[1].InputSlot = 0;
    polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
    polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    polygonLayout[1].InstanceDataStepRate = 0;
 
    // 레이아웃의 요소 수를 가져옵니다.
    UINT numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
 
    // 정점 입력 레이아웃을 만듭니다.
    result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(),
        vertexShaderBuffer->GetBufferSize(), &m_layout);
    if (FAILED(result))
    {
        return false;
    }
 
    // 더 이상 사용되지 않는 정점 셰이더 퍼버와 픽셀 셰이더 버퍼를 해제합니다.
    vertexShaderBuffer->Release();
    vertexShaderBuffer = 0;
 
    pixelShaderBuffer->Release();
    pixelShaderBuffer = 0;
 
    // 정점 셰이더에 있는 행렬 상수 버퍼의 구조체를 작성합니다.
    D3D11_BUFFER_DESC constantBufferDesc;
    constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    constantBufferDesc.ByteWidth = sizeof(ConstantBufferType);
    constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    constantBufferDesc.MiscFlags = 0;
    constantBufferDesc.StructureByteStride = 0;
 
    // 상수 버퍼 포인터를 만들어 이 클래스에서 정점 셰이더 상수 버퍼에 접근할 수 있게 합니다.
    result = device->CreateBuffer(&constantBufferDesc, NULL&m_constantBuffer);
    if (FAILED(result))
    {
        return false;
    }
 
    // 텍스처 샘플러 상태 구조체를 생성 및 설정합니다.
    D3D11_SAMPLER_DESC samplerDesc;
    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.MipLODBias = 0.0f;
    samplerDesc.MaxAnisotropy = 1;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
    samplerDesc.BorderColor[0= 0;
    samplerDesc.BorderColor[1= 0;
    samplerDesc.BorderColor[2= 0;
    samplerDesc.BorderColor[3= 0;
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
 
    // 텍스처 샘플러 상태를 만듭니다.
    result = device->CreateSamplerState(&samplerDesc, &m_sampleState);
    if (FAILED(result))
    {
        return false;
    }
 
    // Setup the description of the dynamic pixel constant buffer that is in the pixel shader.
    D3D11_BUFFER_DESC pixelBufferDesc;
    pixelBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    pixelBufferDesc.ByteWidth = sizeof(PixelBufferType);
    pixelBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    pixelBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    pixelBufferDesc.MiscFlags = 0;
    pixelBufferDesc.StructureByteStride = 0;
 
    // Create the pixel constant buffer pointer so we can access the pixel shader constant buffer from within this class.
    result = device->CreateBuffer(&pixelBufferDesc, NULL&m_pixelBuffer);
    if(FAILED(result))
    {
        return false;
    }
 
    return true;
}
 
 
void FontShaderClass::ShutdownShader()
{
    // Release the pixel constant buffer.
    if(m_pixelBuffer)
    {
        m_pixelBuffer->Release();
        m_pixelBuffer = 0;
    }
 
    // 샘플러 상태를 해제한다.
    if (m_sampleState)
    {
        m_sampleState->Release();
        m_sampleState = 0;
    }
 
    // 행렬 상수 버퍼를 해제합니다.
    if (m_constantBuffer)
    {
        m_constantBuffer->Release();
        m_constantBuffer = 0;
    }
 
    // 레이아웃을 해제합니다.
    if (m_layout)
    {
        m_layout->Release();
        m_layout = 0;
    }
 
    // 픽셀 쉐이더를 해제합니다.
    if (m_pixelShader)
    {
        m_pixelShader->Release();
        m_pixelShader = 0;
    }
 
    // 버텍스 쉐이더를 해제합니다.
    if (m_vertexShader)
    {
        m_vertexShader->Release();
        m_vertexShader = 0;
    }
}
 
 
void FontShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
    // 에러 메시지를 출력창에 표시합니다.
    OutputDebugStringA(reinterpret_cast<const char*>(errorMessage->GetBufferPointer()));
 
    // 에러 메세지를 반환합니다.
    errorMessage->Release();
    errorMessage = 0;
 
    // 컴파일 에러가 있음을 팝업 메세지로 알려줍니다.
    MessageBox(hwnd, L"Error compiling shader.", shaderFilename, MB_OK);
}
 
 
bool FontShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX viewMatrix,
    XMMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, XMFLOAT4 pixelColor)
{
    // 상수 버퍼의 내용을 쓸 수 있도록 잠급니다.
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    if (FAILED(deviceContext->Map(m_constantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0&mappedResource)))
    {
        return false;
    }
 
    // 상수 버퍼의 데이터에 대한 포인터를 가져옵니다.
    ConstantBufferType* dataPtr = (ConstantBufferType*)mappedResource.pData;
 
    // 행렬을 transpose하여 셰이더에서 사용할 수 있게 합니다
    worldMatrix = XMMatrixTranspose(worldMatrix);
    viewMatrix = XMMatrixTranspose(viewMatrix);
    projectionMatrix = XMMatrixTranspose(projectionMatrix);
 
    // 상수 버퍼에 행렬을 복사합니다.
    dataPtr->world = worldMatrix;
    dataPtr->view = viewMatrix;
    dataPtr->projection = projectionMatrix;
 
    // 상수 버퍼의 잠금을 풉니다.
    deviceContext->Unmap(m_constantBuffer, 0);
 
    // 정점 셰이더에서의 상수 버퍼의 위치를 설정합니다.
    unsigned int bufferNumber = 0;
 
    // 마지막으로 정점 셰이더의 상수 버퍼를 바뀐 값으로 바꿉니다.
    deviceContext->VSSetConstantBuffers(bufferNumber, 1&m_constantBuffer);
 
    // 픽셀 셰이더에서 셰이더 텍스처 리소스를 설정합니다.
    deviceContext->PSSetShaderResources(01&texture);
 
    // Lock the pixel constant buffer so it can be written to.
    if(FAILED(deviceContext->Map(m_pixelBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0&mappedResource)))
    {
        return false;
    }
 
    // Get a pointer to the data in the pixel constant buffer.
    PixelBufferType* dataPtr2 = (PixelBufferType*)mappedResource.pData;
 
    // Copy the pixel color into the pixel constant buffer.
    dataPtr2->pixelColor = pixelColor;
 
    // Unlock the pixel constant buffer.
    deviceContext->Unmap(m_pixelBuffer, 0);
 
    // Set the position of the pixel constant buffer in the pixel shader.
    bufferNumber = 0;
 
    // Now set the pixel constant buffer in the pixel shader with the updated value.
    deviceContext->PSSetConstantBuffers(bufferNumber, 1&m_pixelBuffer);
    return true;
}
 
 
void FontShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
    // 정점 입력 레이아웃을 설정합니다.
    deviceContext->IASetInputLayout(m_layout);
 
    // 삼각형을 그릴 정점 셰이더와 픽셀 셰이더를 설정합니다.
    deviceContext->VSSetShader(m_vertexShader, NULL0);
    deviceContext->PSSetShader(m_pixelShader, NULL0);
 
    // 픽셀 쉐이더에서 샘플러 상태를 설정합니다.
    deviceContext->PSSetSamplers(01&m_sampleState);
 
    // 삼각형을 그립니다.
    deviceContext->DrawIndexed(indexCount, 00);
}
cs




 TextClass 클래스는 어플리케이션이 필요로 하는 모든 2D 문자열을 그리는 일을 담당합니다. FontClass와 FontShaderClass를 사용하여 2D 문자열을 화면에 그립니다.


Textclass.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
#pragma once
 
class FontClass;
class FontShaderClass;
 
class TextClass : public AlignedAllocationPolicy<16>
{
private:
    struct SentenceType
    {
        ID3D11Buffer *vertexBuffer, *indexBuffer;
        int vertexCount, indexCount, maxLength;
        float red, green, blue;
    };
 
    struct VertexType
    {
        XMFLOAT3 position;
        XMFLOAT2 texture;
    };
 
public:
    TextClass();
    TextClass(const TextClass&);
    ~TextClass();
 
    bool Initialize(ID3D11Device*, ID3D11DeviceContext*, HWND, intint, XMMATRIX);
    void Shutdown();
    bool Render(ID3D11DeviceContext*, XMMATRIX, XMMATRIX);
 
private:
    bool InitializeSentence(SentenceType**int, ID3D11Device*);
    bool UpdateSentence(SentenceType*char*intintfloatfloatfloat, ID3D11DeviceContext*);
    void ReleaseSentence(SentenceType**);
    bool RenderSentence(ID3D11DeviceContext*, SentenceType*, XMMATRIX, XMMATRIX);
 
private:
    FontClass* m_Font = nullptr;
    FontShaderClass* m_FontShader = nullptr;
    int m_screenWidth = 0;
    int m_screenHeight = 0;
    XMMATRIX m_baseViewMatrix;
    SentenceType* m_sentence1;
    SentenceType* m_sentence2;
};
cs



Textclass.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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#include "stdafx.h"
#include "FontClass.h"
#include "FontShaderClass.h"
#include "TextClass.h"
 
 
TextClass::TextClass()
{
}
 
TextClass::TextClass(const TextClass& other)
{
}
 
TextClass::~TextClass()
{
}
 
bool TextClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, HWND hwnd, int screenWidth,
 int screenHeight, XMMATRIX baseViewMatrix)
{
    bool result;
 
 
    // Store the screen width and height.
    m_screenWidth = screenWidth;
    m_screenHeight = screenHeight;
 
    // Store the base view matrix.
    m_baseViewMatrix = baseViewMatrix;
 
    // Create the font object.
    m_Font = new FontClass;
    if (!m_Font)
    {
        return false;
    }
 
    // Initialize the font object.
    result = m_Font->Initialize(device, "../Dx11Demo_12/data/fontdata.txt", L"../Dx11Demo_12/data/font.dds");
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize the font object.", L"Error", MB_OK);
        return false;
    }
 
    // Create the font shader object.
    m_FontShader = new FontShaderClass;
    if (!m_FontShader)
    {
        return false;
    }
 
    // Initialize the font shader object.
    result = m_FontShader->Initialize(device, hwnd);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize the font shader object.", L"Error", MB_OK);
        return false;
    }
 
    // Initialize the first sentence.
    result = InitializeSentence(&m_sentence1, 16, device);
    if (!result)
    {
        return false;
    }
 
    // Now update the sentence vertex buffer with the new string information.
    result = UpdateSentence(m_sentence1, "Hello"1001001.0f, 1.0f, 1.0f, deviceContext);
    if (!result)
    {
        return false;
    }
 
    // Initialize the first sentence.
    result = InitializeSentence(&m_sentence2, 16, device);
    if (!result)
    {
        return false;
    }
 
    // Now update the sentence vertex buffer with the new string information.
    result = UpdateSentence(m_sentence2, "Goodbye"1002001.0f, 1.0f, 0.0f, deviceContext);
    if (!result)
    {
        return false;
    }
 
    return true;
}
 
 
void TextClass::Shutdown()
{
    // Release the first sentence.
    ReleaseSentence(&m_sentence1);
 
    // Release the second sentence.
    ReleaseSentence(&m_sentence2);
 
    // Release the font shader object.
    if (m_FontShader)
    {
        m_FontShader->Shutdown();
        delete m_FontShader;
        m_FontShader = 0;
    }
 
    // Release the font object.
    if (m_Font)
    {
        m_Font->Shutdown();
        delete m_Font;
        m_Font = 0;
    }
 
    return;
}
 
 
bool TextClass::Render(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX orthoMatrix)
{
    bool result;
 
 
    // Draw the first sentence.
    result = RenderSentence(deviceContext, m_sentence1, worldMatrix, orthoMatrix);
    if (!result)
    {
        return false;
    }
 
    // Draw the second sentence.
    result = RenderSentence(deviceContext, m_sentence2, worldMatrix, orthoMatrix);
    if (!result)
    {
        return false;
    }
 
    return true;
}
 
 
bool TextClass::InitializeSentence(SentenceType** sentence, int maxLength, ID3D11Device* device)
{
    VertexType* vertices;
    unsigned long* indices;
    D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D11_SUBRESOURCE_DATA vertexData, indexData;
    HRESULT result;
    int i;
 
 
    // Create a new sentence object.
    *sentence = new SentenceType;
    if (!*sentence)
    {
        return false;
    }
 
    // Initialize the sentence buffers to null.
    (*sentence)->vertexBuffer = 0;
    (*sentence)->indexBuffer = 0;
 
    // Set the maximum length of the sentence.
    (*sentence)->maxLength = maxLength;
 
    // Set the number of vertices in the vertex array.
    (*sentence)->vertexCount = 6 * maxLength;
 
    // Set the number of indexes in the index array.
    (*sentence)->indexCount = (*sentence)->vertexCount;
 
    // Create the vertex array.
    vertices = new VertexType[(*sentence)->vertexCount];
    if (!vertices)
    {
        return false;
    }
 
    // Create the index array.
    indices = new unsigned long[(*sentence)->indexCount];
    if (!indices)
    {
        return false;
    }
 
    // Initialize vertex array to zeros at first.
    memset(vertices, 0, (sizeof(VertexType) * (*sentence)->vertexCount));
 
    // Initialize the index array.
    for (i = 0; i<(*sentence)->indexCount; i++)
    {
        indices[i] = i;
    }
 
    // Set up the description of the dynamic vertex buffer.
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * (*sentence)->vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;
 
    // Give the subresource structure a pointer to the vertex data.
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;
 
    // Create the vertex buffer.
    result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &(*sentence)->vertexBuffer);
    if (FAILED(result))
    {
        return false;
    }
 
    // Set up the description of the static index buffer.
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long* (*sentence)->indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;
 
    // Give the subresource structure a pointer to the index data.
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;
 
    // Create the index buffer.
    result = device->CreateBuffer(&indexBufferDesc, &indexData, &(*sentence)->indexBuffer);
    if (FAILED(result))
    {
        return false;
    }
 
    // Release the vertex array as it is no longer needed.
    delete[] vertices;
    vertices = 0;
 
    // Release the index array as it is no longer needed.
    delete[] indices;
    indices = 0;
 
    return true;
}
 
 
bool TextClass::UpdateSentence(SentenceType* sentence, char* text, int positionX, int positionY, float red, float green,
 float blue, ID3D11DeviceContext* deviceContext)
{
    int numLetters;
    VertexType* vertices;
    float drawX, drawY;
    HRESULT result;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    VertexType* verticesPtr;
 
 
    // Store the color of the sentence.
    sentence->red = red;
    sentence->green = green;
    sentence->blue = blue;
 
    // Get the number of letters in the sentence.
    numLetters = (int)strlen(text);
 
    // Check for possible buffer overflow.
    if (numLetters > sentence->maxLength)
    {
        return false;
    }
 
    // Create the vertex array.
    vertices = new VertexType[sentence->vertexCount];
    if (!vertices)
    {
        return false;
    }
 
    // Initialize vertex array to zeros at first.
    memset(vertices, 0, (sizeof(VertexType) * sentence->vertexCount));
 
    // Calculate the X and Y pixel position on the screen to start drawing to.
    drawX = (float)(((m_screenWidth / 2* -1+ positionX);
    drawY = (float)((m_screenHeight / 2- positionY);
 
    // Use the font class to build the vertex array from the sentence text and sentence draw location.
    m_Font->BuildVertexArray((void*)vertices, text, drawX, drawY);
 
    // Lock the vertex buffer so it can be written to.
    result = deviceContext->Map(sentence->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0&mappedResource);
    if (FAILED(result))
    {
        return false;
    }
 
    // Get a pointer to the data in the vertex buffer.
    verticesPtr = (VertexType*)mappedResource.pData;
 
    // Copy the data into the vertex buffer.
    memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * sentence->vertexCount));
 
    // Unlock the vertex buffer.
    deviceContext->Unmap(sentence->vertexBuffer, 0);
 
    // Release the vertex array as it is no longer needed.
    delete[] vertices;
    vertices = 0;
 
    return true;
}
 
 
void TextClass::ReleaseSentence(SentenceType** sentence)
{
    if (*sentence)
    {
        // Release the sentence vertex buffer.
        if ((*sentence)->vertexBuffer)
        {
            (*sentence)->vertexBuffer->Release();
            (*sentence)->vertexBuffer = 0;
        }
 
        // Release the sentence index buffer.
        if ((*sentence)->indexBuffer)
        {
            (*sentence)->indexBuffer->Release();
            (*sentence)->indexBuffer = 0;
        }
 
        // Release the sentence.
        delete *sentence;
        *sentence = 0;
    }
 
    return;
}
 
 
bool TextClass::RenderSentence(ID3D11DeviceContext* deviceContext, SentenceType* sentence, XMMATRIX worldMatrix,
    XMMATRIX orthoMatrix)
{
    unsigned int stride, offset;
    XMFLOAT4 pixelColor;
    bool result;
 
 
    // Set vertex buffer stride and offset.
    stride = sizeof(VertexType);
    offset = 0;
 
    // Set the vertex buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetVertexBuffers(01&sentence->vertexBuffer, &stride, &offset);
 
    // Set the index buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetIndexBuffer(sentence->indexBuffer, DXGI_FORMAT_R32_UINT, 0);
 
    // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
 
    // Create a pixel color vector with the input sentence color.
    pixelColor = XMFLOAT4(sentence->red, sentence->green, sentence->blue, 1.0f);
 
    // Render the text using the font shader.
    result = m_FontShader->Render(deviceContext, sentence->indexCount, worldMatrix, m_baseViewMatrix, orthoMatrix,
 m_Font->GetTexture(), pixelColor);
    if (!result)
    {
        false;
    }
 
    return true;
}
cs






이번 튜토리얼에서는 블렌딩 상태를 통합하기 위해 D3DClass 클래스도 수정되었습니다. 블렌딩(Blendig)은 글자가 3D 물체들을 배경으로 조화롭게 그려지도록 합니다. 만약 블렌딩을 켜지 않는다면 텍스트 뒤에 검은 삼각형이 있는 것을 보게 될 것입니다. 반대로 블렌딩이 켜져 있다면 오로지 글자와 관련된 픽셀들만 화면에 나타나고 나머지 부분의 삼각형은 투명 처리됩니다. 여기서는 그에 관해 더 자세히 다루지 않고 튜토리얼이 적당히 돌아가는 정도의 블렌딩을 사용할 것입니다.


D3dclass.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
#pragma once
 
class D3DClass : public AlignedAllocationPolicy<16>
{
public:
    D3DClass();
    D3DClass(const D3DClass&);
    ~D3DClass();
 
    bool Initialize(intintbool, HWND, boolfloatfloat);
    void Shutdown();
 
    void BeginScene(floatfloatfloatfloat);
    void EndScene();
 
    ID3D11Device* GetDevice();
    ID3D11DeviceContext* GetDeviceContext();
 
    void GetProjectionMatrix(XMMATRIX&);
    void GetWorldMatrix(XMMATRIX&);
    void GetOrthoMatrix(XMMATRIX&);
 
    void GetVideoCardInfo(char*int&);
 
    void TurnZBufferOn();
    void TurnZBufferOff();
 
    void TurnOnAlphaBlending();
    void TurnOffAlphaBlending();
private:
    bool m_vsync_enabled = false;
    int m_videoCardMemory = 0;
    char m_videoCardDescription[128= { 0, };
    IDXGISwapChain* m_swapChain = nullptr;
    ID3D11Device* m_device = nullptr;
    ID3D11DeviceContext* m_deviceContext = nullptr;
    ID3D11RenderTargetView* m_renderTargetView = nullptr;
    ID3D11Texture2D* m_depthStencilBuffer = nullptr;
    ID3D11DepthStencilState* m_depthStencilState = nullptr;
    ID3D11DepthStencilView* m_depthStencilView = nullptr;
    ID3D11RasterizerState* m_rasterState = nullptr;
    XMMATRIX m_projectionMatrix;
    XMMATRIX m_worldMatrix;
    XMMATRIX m_orthoMatrix;
    ID3D11DepthStencilState* m_depthDisabledStencilState = nullptr;
    ID3D11BlendState* m_alphaEnableBlendingState = nullptr;
    ID3D11BlendState* m_alphaDisableBlendingState = nullptr;
};
cs



D3dclass.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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
#include "stdafx.h"
#include "d3dclass.h"
 
 
D3DClass::D3DClass()
{
}
 
 
D3DClass::D3DClass(const D3DClass& other)
{
}
 
 
D3DClass::~D3DClass()
{
}
 
 
bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullscreen,
    float screenDepth, float screenNear)
{
    // 수직동기화 상태를 저장합니다
    m_vsync_enabled = vsync;
 
    // DirectX 그래픽 인터페이스 팩토리를 생성합니다
    IDXGIFactory* factory = nullptr;
    if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory)))
    {
        return false;
    }
 
    // 팩토리 객체를 사용하여 첫번째 그래픽 카드 인터페이스 어뎁터를 생성합니다
    IDXGIAdapter* adapter = nullptr;
    if (FAILED(factory->EnumAdapters(0&adapter)))
    {
        return false;
    }
 
    // 출력(모니터)에 대한 첫번째 어뎁터를 지정합니다.
    IDXGIOutput* adapterOutput = nullptr;
    if (FAILED(adapter->EnumOutputs(0&adapterOutput)))
    {
        return false;
    }
 
    // 출력 (모니터)에 대한 DXGI_FORMAT_R8G8B8A8_UNORM 표시 형식에 맞는 모드 수를 가져옵니다
    unsigned int numModes = 0;
    if (FAILED(adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL)))
    {
        return false;
    }
 
    // 가능한 모든 모니터와 그래픽카드 조합을 저장할 리스트를 생성합니다
    DXGI_MODE_DESC* displayModeList = new DXGI_MODE_DESC[numModes];
    if (!displayModeList)
    {
        return false;
    }
 
    // 이제 디스플레이 모드에 대한 리스트를 채웁니다
    if (FAILED(adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, 
displayModeList)))
    {
        return false;
    }
 
    // 이제 모든 디스플레이 모드에 대해 화면 너비/높이에 맞는 디스플레이 모드를 찾습니다.
    // 적합한 것을 찾으면 모니터의 새로고침 비율의 분모와 분자 값을 저장합니다.
    unsigned int numerator = 0;
    unsigned int denominator = 0;
    for (unsigned int i = 0; i<numModes; i++)
    {
        if (displayModeList[i].Width == (unsigned int)screenWidth)
        {
            if (displayModeList[i].Height == (unsigned int)screenHeight)
            {
                numerator = displayModeList[i].RefreshRate.Numerator;
                denominator = displayModeList[i].RefreshRate.Denominator;
            }
        }
    }
 
    // 비디오카드의 구조체를 얻습니다
    DXGI_ADAPTER_DESC adapterDesc;
    if (FAILED(adapter->GetDesc(&adapterDesc)))
    {
        return false;
    }
 
    // 비디오카드 메모리 용량 단위를 메가바이트 단위로 저장합니다
    m_videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);
 
    // 비디오카드의 이름을 저장합니다
    size_t stringLength = 0;
    if (wcstombs_s(&stringLength, m_videoCardDescription, 128, adapterDesc.Description, 128!= 0)
    {
        return false;
    }
 
    // 디스플레이 모드 리스트를 해제합니다
    delete[] displayModeList;
    displayModeList = 0;
 
    // 출력 어뎁터를 해제합니다
    adapterOutput->Release();
    adapterOutput = 0;
 
    // 어뎁터를 해제합니다
    adapter->Release();
    adapter = 0;
 
    // 팩토리 객체를 해제합니다
    factory->Release();
    factory = 0;
 
    // 스왑체인 구조체를 초기화합니다
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
 
    // 백버퍼를 1개만 사용하도록 지정합니다
    swapChainDesc.BufferCount = 1;
 
    // 백버퍼의 넓이와 높이를 지정합니다
    swapChainDesc.BufferDesc.Width = screenWidth;
    swapChainDesc.BufferDesc.Height = screenHeight;
 
    // 32bit 서페이스를 설정합니다
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 
    // 백버퍼의 새로고침 비율을 설정합니다
    if (m_vsync_enabled)
    {
        swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
        swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
    }
    else
    {
        swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
        swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    }
 
    // 백버퍼의 사용용도를 지정합니다
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 
    // 랜더링에 사용될 윈도우 핸들을 지정합니다
    swapChainDesc.OutputWindow = hwnd;
 
    // 멀티샘플링을 끕니다
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
 
    // 창모드 or 풀스크린 모드를 설정합니다
    if (fullscreen)
    {
        swapChainDesc.Windowed = false;
    }
    else
    {
        swapChainDesc.Windowed = true;
    }
 
    // 스캔 라인 순서 및 크기를 지정하지 않음으로 설정합니다.
    swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
 
    // 출력된 다음 백버퍼를 비우도록 지정합니다
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
 
    // 추가 옵션 플래그를 사용하지 않습니다
    swapChainDesc.Flags = 0;
 
    // 피처레벨을 DirectX 11 로 설정합니다
    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
 
    // 스왑 체인, Direct3D 장치 및 Direct3D 장치 컨텍스트를 만듭니다.
    if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL0&featureLevel, 1,
        D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL&m_deviceContext)))
    {
        return false;
    }
 
    // 백버퍼 포인터를 얻어옵니다
    ID3D11Texture2D* backBufferPtr = nullptr;
    if (FAILED(m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr)))
    {
        return false;
    }
 
    // 백 버퍼 포인터로 렌더 타겟 뷰를 생성한다.
    if (FAILED(m_device->CreateRenderTargetView(backBufferPtr, NULL&m_renderTargetView)))
    {
        return false;
    }
 
    // 백버퍼 포인터를 해제합니다
    backBufferPtr->Release();
    backBufferPtr = 0;
 
    // 깊이 버퍼 구조체를 초기화합니다
    D3D11_TEXTURE2D_DESC depthBufferDesc;
    ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));
 
    // 깊이 버퍼 구조체를 작성합니다
    depthBufferDesc.Width = screenWidth;
    depthBufferDesc.Height = screenHeight;
    depthBufferDesc.MipLevels = 1;
    depthBufferDesc.ArraySize = 1;
    depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthBufferDesc.SampleDesc.Count = 1;
    depthBufferDesc.SampleDesc.Quality = 0;
    depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthBufferDesc.CPUAccessFlags = 0;
    depthBufferDesc.MiscFlags = 0;
 
    // 설정된 깊이버퍼 구조체를 사용하여 깊이 버퍼 텍스쳐를 생성합니다
    if (FAILED(m_device->CreateTexture2D(&depthBufferDesc, NULL&m_depthStencilBuffer)))
    {
        return false;
    }
 
    // 스텐실 상태 구조체를 초기화합니다
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
 
    // 스텐실 상태 구조체를 작성합니다
    depthStencilDesc.DepthEnable = true;
    depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
 
    depthStencilDesc.StencilEnable = true;
    depthStencilDesc.StencilReadMask = 0xFF;
    depthStencilDesc.StencilWriteMask = 0xFF;
 
    // 픽셀 정면의 스텐실 설정입니다
    depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
    depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
 
    // 픽셀 뒷면의 스텐실 설정입니다
    depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
    depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
 
    // 깊이 스텐실 상태를 생성합니다
    if (FAILED(m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState)))
    {
        return false;
    }
 
    // 깊이 스텐실 상태를 설정합니다
    m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);
 
    // 깊이 스텐실 뷰의 구조체를 초기화합니다
    D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
    ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
 
    // 깊이 스텐실 뷰 구조체를 설정합니다
    depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    depthStencilViewDesc.Texture2D.MipSlice = 0;
 
    // 깊이 스텐실 뷰를 생성합니다
    if (FAILED(m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView)))
    {
        return false;
    }
 
    // 렌더링 대상 뷰와 깊이 스텐실 버퍼를 출력 렌더 파이프 라인에 바인딩합니다
    m_deviceContext->OMSetRenderTargets(1&m_renderTargetView, m_depthStencilView);
 
    // 그려지는 폴리곤과 방법을 결정할 래스터 구조체를 설정합니다
    D3D11_RASTERIZER_DESC rasterDesc;
    rasterDesc.AntialiasedLineEnable = false;
    rasterDesc.CullMode = D3D11_CULL_BACK;
    rasterDesc.DepthBias = 0;
    rasterDesc.DepthBiasClamp = 0.0f;
    rasterDesc.DepthClipEnable = true;
    rasterDesc.FillMode = D3D11_FILL_SOLID;
    rasterDesc.FrontCounterClockwise = false;
    rasterDesc.MultisampleEnable = false;
    rasterDesc.ScissorEnable = false;
    rasterDesc.SlopeScaledDepthBias = 0.0f;
 
    // 방금 작성한 구조체에서 래스터 라이저 상태를 만듭니다
    if (FAILED(m_device->CreateRasterizerState(&rasterDesc, &m_rasterState)))
    {
        return false;
    }
 
    // 이제 래스터 라이저 상태를 설정합니다
    m_deviceContext->RSSetState(m_rasterState);
 
    // 렌더링을 위해 뷰포트를 설정합니다
    D3D11_VIEWPORT viewport;
    viewport.Width = (float)screenWidth;
    viewport.Height = (float)screenHeight;
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;
 
    // 뷰포트를 생성합니다
    m_deviceContext->RSSetViewports(1&viewport);
 
    // 투영 행렬을 설정합니다
    float fieldOfView = XM_PI / 4.0f;
    float screenAspect = (float)screenWidth / (float)screenHeight;
 
    // 3D 렌더링을위한 투영 행렬을 만듭니다
    m_projectionMatrix = XMMatrixPerspectiveFovLH(fieldOfView, screenAspect, screenNear, screenDepth);
 
    // 세계 행렬을 항등 행렬로 초기화합니다
    m_worldMatrix = XMMatrixIdentity();
 
    // 2D 렌더링을위한 직교 투영 행렬을 만듭니다
    m_orthoMatrix = XMMatrixOrthographicLH((float)screenWidth, (float)screenHeight, screenNear, screenDepth);
 
    // 이제 2D 렌더링을위한 Z 버퍼를 끄는 두 번째 깊이 스텐실 상태를 만듭니다. 유일한 차이점은
    // DepthEnable을 false로 설정하면 다른 모든 매개 변수는 다른 깊이 스텐실 상태와 동일합니다.
    D3D11_DEPTH_STENCIL_DESC depthDisabledStencilDesc;
    ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));
 
    depthDisabledStencilDesc.DepthEnable = false;
    depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
    depthDisabledStencilDesc.StencilEnable = true;
    depthDisabledStencilDesc.StencilReadMask = 0xFF;
    depthDisabledStencilDesc.StencilWriteMask = 0xFF;
    depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
    depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
    depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
 
    // 장치를 사용하여 상태를 만듭니다.
    if (FAILED (m_device-> CreateDepthStencilState (& depthDisabledStencilDesc, & m_depthDisabledStencilState)))
    {
        return false;
    }
 
    // Clear the blend state description.
    D3D11_BLEND_DESC blendStateDescription;
    ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
 
    // Create an alpha enabled blend state description.
    blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
    blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
    blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
    blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
    blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;
 
    // Create the blend state using the description.
    if(FAILED(m_device->CreateBlendState(&blendStateDescription, &m_alphaEnableBlendingState)))
    {
        return false;
    }
 
    // Modify the description to create an alpha disabled blend state description.
    blendStateDescription.RenderTarget[0].BlendEnable = FALSE;
 
    // Create the blend state using the description.
    if(FAILED(m_device->CreateBlendState(&blendStateDescription, &m_alphaDisableBlendingState)))
    {
        return false;
    }
 
    return true;
}
 
 
void D3DClass::Shutdown()
{
    // 종료 전 윈도우 모드로 설정하지 않으면 스왑 체인을 해제 할 때 예외가 발생합니다.
    if (m_swapChain)
    {
        m_swapChain->SetFullscreenState(falseNULL);
    }
 
    if(m_alphaEnableBlendingState)
    {
        m_alphaEnableBlendingState->Release();
        m_alphaEnableBlendingState = 0;
    }
 
    if(m_alphaDisableBlendingState)
    {
        m_alphaDisableBlendingState->Release();
        m_alphaDisableBlendingState = 0;
    }
 
    if (m_rasterState)
    {
        m_rasterState->Release();
        m_rasterState = 0;
    }
 
    if (m_depthStencilView)
    {
        m_depthStencilView->Release();
        m_depthStencilView = 0;
    }
 
    if (m_depthDisabledStencilState)
    {
        m_depthDisabledStencilState-> Release ();
        m_depthDisabledStencilState = 0;
    }
 
    if (m_depthStencilState)
    {
        m_depthStencilState->Release();
        m_depthStencilState = 0;
    }
 
    if (m_depthStencilBuffer)
    {
        m_depthStencilBuffer->Release();
        m_depthStencilBuffer = 0;
    }
 
    if (m_renderTargetView)
    {
        m_renderTargetView->Release();
        m_renderTargetView = 0;
    }
 
    if (m_deviceContext)
    {
        m_deviceContext->Release();
        m_deviceContext = 0;
    }
 
    if (m_device)
    {
        m_device->Release();
        m_device = 0;
    }
 
    if (m_swapChain)
    {
        m_swapChain->Release();
        m_swapChain = 0;
    }
}
 
 
void D3DClass::BeginScene(float red, float green, float blue, float alpha)
{
    // 버퍼를 지울 색을 설정합니다
    float color[4= { red, green, blue, alpha };
 
    // 백버퍼를 지웁니다
    m_deviceContext->ClearRenderTargetView(m_renderTargetView, color);
 
    // 깊이 버퍼를 지웁니다
    m_deviceContext->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
}
 
 
void D3DClass::EndScene()
{
    // 렌더링이 완료되었으므로 화면에 백 버퍼를 표시합니다.
    if (m_vsync_enabled)
    {
        // 화면 새로 고침 비율을 고정합니다.
        m_swapChain->Present(10);
    }
    else
    {
        // 가능한 빠르게 출력합니다
        m_swapChain->Present(00);
    }
}
 
 
ID3D11Device* D3DClass::GetDevice()
{
    return m_device;
}
 
 
ID3D11DeviceContext* D3DClass::GetDeviceContext()
{
    return m_deviceContext;
}
 
 
void D3DClass::GetProjectionMatrix(XMMATRIX& projectionMatrix)
{
    projectionMatrix = m_projectionMatrix;
}
 
 
void D3DClass::GetWorldMatrix(XMMATRIX& worldMatrix)
{
    worldMatrix = m_worldMatrix;
}
 
 
void D3DClass::GetOrthoMatrix(XMMATRIX& orthoMatrix)
{
    orthoMatrix = m_orthoMatrix;
}
 
 
void D3DClass::GetVideoCardInfo(char* cardName, int& memory)
{
    strcpy_s(cardName, 128, m_videoCardDescription);
    memory = m_videoCardMemory;
}
 
void D3DClass::TurnZBufferOn()
{
    m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);
}
 
 
void D3DClass::TurnZBufferOff()
{
    m_deviceContext->OMSetDepthStencilState(m_depthDisabledStencilState, 1);
}
 
void D3DClass::TurnOnAlphaBlending()
{
    // Setup the blend factor.
    float blendFactor[4= { 0.0f, 0.0f, 0.0f, 0.0f };
    
    // Turn on the alpha blending.
    m_deviceContext->OMSetBlendState(m_alphaEnableBlendingState, blendFactor, 0xffffffff);
}
 
 
void D3DClass::TurnOffAlphaBlending()
{
    // Setup the blend factor.
    float blendFactor[4= { 0.0f, 0.0f, 0.0f, 0.0f };
    
    // Turn off the alpha blending.
    m_deviceContext->OMSetBlendState(m_alphaDisableBlendingState, blendFactor, 0xffffffff);
}
cs



Graphicsclass.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
#pragma once
 
/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;
 
 
class D3DClass;
class CameraClass;
class TextClass;
 
class GraphicsClass
{
public:
    GraphicsClass();
    GraphicsClass(const GraphicsClass&);
    ~GraphicsClass();
 
    bool Initialize(intint, HWND);
    void Shutdown();
    void Frame();
    bool Render();
 
private:
    D3DClass* m_Direct3D = nullptr;
    CameraClass* m_Camera = nullptr;
    TextClass* m_Text = nullptr;
};
cs



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
#include "stdafx.h"
#include "d3dclass.h"
#include "cameraclass.h"
#include "textclass.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;
    }
 
    // 카메라 포지션 설정
    XMMATRIX baseViewMatrix;
    m_Camera->SetPosition(0.0f, 0.0f, -1.0f);
    m_Camera->Render();
    m_Camera->GetViewMatrix(baseViewMatrix);
 
    // m_Text 객체 생성
    m_Text = new TextClass;
    if(!m_Text)
    {
        return false;
    }
 
    // m_Text 객체 초기화
    if (!m_Text->Initialize(m_Direct3D->GetDevice(), m_Direct3D->GetDeviceContext(), hwnd, screenWidth, screenHeight,
 baseViewMatrix))
    {
        MessageBox(hwnd, L"Could not initialize the text object.", L"Error", MB_OK);
        return false;
    }
 
    return true;
}
 
 
void GraphicsClass::Shutdown()
{
    // m_Text 객체 반환
    if (m_Text)
    {
        m_Text->Shutdown();
        delete m_Text;
        m_Text = 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;
    }
}
 
 
void GraphicsClass::Frame()
{
    // Set the position of the camera.
    m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
}
 
 
bool GraphicsClass::Render()
{
    // 씬을 그리기 위해 버퍼를 지웁니다
    m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
 
    // 카메라의 위치에 따라 뷰 행렬을 생성합니다
    m_Camera->Render();
 
    // 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다
    XMMATRIX worldMatrix, viewMatrix, projectionMatrix, orthoMatrix;
    
    m_Camera->GetViewMatrix(viewMatrix);
    m_Direct3D->GetWorldMatrix(worldMatrix);
    m_Direct3D->GetProjectionMatrix(projectionMatrix);
    m_Direct3D->GetOrthoMatrix(orthoMatrix);
 
    // 모든 2D 렌더링을 시작하려면 Z 버퍼를 끕니다.
    m_Direct3D->TurnZBufferOff();
 
    // Turn on the alpha blending before rendering the text.
    m_Direct3D->TurnOnAlphaBlending();
 
    // Render the text strings.
    if(!m_Text->Render(m_Direct3D->GetDeviceContext(), worldMatrix, orthoMatrix))
    {
        return false;
    }
 
    // Turn off alpha blending after rendering the text.
    m_Direct3D->TurnOffAlphaBlending();
 
    // 모든 2D 렌더링이 완료되었으므로 Z 버퍼를 다시 켜십시오.
    m_Direct3D->TurnZBufferOn();
 
    // 버퍼의 내용을 화면에 출력합니다
    m_Direct3D->EndScene();
 
    return true;
}
cs




출력 화면




마치면서


이제 화면의 원하는 위치에 색상을 입힌 텍스트를 그릴 수 있게 되었습니다. 



연습문제


1. 프로그램을 다시 컴파일하여 "Hello"라는 글자가 100, 100 위치에 나타나고 "Goodbye" 글자가 아래에 생기는지 확인하십시오. 


2. 문장의 색상, 위치, 내용을 바꿔 보십시오. 


3. 세 번째 문장 구조체를 만들어 화면에 그려 보십시오. 


4. GraphicsClass::Render 함수에서 블렌딩과 관련된 호출을 주석 처리하고 m_D3D->BeginScene(0.0f, 0.0f, 1.0f, 1.0f); 로 고쳐 보십시오. 이렇게 하면 왜 블렌딩 처리가 필요한지 보여줄 것입니다. 


5. GraphicsClass::Render 함수에서 주석 처리된 블렌딩 호출을 주석 해제하고 m_D3D->BeginScene(0.0f, 0.0f, 1.0f, 1.0f); 로 고쳐서 무엇이 달라졌는지 확인해 보십시오.



소스코드


소스코드 : Dx11Demo_12.zip