class A
{
public:
A() {}
};
class B : public A
{
using A::A;
};
디버깅 클래스를 클래스 내부에서 만들어서 사용하려고 했는데 생성자 상속이 잘 되지 않았다. 아직 이유는 못 찾았다. using 구문으로 넣어줘도 계속 빨간 줄이 쳐져서 그냥 밖으로 분리했다.
현재까지 완성된 Exception 클래스는 다음과 같다. 거의 다 되었는데, HRESULT를 리턴하지 않는 context->Draw 같은 함수들에 대한 디버깅을 하고 싶은데 어떻게 해야할 지 모르겠다.
#pragma once
class BasicException : public std::exception
{
public:
/*To know where the Exception was thrown from*/
BasicException(const char* file, int line);
const char* what() const noexcept override;
/*show what the exception type is*/
virtual const char* GetType() const noexcept;
std::string GetOriginString() const noexcept;
private:
int _line;
std::string _file;
protected:
mutable std::string whatBuffer;
DEFINEPROPERTY:
READONLY_PROPERTY(std::string, file);
GET(file) { return _file; }
READONLY_PROPERTY(int, line);
GET(line) { return _line; }
};
/*
Window 생성시 HRERROR Exception 처리
*/
class HrException : public BasicException
{
public:
HrException(const char* file, int line, HRESULT hr) noexcept;
static std::string TranslateErrorCode(HRESULT hr) noexcept;
const char* what() const noexcept override;
const char* GetType() const noexcept override;
std::string GetErrorDescription() const noexcept;
private:
HRESULT _hr;
DEFINEPROPERTY:
READONLY_PROPERTY(HRESULT, hr);
GET(hr) noexcept { return _hr; }
};
/*
DirectX의 HRESULT 리턴하는 함수 호출 시 발생하는 Exception 처리
*/
class GraphicsException : public HrException
{
public:
GraphicsException(const char* file, int line, HRESULT hr, std::vector <std::string> infoMsgs = {}) noexcept;
const char* GetType() const noexcept override;
private:
std::string info;
DEFINEPROPERTY:
READONLY_PROPERTY(std::string, infoString);
GET(infoString) noexcept { return info; }
};
/*
Bind 하는 함수들 호출 중 오류가 발생할 경우 (예: vertexShader를 바인딩하지 않고 렌더링을 했을 경우) 오류를 출력하기 위한 Exception
HRESULT를 리턴하지 않는 함수를 대상으로 함.
*/
class InfoException : public BasicException
{
public:
InfoException(const char* file, int line, std::vector<std::string> infoMsgs = {}) noexcept;
const char* GetType() const noexcept override;
private:
std::string info;
DEFINEPROPERTY:
READONLY_PROPERTY(std::string, infoString);
GET(infoString) noexcept { return info; }
};
#ifndef NDEBUG
#define BASIC_EXCEPT BasicException(__FILE__, __LINE__)
#define HWND_EXCEPT(hr) HrException(__FILE__, __LINE__, hr)
#define GFX_THROW_HR(hrcall) if(FAILED(hr = (hrcall))) throw GraphicsException(__FILE__, __LINE__, hr)
#else
BASIC_EXCEPT BasicException(__FILE__, __LINE__)
HWND_EXCEPT(hr) HrException(__FILE__, __LINE__, hr)
GFX_THROW_INFO(hrcall) if (FAILED(hr = (hrcall))) throw GraphicsException(__FILE__, __LINE__, hr)
#endif
그리고 드디어 셰이더 컴파일이 제대로 되었다. 이유는 ProjectDir가 나는 sln 파일이 있는 곳이 기준인 줄 알았는데 코드들이 있는 폴더를 가리키고 있었다.
셰이더 컴파일을 위해서는 다음이 필요하다.
#include <d3dcompiler.h>
#pragma comment(lib, "D3DCompiler.lib")
이걸 포함시켜줘야D3DReadFileToBlob 함수를 사용할 수 있게 된다. cso 파일로 컴파일된 셰이더 파일을 ID3D11Blob으로 가져오고, 그 데이터를 토대로 CreateVertexShader, CreatePixelShader 함수를 호출하는 식으로 셰이더를 만들었다.
어쨌든 결국에 삼각형...을 화면에 띄우기는 했다.
문제는 삼각형의 모양이다. 저 가운데 점은 아무리 수정을 해주어도 여전히 가운데를 가리키고 있었다.
삼각형 하나 띄우는게 이렇게 힘들 줄이야..
지금까지 해온 것들을 되돌아보면서 원인을 찾아봐야 겠다.
자, App이라는 클래스 내부에는 Window를 갖고 있다. Window가 하는 역할은 WindowsAPI를 이용해 윈도우즈 응용 프로그램 창을 띄우고, 내부에 Input과 Graphics 클래스를 포함하고 있는 녀석이다. Input은 해당 윈도우로 오는 입력을 처리하기 위한 것이므로 일단 제껴두고, Graphics에서 현재 SwapChain, Device, Context를 생성하고, 스왑체인의 백버퍼를 가지고 렌더타겟뷰를 만들고, 뷰포트를 세팅한 후, DepthStencilView를 만들어주었다.
여기서 화면에 그림을 그리기 위한 필수적인 요소는 다음과 같다.
Vertex 구조체를 정의한다.
Vertex 구조체에 들어있는 데이터가 각각 어떤 의미를 갖고 있는지를 Graphic 카드에게 전달해주기 위해 InputLayout을 등록한다. 하지만 이 과정에서 VertexShader의 바이트 코드를 요구하므로 VsShader가 만들어진 후 ID3D11Blob 구조체를 여기다가 갖다 줘야 한다. 즉, 이 InputLayout 등록은 VS가 생성된 이후에 가능하다.
Vertex 구조체 배열을 생성하고, 이를 토대로 VertexBuffer를 생성한다. ID3D11Buffer 구조체를 활용하여 만든다.
생성한 버퍼를 IA에 Set해준다. IASetVertexBuffers를 호출한다.
vertex shader와 pixel shader를 생성한다.
생성된 vertex shader와 pixel shader를 바인딩해준다. VSSetShsader 와 PSSetShader 를 호출해 주는 것이다.
렌더타겟을 바인딩한다. 즉, 생성된 RenderTargetView 구조체는 여러개 있을 수 있으며, 그중에 어떤 RenderTargetView를 파이프라인에 넣을 것인지 지정을 해주어야 한다.
그리고 primitiveTopology를 세팅해준다. 들어간 Vertex구조체 배열을 어떤 방식으로 이어주어야 하는지를 알려주는 것이다. 삼각형이니까 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST 로 해주면 된다.
Viewport를 생성해준다. RenderTargetView 내부를 다시한번 쪼개서 여러 개의 뷰포트로 만들어줄 수 있다. 아직 그런 Variation을 넣을 정도의 수준이 아니니 그냥 창 크기대로 Viewport를 한개 만들어서 등록해주었다.
레스터라이져, DepthStencil 버퍼는 일부러 만들고 세팅하지 않아도 기본적인 삼각형을 띄우는 데 문제는 없다.
근데 왜 안되냐고.
엇... 갑자기 해결했다. 이유는 VertexBuffer를 만들 때 Description에서 ByteWidth에 뭔가 문제가 있었다.
struct Vertex{float x,y,z;} 를 저장한 vector를 가지고 버퍼를 생성하기 위해 ByteWidth는 데이터 전체의 크기이기 때문에sizeof(Vertex) * v.size() 를 해주면 전체 데이터의 크기를 알 수 있을 것이다. 실제로 이렇게 쓰면 잘 동작했다.
문제는 내가 같잖은 생각으로 v.data()를 하면 Vertex*가 나오는걸 Vertex가 나오는 줄 알고 멍청하게 sizeof(v.data()) * v.size() 라고 써버렸다. 그러니까 데이터를 담을 공간이 부족하게 되고 내가 넣은 Vertex의 정보가 짤려서 세 번째 Vertex의 값이 0으로 되버린 것이다.