드디어 다이렉트X를 시작했다.
처음 세팅하는게 좀 복잡했는데 코드가 이해 안가는건 어쩔수 없는거지만
세팅이 안되면 코딩도 할 수 없으니 문제였다.
30명에 가까운 학생들을 한명한명 다 봐주다보니 진행속도가 너무 더뎠다.
그래도 내일부턴 괜찮아질거 같다.
다이렉트X 와 openGL
openGL
3D 그래픽 카드가 출시 되고 이에 맞춰 하드웨어와 소프트웨어가 발전해왔다.
하지만 당시 하드웨어 규격이 제대로 잡혀있지 않았고,
이 하드웨어를 돌리려면 OS에서 지원을 해주어야 하는데
소프트웨어도 기준이 제대로 잡혀있지 않았다.
이 발전해가는 3D그래픽카드를 사용할 수 있는 api가 필요했는데,
그래픽카드회사에서 직접 제공하는 api는 수준이 낮았다.
그래서 openGL이 나왔다.
기반은 유닉스였으며
이름 그대로 열려있는 그래픽 라이브러리라
프로그래머들이 주도하여 누구나 참여하고 만들어 나갔으며
3D그래픽카드를 이용한 프로그래밍을 선도했다.
하드웨어도 규격이 점점 잡혀갔으며 openGL도 자리를 잡아갔다.
MS의 DirectX
openGL을 본 마이크로소프트는 시장을 선도하기위해 눈치빠르게 참여하였으며
그때 나온것이 윈도우를 기반으로 한 DirectX이다.
워낙 큰 기업이고 주력으로 밀었기 때문에 openGL은 자연스럽게 밀렸다고 한다.
요즘은 안보이지만
옛날에 게임을 깔다보면 DirectX가 깔리는걸 본 적이 있는 분들이 많으실것이다.
게임을 다이렉트x api를 사용하여 인터페이스를 만들었으니 없으면 동작이 되지 않기 때문에 그렇다.
초기에는 하드웨어에 맞춰 소프트웨어가 중구난방으로 개발됐고
이에따라 잦은 버전업을 했기때문에 윈도우에 내장시키질 못했는데
요즘에는 다이렉트x가 윈도우에 내장되어서 나오기 때문에 잘 보이진 않는다.
암튼 초기에는 이 번거로운 잦은 업데이트와
하드웨어를 개발할때 소프트웨어를 신경써야하고
소프트웨어를 개발할때 하드웨어를 신경써야 하는 악순환을 막기위해 DXGI가 나왔는데
다이렉트x와 그래픽카드 사이에 인터페이스를 두는 것이다.
이 인터페이스만 지킨다면 그래픽카드를 맘대로 개발 가능하고
소프트웨어는 이 인터페이스를 기반으로 돌아가게 만들면 된다.
다이렉트X가 업데이트가 된지 오랜 기간이 흘렀음에도 불구하고
오늘날 그래픽의 새로운 기능들이 나오는건 그래픽카드들이 DXGI를 기반으로 새로운 기능들을 지원해주기 때문이라고 한다.
다이렉트 X SDK 설치
Download DirectX SDK from Official Microsoft Download Center
여기에 들어가서 SDK를 통으로 다 받아도 상관없지만
요즘은 다이렉트X가 게임에만 쓰이는것이 아니라 영상쪽에서도 쓰이고 다방면에 쓰이기 때문에 덩치가 커졌다.
우린 게임을 만들기 위해 설치하는 것이기 때문에
요기에 들어가서 다운을 받아준다.(마소 공식 깃허브)
압축을 풀면 무수한 파일들이 나오는데 자신의 비주얼스튜디오 버전,윈도 버전을 선택해서 sln파일을 눌러 들어가준다.
이런식으로 디버그와 릴리즈가 있는데 디버그 - x86 ,x64 / 릴리즈 - x86 ,x64 하나하나 다 선택해서
F7을 눌러 솔루션 빌드를 해준다.(4개 다 해주어야한다)
그러면 압축을 푼 경로에 Bin이라는 폴더가 생기는데 , 그 안에 들어가보면 폴더별로 다 이 파일이 있을것이다.
같은 파일이 디버그 - x86 ,x64 / 릴리즈 - x86 ,x64 하나하나 다있으니 총 4개가 필요하다.
이제 비주얼스튜디오로 프로젝트를 만들어 주어야 하는데,
WINAPI 프로젝트를 만들었던것 처럼 c++ - 윈도우데스크톱 마법사 - 윈도우 응용프로그램 , 빈프로젝트로 만들어준다.
마소에서 받은 파일에서
Inc 폴더와 위에 설명한 DirectXTK.lib 파일 4개를 새로만든 프로젝트 폴더로 옮겨주면 끝이다.
(DirectXTK.lib 파일은 폴더를 4개로 나눠놔야 한다.)
그리고 속성에 들어와 VC++디렉터리에서 구성/플랫폼을 선택해주고 아까 옮긴 DirectXTK.lib파일이 있는 폴더를
구성/플랫폼에 맞게 하나하나 설정해준다.
그리고 구성 - 모든 구성 / 플랫폼 - 모든 플랫폼으로 설정하고
포함 디렉터리에는 아까 옮긴 inc폴더를 지정해주면 끝이다.(헤더파일)
이제 또 디버그 - x86 ,x64 / 릴리즈 - x86 ,x64 돌려가며 하나하나 빌드를 다 해준다.
오류가 안난다면 성공이다.
다이렉트X 시작
#include<Windows.h>
//라이브러리 추가 방법
#pragma comment (lib, "d3d11.lib") //윈도우 내장 라이브러리
#pragma comment (lib, "DirectXTK.lib")
int APIENTRY wWinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrev, _In_ LPWSTR IpCmdLine, _In_ int nCmd)
{
return 0;
}
다이렉트X도 윈도우즈 프로그램이기 때문에 WINAPI와 같이 메인 함수가 있어야한다.
인자값들도 다 똑같다.
그리고 2개의 라이브러리를 추가 시켜줘야한다.
d3d11.lib는 다이렉트X 11버전의 라이브러리 이고 기본기능들과 그래픽카드와의 통신등을 포함하고 있다.
마이크로소프트 도큐먼트에 DirectXTK.lib 는 D3DX11의 현재 사용에 대한 대체 기술에 포함된다고 한다.
아마 DX11에서 게임을 위한 라이브러리가 아닐까 싶다.
WINAPI? APIENTRY?
WINAPI를 진행할때 메인함수의 앞에는 WINAPI가 있었다.
이번엔 APIENTRY가 있어서 궁금해서 찾아봤다.
이상하게도 APIENTRY는 WINAPI로 정의 해놓았다.
더 찾아보니
혹시나 모를 확장성을 위해 다른것들을 나눠놓은것이지만 요즘은 다른 의미가 없다
과거의 유물이고 요즘은 신경 쓸 필요 없다
라고들 하는데 자료가 많이 없어서 잘 모르겠다.
_In_ 과 _in_opt_ 는 무엇인가?
또 메인함수에 생소한 무언가가 있다.
궁금해서 검색해보니 해외 사이트에서 찾을 수 있었다.
MICROSOFT SAL(소스 코드 주석 언어)
함수가 해당 매개 변수를 사용하는 방법, 매개 변수에 대한 가정 및 완료 시 보장을 설명하는 데 사용할 수 있는 주석 집합을 제공합니다.
주석은 헤더 파일에 <sal.h>정의됩니다.
C++에 대한 Visual Studio 코드 분석은 SAL 주석을 사용하여 함수 분석을 수정합니다.
간단히 말하면 SAL은 컴파일러가 코드를 검사할 수 있는 저렴한 방법입니다.
주석이기 때문에 지워도 오류는 나지 않지만 내부적으로 어떻게 되는지는 모르겠다.
해석해보면 2번째 매개변수로 설정해둔 HINSTANCE hprev만 선택사항이고 나머지는 꼭 필요하다고 해석하면 될 것 같다.
더 궁금하신 분들은 여길 보시면 된다.
필요한 기능들 만들기
일단 정리를 하고 갈 개념이 있다.
wchar_t & wstring
wchar_t 는 아스키 코드로 표현할 수 없는 국제문자를 다루기 위한 자료형이다.
유니코드로 입력될 수 있는 문자들은 wchar_t형의 변수에 저장될 수 있다.
코드로 하나를 테스트 해보았다.
#include<iostream>
#include<string>
#include <locale>
int main()
{ //string
std::string stringtext = "유니티";
//wstring
std::wstring wstringtext = L"유니티";
std::wcout.imbue(std::locale("korean"));
std::cout << stringtext << std::endl;
std::wcout << wstringtext << std::endl << std::endl;
std::cout << stringtext.length() << std::endl;
std::cout << wstringtext.length() << std::endl;
return 0;
}
한글을 처리할때
일반 스트링은 세글자의 길이를 6으로 처리했고
w스트링은 세글자의 길이를 3으로 정확히 처리했다.
char는 1바이트이고
w_char는 파고들어가보면 unsigned short형 이라고 한다.
고로 w_char는 2바이트 라는것을 알 수 있다.
아직 정확히 어디에 쓸진 모르겠지만 영어가 아닌 문자열을 담을 수 있는 통이라고 생각하면 될 것같다.
스트링 -> w스트링 변환 기능 만들기
WINAPI는 wstring을 요구하는경우가 많아서 컨버터를 만들었다.
#pragma once
#include<string>
class StringConverter
{
public:
//일반 스트링을 넣으면 와이드스트링으로 반환해주는 함수
static std::wstring StringToWide(std::string str);
};
스트링을 1번만 선언해주고 (서로 include 할경우 무한 include를 하는 경우를 방지 하기 위해)
StringToWide라는 함수를 만들었다.
매개변수를 string으로 받아서 wstring으로 반환한다는 것을 알 수 있다.
#include "StringConverter.h"
std::wstring StringConverter::StringToWide(std::string str)
{
//스트링은 문자열.. 배열임 시작과 끝만 있다면 다 가져올수있음
return std::wstring(str.begin(),str.end());
}
클래스에서 함수를 오른쪽 클릭하고 선언/정의를 눌러 만들어 준 뒤에
str의 첫 주소값과 마지막 주소값을 wstring으로 리턴해주게 했다.
에러 띄우기 기능 만들기
오류가 떴을때 띄워주기위해 ErrorLogger라는 클래스를 만들어 주었다.
#pragma once//한번만 인클루드하는 전처리기 , 컴파일 전에 처리함 그래서 전처리기
#include"StringConverter.h" //얘를 인클루드해서 스트링을 인클루드 할 필요 없음
#include<Windows.h>
class ErrorLogger
{
public:
static void Log(std::string message);
static void Log(HRESULT hr, std::string message);
};
함수를 2개 만들어 주었다.
일반 메세지를 띄워주는 함수, HRESULT를 통해 결과값을 띄워주는 함수를 만들어준다.
HRESULT란?
HRESULT를 사용함으로 클라이언트에게 일관성있는 함수의 정보를 전달할 수 있게 한다고 한다.
상황에 따라 이런 메세지들이 리턴 되는 것 같다.
이제 만든 함수의 구현을 해주자.
#include "ErrorLogger.h"
#include<comdef.h> // hresult값을 해석가능
void ErrorLogger::Log(std::string message)
{
std::string error_message = "Error: " + message;
// 메박A는 일반스트링으로 표현가능
//LP = 레프트포인트 / 포인트라 생각하면됨.
//CSTR 문자열의 첫번째 요구
//제목이름, 아이콘
MessageBoxA(NULL, error_message.c_str(), "Error", MB_ICONERROR);
//아스키로 출력
}
//HRESULT -> 윈도우가 돌아가다 나오는 결과값
void ErrorLogger::Log(HRESULT hr, std::string message)
{
_com_error error(hr);
std::wstring error_message = L"Error: " + StringConverter::StringToWide(message) + L'\n' + error.ErrorMessage();
MessageBoxW(NULL, error_message.c_str(), L"Error", MB_ICONERROR);
}
함수 오버로딩을 통해 2개를 구현해준다.
첫번째는 일반 스트링을 매개변수로 받아서
error_message 라는 변수에 "에러" 와 매개변수 스트링을 합쳐준다.
MessageBoxA는 일반 스트링을 써서 메세지 박스를 띄워주는 함수인데
MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
요구하는 매개변수는 위와 같다.
첫번째는 윈도핸들,
두번째는 띄울 텍스트,
세번째는 메세지 박스 왼쪽 상단의 텍스트,
네번째는 (UINT = unsigned int를 정의해놨다) 메세지 박스 옵션을 넣어주면 된다.
그리고 우리는 위에서 소스 코드 주석 언어(SAL)을 공부했으니 알 수 있다.
4번째 매개변수는 꼭 입력해주어야 하지만 나머지는 _In_opt_ 이기때문에 꼭 필요한건 아니란 것을
나는 uType에 MB_ICONERROR를 넣어주었는데
stop-sign icon을 메세지박스에 나타나게 하는 임을 확인할 수 있다.
MessageBox function (winuser.h) - Win32 apps | Microsoft Docs
위에있는 C_str함수는 string을 char* 형으로 변환해주는 함수이다.
근데 매개변수에는 LPCSTR형을 요구하는데 왜 char*형으로 변환해주어야 하는지 의문이 들었다.
typedef const char* LPCSTR;
원형을 보니 LPCSTR이라는게 char*를 정의한것이기 때문이다.
찾아보니
LP : Long Pointer
T: TCHAR (멀티 바이트 환경에서는 char로, 유니코드 환경에서는 wide char형으로 형변환 해준다고 한다.)
STR : string
char*형이면 C_str함수로 변경해서 넘겨주면 되는 것 같다.
void ErrorLogger::Log(HRESULT hr, std::string message)
{
_com_error error(hr);
std::wstring error_message = L"Error: " + StringConverter::StringToWide(message) + L'\n' + error.ErrorMessage();
MessageBoxW(NULL, error_message.c_str(), L"Error", MB_ICONERROR);
}
2번째 함수에서는
에러 값을 받아서 hr에 넣고,
2번째 매개변수로 넘긴 message를 받아
error_Message라는 변수에 아까 만든 함수를 이용해 wstring으로 바꾼후 hr을 합쳐줬다.
여기선 메세지박스A대신에 MessageBoxW를 이용했는데,
wstring을 써서 메세지박스를 띄울 수 있다.
이런식으로 에러가 잘 나오는것을 볼 수있다.
C++이라 클래스를 만들고 정의를 하고 include를 하고 이런 하나하나가 자주 쓰던 C#과 달라서 어렵다.
근데 거기서 윈도우API로 윈도우창을 만드니 난이도를 더 올려주는 것 같다.
다음엔 구조를 잡고 윈도우 창을 만든것을 정리 할 예정이다.
'프로그래밍 공부 > C++ 프로그래밍' 카테고리의 다른 글
렌더링 파이프라인에 대해서.. (0) | 2022.06.30 |
---|---|
Direct X 6일차💻 <WINAPI> #마지막 (0) | 2022.06.21 |
Direct X 5일차💻 <WINAPI> #3 (0) | 2022.06.19 |
Direct X 4일차💻 <WINAPI> #2 (0) | 2022.06.19 |
WINAPI 추가정리 (0) | 2022.06.19 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!