2024년 6월 6일 목요일

DirectX11 Tutorial - DirectWrite

 

저번 코드는 딱 기본 삼각형 하나만 출력하는 프로그램이지만

이번엔 마크처럼 디버깅 정보를 화면에 띄우는 기능을 구현함




즉 글자를 화면에 그릴 것인데 쉬운 방법으로 두가지가 있음

Sprite Font - 폰트 이미지를 매핑하여 해당 글자에 맞는 

부분에 맞춰 텍스쳐를 입히는 방법이고





DirectWrite - Direct 2D에 있는 기능을 가져와서

3D와 함께 사용하며 글자를 그리는 방법이다.


마소 홈페이지에 잘 정리되어 있으므로 참고



첫번째 방법의 문제점은 한글은 쓰기 힘들고 폰트 이미지를

새로 그리든 어딘가에서 구해야 하는 어려움이 있다.


Dwrite는 아직까지 큰 문제점은 없었음

고로 두번째 방법으로 코드를 작성할 것





프로젝트 속성에 들어가서

링커 -> 입력 -> 추가 종속성에 위와 같이

d2d1.lib과 dwrite.lib을 추가해준다.


아니면


헤더 포함시킬때 위쪽에 #pragma로 해주어도 된다.

둘다 같은 의미



그리고 Device를 생성할때 플래그를 하나 변경해주어야 하는데


D3D11_CREATE_DEVICE_BGRA_SUPPORT

Direct3D와 2D를 둘다 쓰려면 필요하다.




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
#include "textclass.h"
 
TextClass::TextClass()
{
    m_d2dFactory = 0;
    m_dwFactory = 0;
    m_defaultFormat = 0;
    m_renderTarget = 0;
    m_defaultBrush = 0;
}
 
TextClass::~TextClass()
{
 
}
 
bool TextClass::Initialize(D3DClass* d3dClass)
{
    HRESULT result;
    IDXGISurface* backBuffer;
    D2D1_RENDER_TARGET_PROPERTIES props;
 
    //D3D의 스왑체인 버퍼를 가져옴
    result = d3dClass->GetSwapChain()->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
    if (FAILED(result))
    {
        return false;
    }
 
    //변수 초기화
    ZeroMemory(&props, sizeof(D2D1_RENDER_TARGET_PROPERTIES));
 
    //Direct2D 렌더 타켓 설정
    props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
    props.dpiX = 0.0f;//0 기본
    props.dpiY = 0.0f;//0 기본
    props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
    props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
    props.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED);
 
    //D2D 팩토리 생성 (싱글 스레드)
    result = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_d2dFactory);
    if (FAILED(result))
    {
        return false;
    }
 
    //렌더 타켓 생성
    result = m_d2dFactory->CreateDxgiSurfaceRenderTarget(
        backBuffer,
        &props,
        &m_renderTarget
    );
 
    if (FAILED(result))
    {
        return false;
    }
 
    //백 버퍼 포인터 해제
    backBuffer->Release();
    backBuffer = 0;
 
    //Dwrite 팩토리 생성
    result = DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,    //해당 팩토리를 공유할지 격리할지 설정
        __uuidof(IDWriteFactory),    //팩토리 인터페이스를 식별하는 GUID
        reinterpret_cast<IUnknown**>(&m_dwFactory)//팩토리에 대한 포인터 주소
    );
 
    if (FAILED(result))
    {
        return false;
    }
 
    //폰트 설정
    result = m_dwFactory->CreateTextFormat(
        L"나눔스퀘어 네오 OTF",    //폰트 이름
        NULL,                    //폰트 컬렉션에 대한 포인터 주소 NULL은 시스템 폰트 컬렉션
        DWRITE_FONT_WEIGHT::DWRITE_FONT_WEIGHT_NORMAL,//폰트 두께
        DWRITE_FONT_STYLE::DWRITE_FONT_STYLE_NORMAL,//폰트 스타일
        DWRITE_FONT_STRETCH::DWRITE_FONT_STRETCH_NORMAL,//폰트 스트레치
        30.0f,                    //폰트 사이즈
        L"ko",                    //지역 이름 ex) KO, EN
        &m_defaultFormat                //텍스트 형식(IDWriteTextFormat)에 대한 포인터 주소를 반환한다
    );
 
    if (FAILED(result))
    {
        return false;
    }
 
    //기본 브러쉬 설정 (빨강)
    result = m_renderTarget->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f)),
        &m_defaultBrush
    );
    if (FAILED(result))
    {
        return false;
    }
 
    //기본 텍스트 정렬 (왼쪽)
    result = m_defaultFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
    if (FAILED(result))
    {
        return false;
    }
 
    return true;
}
 
//텍스트 랜더링 기본 폰트, 컬러
void TextClass::RenderText(CONST WCHAR* ptext, FLOAT x, FLOAT y, FLOAT width, FLOAT height)
{
    m_renderTarget->DrawTextW(
        ptext,
        wcslen(ptext),
        m_defaultFormat,
        D2D1::RectF(x, y, width, height),
        m_defaultBrush,
        D2D1_DRAW_TEXT_OPTIONS_NONE,
        DWRITE_MEASURING_MODE_NATURAL
    );
}
 
//텍스트 랜더링 지정 폰트, 컬러
void TextClass::RenderText(CONST WCHAR* ptext, FLOAT x, FLOAT y, FLOAT width, FLOAT height, IDWriteTextFormat* pformat, ID2D1SolidColorBrush* pbrush)
{
    m_renderTarget->DrawTextW(
        ptext,
        wcslen(ptext),
        pformat,
        D2D1::RectF(x, y, width, height),
        pbrush,
        D2D1_DRAW_TEXT_OPTIONS_NONE,
        DWRITE_MEASURING_MODE_NATURAL
    );
}
 
//텍스트 랜더링 시작 함수
void TextClass::BeginDraw()
{
    m_renderTarget->BeginDraw();
}
 
//텍스트 랜더링 종료 함수
void TextClass::EndDraw()
{
    m_renderTarget->EndDraw();
}
 
void TextClass::Shutdown()
{
    if (m_defaultBrush)
    {
        m_defaultBrush->Release();
        m_defaultBrush = 0;
    }
 
    if (m_defaultFormat)
    {
        m_defaultFormat->Release();
        m_defaultFormat = 0;
    }
 
    if (m_renderTarget)
    {
        m_renderTarget->Release();
        m_renderTarget = 0;
    }
 
    if (m_dwFactory)
    {
        m_dwFactory->Release();
        m_dwFactory = 0;
    }
 
    if (m_d2dFactory)
    {
        m_d2dFactory->Release();
        m_d2dFactory = 0;
    }
 
}
cs


결과



+저번 코드에서 이어서 만들다가

삼각형이랑 글자가 픽셀비율이 이상한 오류가 있었는데 

D3DClass.cpp에서 윈도우 크기를 잘못 전달해서 생긴 문제였음



hwnd에서 바로 창크기를 구해서 구현함


원인이 무엇인지 콘솔로 확인해봤는데

아마 ㅡ ㅁ X 있는 부분인 테두리 때문에 

비율이 안맞아 픽셀이 늘어나는 현상인것으로 보임


SystemClass에서 윈도우 생성할 때 테두리 포함한 크기를

전달 해야 해결이 될듯함

h117562/Tutorial_DirectWrite (github.com)



댓글 없음:

댓글 쓰기

c++ thread.h

 c++에서 쓰레드 돌릴려면 thread.h 헤더를 쓰면 되는데 이 친구는 쓰레드가 아직 실행 중인지, 아니면 강제 종료하거나 하는 함수가 없어서 조금 아쉬운 애다. std::thread 는 로컬 변수로 선언하든 new 동적 할당을 하든 start 함...