프로그래밍 공부
작성일
2023. 10. 10. 19:42
작성자
WDmil
728x90

전에 만들었던 기본 예시파일을 기초적인 FrameWork로 나눈다.

 

휴일동안 직접 해봤는데 객체간 나누기가 너무 시간이 오래걸린다는걸 관과했다.

 

그래도 바로 다음날 수업에 Framework나누기를 해서 다행이라고 생각한다.

 

각 객체를 FrameWork로 나누기 위해서는 기초적인 객체 개념을 먼저 보아야 하는데,

 

C에서는  struct로 나누다가. C++에 와서 Class라는 개념으로 바뀌었다.

C C++
Struct 객체
  함수
  Class상속
   

상속 = 부모 -> 자식 상속관계로 이루어진다.

 

이러한 Class는 각 int나 char처럼. 객체로 다룰 수 있는데.

 

이를통해 포인터처럼 사용해서 class def() {}; 라고 한다면,

def* CL = new def();

이런식으로 동적할당을 진행해줄 수 있게 되었다.

 

이러한 동적할당의 개념을 조금 바꾼다면, 부모와 자식관계를 이용하는 상속 개념으로 이해해줄 수 있는데,

이는 다음과 같다.

 

위에서 만든 class에 다음과 같은 항을 추가한뒤.

class def 
{
private:
	int one;   
};

다음과 같이 선언하면,

class def2 : public def
{

};

def2 는 def를 상속받았음으로, 자식class

def는 def2에 상속했음으로 부모class라 칭할 수 있겠다.

 

여기서 업케스팅다운케스팅 이라는 개념을 짚고 넘어가야 한다.

 

그러나, 부모가 자식클레스를 받게되면

부모에서 자식의 개인적인 함수를 사용하지 못한다.

 

그래서 인터페이스 방식을 사용하는데. 이것을 사용하기 위해 가상함수 개념을 알고 넘어가야한다.

 

그렇다면, 부모에서는 함수가 있다는것만 알고있으면 자식함수에서 사용하는걸 쓸 수 있다.

 

그런데, 업케스팅에서는 소멸자 는 가상함수로 하면 안된다.

 

이유는. 가상함수는 각각 호출되어 스스로를 지워야 하기 때문, 소멸자가 덮어씌워지면 전 소멸자가 사용되지 않기때문

 

하나만 나타나도 되는 방식은 싱글톤 방식을 많이쓴다.

 

이런 방식을 사용하여 데이터 구조도를 좀더 유연하게 객관화되도록 바꿀 수 있다.

 


 

지금까지 작성했었던 코드 구조도를 객관화 시켜 여러 Class로 나누어보자.

 

우선. Winmain과 Device(GPU, CPU) , RenderManager(총 Render와 Update 수행)

 

로 나눌 수 있다.

 

다음과 같이 유틸리티들을 나누어보자

Scenes 에는 오브젝트 들이 나열된 하나의 입방체를 구성하는 부분이 될 것이며,

 

Device는 GPU와 CPU객체값들을.

 

Manager에서는 오브젝트들을 렌더링해주는 렌더링 그룹 으로 이해하면 될것이다.

 

그 후. Framework에 그에대한 헤더들을 기입해주면된다.


Device

#pragma once

// 디바이스 크리에이트
class Device : public Singleton<Device>
{
private:
	// 나 스스로와 싱글톤 에서만 접근이 가능하다.
	friend class Singleton;

	Device();
	~Device();
public:
	void Clear();
	void Present();
	ID3D11Device* GetDevice() { return device; }
	ID3D11DeviceContext* GetDeviceCOntext() { return deviceContext; }
	
private:
	void CreateDeviceAndSwapChain();

private:
	ID3D11Device* device;   // CPU
	ID3D11DeviceContext* deviceContext; // GPU

	IDXGISwapChain* swapChain; // 백버퍼 관리 인터페이스
	ID3D11RenderTargetView* renderTargetView; // GPU쪽 관련 메모리를 접근하는 용도. 중재역활
};
#include "Framework.h"

Device::Device()
{
	CreateDeviceAndSwapChain();
}

Device::~Device()
{
    device->Release();
    deviceContext->Release();

    swapChain->Release();
    renderTargetView->Release();
    // 할당된 것들을 전부 릴리즈 해준다.
}

void Device::Clear()
{
    float clearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };
    deviceContext->ClearRenderTargetView(renderTargetView, clearColor);


}

void Device::Present()
{
    swapChain->Present(0, 0);
}

void Device::CreateDeviceAndSwapChain()
{
    DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; // DESC = Discripstion. 표현하다. 스왑체인을 표현하다.
    swapChainDesc.BufferDesc.Width = WIN_WIDTH; // 디바이스 크기
    swapChainDesc.BufferDesc.Height = WIN_HEIGHT; // 디바이스 크기
    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; // 프레임
    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; // 프레임 디폴트값으로 저장해준다.
    // RefreshRate : 디스플레이 모드 갱신률(주시율 : Numerate / Denominator)
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.SampleDesc.Count = 1;   // 표본화 라는 개념.
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 1;
    swapChainDesc.OutputWindow = hWnd;
    swapChainDesc.Windowed = true;
    // 표본화 : 화면을 확대하거나 줄이면 이미지가 꺠진다. 이를 방지하기 위해
    // 아예 해상도가 높게 저장한다음에 줄일때만 해상도를 죽이는 느낌으로
    // 그런데 용량이 너무 큰 문제가 있음
    // DX에서는 다중 표본화 라는 기술을 사용한다.
    // 여러개의 이미지를 가지게한다음에 화소가 바뀔때 다른걸 넣는다.
    // 그런데 게임프로그래밍에서는 이걸 안쓴다.
    // 1이라는건 이걸 안쓰고 1개의 이미지만 사용한다는것.

    D3D11CreateDeviceAndSwapChain(
        nullptr,                    // 1. 어답터 : 창관리어뎁터, null이 들어가면 기본이 사용됨
        D3D_DRIVER_TYPE_HARDWARE,   // 2. 드라이버 타입 : 하드웨어 인지 가상드라이브인지 등 을 설정한다.
        0,                          // 3. 소프트웨어 타입 : 드라이버타입 에서 소프트웨어 유형을 설정했을때만 지정한다.
        D3D11_CREATE_DEVICE_DEBUG,  // 4. 플레그 : 현재 진행하는 디바이스가 디버그인지 일반인지 지정한다.
        nullptr,                    // 5. 피쳐레벨 : 어플리케이션이 하드웨어 성능을 얼마나 쓰는지 지정
        0,                          // 6. 피쳐레벨 : 위 값이 있을경우 배열의 개수를 지정한다.
        D3D11_SDK_VERSION,          // 7. SDK버전 : 현재 SDK버전 을 기입한다.
        &swapChainDesc,             // 8. 스왑체인 : 현재 스왑체인의 기입사항을 지정해준다.
        &swapChain,                 // 9. 스왑체인인터페이스 : 인터페이스 포인터를 기입한다.
        &device,                    // 10. 디바이스(CPU) : CPU 인터페이스 포인터를 기입한다.
        nullptr,                    // 11. 디바이스수준 : CPU기능수준을 기입한다. null일경우 사용X
        &deviceContext              // 12. 디바이스(GPU) : GPU 인터페이스 포인터를 기입한다.
    );

    ID3D11Texture2D* backBuffer;
    //백버퍼 정보는 스왑체인이 들고있다.

    swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
    // IID = interface ID
    // 보이드포인터 = 버튼을 받는데 어떤 버튼인지 모를때 voidpointer를 사용한다.
    // 보이드포인터 이기 때문에 뭐를 받는지 몰라서. 그 포인터의 IID를 받아오는 것이다.
    device->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
    // 렌더타겟뷰를 백버퍼로 사용한다. 렌더타겟뷰의 주소를 전달.
    backBuffer->Release();
    // 벡버퍼를 그래픽카드로 넘겨주면 쓸모가 없기 떄문에 삭제해준다.
    deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
    // 3D를 넣으려면 깊이값도 넣어야 하지만, nullptr로 아직 넘어간다. 아웃펏 머지 설정한것
}

위와 같이 Device를 나누어서 조립해줄 수 있다.

 

Device에서는 그래픽 파이프라인 중, 백버퍼와 Winmain의 기본화면을 초기화시켜주는 부분을 담당한다.


GameManager

#pragma once
class GameManager
{
public:
	GameManager();
	~GameManager();

	void Update();
	void Render();

private:
	void Create();
	void Delete();
private:
	Scene* scene;
};
#include "Framework.h"
#include "GameManager.h"

#include "Scenes/TutorialScene.h"

GameManager::GameManager()
{
	Create();

	scene = new TutorialScene();
}

GameManager::~GameManager()
{
	delete scene;

	Delete();
}

void GameManager::Update()
{
	scene->Update();
}

void GameManager::Render()
{
	scene->PreRneder();

	Device::Get()->Clear();

	scene->Render();
	scene->PostRender();
	scene->GUIRender();

	Device::Get()->Present();
}

void GameManager::Create()
{
	Device::Get();
}

void GameManager::Delete()
{
	Device::Delete();
}

GameManager에서는 위에서 설명했다싶이. 그래픽 라인의 Render와 Update를 담당한다.

 

여기의 scene와 Render에 각각 오브젝트가 들어있는 Scene값이 들어가면 될것이다.


Scene

#pragma once

// 인터페이스 방식으로 구현한다.
// 가상함수 ?
/*
	
*/
class Scene
{
public:
	virtual ~Scene() = default;

	virtual void Update() = 0; // 순수 가상함수.
	// 자식Class에서 무조건 override하지 않으면 안되게 정의해주는것.
	// 무조건 업케스팅으로만 써야함.
	virtual void PreRneder() = 0;	// 렌더타겟 쓸 때 기본적으로 써야하는 것을 미리 해주는것.
	virtual void Render() = 0;		// 객체 렌더.
	virtual void PostRender() = 0;	// 후처리
	virtual void GUIRender() = 0;

	virtual void Start() {}
	virtual void End() {}
};

위 Scene는 인터페이스. 가상함수 방식으로 동작한다.

 

가상함수는. 구현되지 않으면 안되는 부분을 임의로 구현해놓은. 건물로 생각하면 건물의 설계도를 미리 그려놓는다고 생각하면 될것이다.

 

설계도 대로 건물을 짓지 않으면 감리가 허가를 안해주듯. 순수 가상함수가 구현되지 않으면 컴파일러가 컴파일 오류를 띄워서 진행시켜주지 않는다.

 

 

728x90