https://www.inflearn.com/course/lecture?courseSlug=directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98&unitId=161127&tab=curriculum

 

학습 페이지

 

www.inflearn.com

 



1.Global Shader 생성

오늘은 Shader 코드를 수정해보자

코드의 최상단을 보자면 Matrix와 Texture 등 변수가 선언되어있는데 내부적으로는 이게 한꺼번에 초기화가 되고있다.

하지만 이렇게 선언된 변수 중에 물체에 종속적인 부분과 그렇지 않은 부분도 있으며 코드상에 중복되는 부분도 많다.

오늘은 그런 부분을 고쳐줄 것이다.

shader에서도 헤더파일처럼 기존 헤더파일을 가져와서 쓸 수 있다. 

이를 통해 자주쓰는 쉐이더 코드를 모아둘 GlobalShader를 만들어주자

이때 그냥 카메라의 좌표를 사용하기에는  회전이 적용될 경우 회전에 적용되는 값도 들어가기 때문에 카메라의 월드 좌표는 카메라의 역행렬을 통해 구해준.  

Global.fx

#ifndef _GLOBAL_FX_
#define _GLOBAL_FX_

/////////////////
// ConstBuffer //
/////////////////

cbuffer GlobalBuffer
{
    matrix V;
    matrix P;
    matrix VP;
};

cbuffer TransformBuffer
{
    matrix W;
};

/////////////////
// VertexBuffer //
/////////////////

struct Vertex
{
    float4 position : POSITION;
};

struct VertexTexture
{
    float4 position : POSITION;
    float2 uv : TEXCOORD;
};

struct VertexColor
{
    float4 Position : POSITION;
    float4 Color : COLOR;
};

struct VertexTextureNormal
{
    float4 position : POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

/////////////////
// Vertexoutput //
/////////////////

struct VertexOutput
{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

/////////////////
// SamplerState //
/////////////////

SamplerState LinearSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState PointSampler
{
    Filter = MIN_MAG_MIP_POINT;
    AddressU = Wrap;
    AddressV = Wrap;
};

/////////////////////
// RasterizerState //
/////////////////////

//와이어프레임모드
RasterizerState FillModeWireFrame
{
    FillMode = Wireframe;
};

///////////
// Macro //
////////////

#define PASS_VP(name, vs, ps)                       \
pass name                                           \
{                                                   \
	SetVertexShader(CompileShader(vs_5_0, vs()));   \
	SetPixelShader(CompileShader(ps_5_0, ps()));    \
}                  


//////////////
// Function //
//////////////

#endif

 

이를 활용하는 쉐이더 코드도 만들어주자

GlobalTest.fx

#include "00. Global.fx"


VertexOutput VS(VertexTextureNormal input)
{
	VertexOutput output;
    output.position = mul(input.position,W);
    output.position = mul(output.position, VP);
	
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}

Texture2D Texture0;

float4 PS(VertexOutput input) : SV_TARGET
{
    //내적
    //return float4(1, 1, 1, 1) * dot(light, normal);
	//텍스처 -> 샘플링된 좌표 
    return Texture0.Sample(LinearSampler, input.uv);
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

그리고 만들어준 글로벌 쉐이더에 맞게 데이터를 버퍼에 저장하고 넘겨주는 RenderManager라는 클래스를 만들어주자

이때 ID3DX11EffectConstantBuffer를 활용하여 정보를 밀어넣어줄 것이다.

RenderManager.h

#pragma once
#include "ConstantBuffer.h"

class Shader;

struct GlobalDesc
{
	Matrix V = Matrix::Identity;
	Matrix P = Matrix::Identity;
	Matrix VP = Matrix::Identity;
};

struct TransformDesc
{
	Matrix W = Matrix::Identity;
};

class RenderManager
{
	DECLARE_SINGLE(RenderManager);

public:
	void Init(shared_ptr<Shader> shader);

	void PushGlobalData(const Matrix& view, const Matrix& projection);
	void PushTransformData(const TransformDesc& desc);

private:
	shared_ptr<Shader> _shader;

	//프레임마다 한번만 세팅
	GlobalDesc _globalDesc;
	shared_ptr<ConstantBuffer<GlobalDesc>> _globalBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _globalEffectBuffer;


	TransformDesc _transformDesc;
	shared_ptr<ConstantBuffer<TransformDesc>> _transformBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _transformEffectBuffer;
};

 

RenderManager.cpp

#include "pch.h"
#include "RenderManager.h"

void RenderManager::Init(shared_ptr<Shader> shader)
{
	_shader = shader;

	_globalBuffer = make_shared<ConstantBuffer<GlobalDesc>>();
	_globalBuffer->Create();
	_globalEffectBuffer = _shader->GetConstantBuffer("GlobalBuffer");

	_transformBuffer = make_shared<ConstantBuffer<TransformDesc>>();
	_transformBuffer->Create();
	_transformEffectBuffer = _shader->GetConstantBuffer("TransformBuffer");
}

void RenderManager::PushGlobalData(const Matrix& view, const Matrix& projection)
{
	_globalDesc.V = view;
	_globalDesc.P = projection;
	_globalDesc.VP = view * projection;
	_globalBuffer->CopyData(_globalDesc);
	//쉐이더로 밀어넣기
	_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}

void RenderManager::PushTransformData(const TransformDesc& desc)
{
	_transformDesc = desc;
	_transformBuffer->CopyData(_transformDesc);
	_transformEffectBuffer->SetConstantBuffer(_transformBuffer->GetComPtr().Get());
}

 

새로 만들어준 매니저에 맞게 MeshRenderer 클래스 수정해주자

MeshRenderer.cpp

#include "pch.h"
#include "MeshRenderer.h"
#include "Camera.h"
#include "Game.h"
#include "Mesh.h"
#include "Shader.h"

MeshRenderer::MeshRenderer() : Super(ComponentType::MeshRenderer)
{

}

MeshRenderer::~MeshRenderer()
{

}

void MeshRenderer::Update()
{
	if (_mesh == nullptr || _texture == nullptr || _shader == nullptr)
		return;

	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());

	auto world = GetTransform()->GetWorldMatrix();
	RENDER->PushTransformData(TransformDesc{ world });
	
	uint32 stride = _mesh->GetVertexBuffer()->GetStride();
	uint32 offset = _mesh->GetVertexBuffer()->GetOffset();

	DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);

	_shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}

 

이제 실제 메인코드인 GlobalTestDemo의 코드를 수정해주자

GlobalTestDemo.h

#pragma once

#include "IExecute.h"
#include "Geometry.h"

class GlobalTestDemo : public IExecute
{
public:
	void Init() override;
	void Update() override;
	void Render() override;


	shared_ptr<Shader> _shader;
	
	// Object
	shared_ptr<GameObject> _obj;
	//Camera
	shared_ptr<GameObject> _camera;
};

GlobalTestDemo.cpp

#include "pch.h"
#include "10. GlobalTestDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void GlobalTestDemo::Init()
{
	_shader = make_shared<Shader>(L"08. GlobalTest.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		RESOURCES->Init();
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	RENDER->Init(_shader);
}

void GlobalTestDemo::Update()
{
	_camera->Update();

	RENDER->Update();
	_obj->Update();
}

void GlobalTestDemo::Render()
{
	
}

 

이렇게 해주면 이제 베이가 구가 나타난다.

 

 

2.Depth Stencil 생성

오늘은 3D에서 중요한 Depth Stencil을 추가해보자 

지금 만약 깊이가 다르게 물체를 2가지 생성해보면 현재 카메라의 위치가 달라질 수록 물체가 다르게 보이며 뒤에 

있는 오브젝트 큐브가 오히려 앞에 있는 것처럼 보이기도 한다.

 

이때 지금의 쉐이더에서는 깊이 정보를 제외하고 각 물체들의 정보만을 가지고 물체를 생성해주고 있는데 이때 깊이 정보 값을 추가 해주어야한다. 이렇게 해주기 위해 우리가 전체적인 그래픽이 필요한 것을 정의해줬던 Graphics 클래스에서

우리의 화면 정보와 깊이정보를 가지고 있을 변수를 선언하고 초기화해주자

이때 desc 부분에서 주석이 옆에 달린 부분을 주의하여 초기화해주자

Graphics.h

#pragma once


//그려지는 것에 필요한 것들
class Graphics
{
	DECLARE_SINGLE(Graphics);

public:
	void Init(HWND hwnd);

	void RenderBegin();
	void RenderEnd();

	ComPtr<ID3D11Device> GetDevice() { return _device; }
	ComPtr<ID3D11DeviceContext> GetDeviceContext() { return _deviceContext; }

private:
	void CreateDeviceAndSwapChain();
	void CreateRenderTargetView();
	void CreateDepthStencilView();
	void SetViewport();

private:
	HWND _hwnd = {};

	// Device & SwapChain
	//물체를 생성 및 바인딩
	ComPtr<ID3D11Device> _device = nullptr;
	ComPtr<ID3D11DeviceContext> _deviceContext = nullptr;
	ComPtr<IDXGISwapChain> _swapChain = nullptr;

	// RTV - 쉐이더에서 계산한거 그려주기
	ComPtr<ID3D11RenderTargetView> _renderTargetView;

	// DSC- 깊이 
	ComPtr<ID3D11Texture2D> _depthStencilTexture;
	ComPtr<ID3D11DepthStencilView> _depthStencilView;


	// Misc
	D3D11_VIEWPORT _viewport = { 0 };
};

Graphics.cpp

#include "pch.h"
#include "Graphics.h"

void Graphics::Init(HWND hwnd)
{
	_hwnd = hwnd;

	CreateDeviceAndSwapChain();
	CreateRenderTargetView();
	CreateDepthStencilView();
	SetViewport();
}

void Graphics::RenderBegin()
{
	_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), _depthStencilView.Get());
	_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), (float*)(&GAME->GetGameDesc().clearColor));
	_deviceContext->RSSetViewports(1, &_viewport);
}

void Graphics::RenderEnd()
{
	HRESULT hr = _swapChain->Present(1, 0);
	CHECK(hr);
}
void Graphics::CreateDeviceAndSwapChain()
{
	DXGI_SWAP_CHAIN_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	{
		desc.BufferDesc.Width = GAME->GetGameDesc().width;
		desc.BufferDesc.Height = GAME->GetGameDesc().height;
		desc.BufferDesc.RefreshRate.Numerator = 60;
		desc.BufferDesc.RefreshRate.Denominator = 1;
		desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		desc.SampleDesc.Count = 1;
		desc.SampleDesc.Quality = 0;
		desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		desc.BufferCount = 1;
		desc.OutputWindow = _hwnd;
		desc.Windowed = TRUE;
		desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	}

	HRESULT hr = ::D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE,
		nullptr,
		0,
		nullptr,
		0,
		D3D11_SDK_VERSION,
		&desc,
		_swapChain.GetAddressOf(),
		_device.GetAddressOf(),
		nullptr,
		_deviceContext.GetAddressOf()
	);

	CHECK(hr);
}

void Graphics::CreateRenderTargetView()
{
	HRESULT hr;

	ComPtr<ID3D11Texture2D> backBuffer = nullptr;
	hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());
	CHECK(hr);

	hr = _device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());
	CHECK(hr);
}

void Graphics::CreateDepthStencilView()
{
	{
		//화면크기와 동일한 텍스처 생성
		D3D11_TEXTURE2D_DESC desc = { 0 };
		ZeroMemory(&desc, sizeof(desc));
		desc.Width = static_cast<uint32>(GAME->GetGameDesc().width);
		desc.Height = static_cast<uint32>(GAME->GetGameDesc().height);
		desc.MipLevels = 1;
		desc.ArraySize = 1;
		desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;		//
		desc.SampleDesc.Count = 1;
		desc.SampleDesc.Quality = 0;
		desc.Usage = D3D11_USAGE_DEFAULT;
		desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;		//2개만 신경써주면된다.
		desc.CPUAccessFlags = 0;
		desc.MiscFlags = 0;

		HRESULT hr = DEVICE->CreateTexture2D(&desc, nullptr, _depthStencilTexture.GetAddressOf());
		CHECK(hr);
	}
	
	{
		D3D11_DEPTH_STENCIL_VIEW_DESC desc;
		ZeroMemory(&desc, sizeof(desc));
		desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;		//
		desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
		desc.Texture2D.MipSlice = 0;

		HRESULT hr = DEVICE->CreateDepthStencilView(_depthStencilTexture.Get(), &desc, _depthStencilView.GetAddressOf());
		CHECK(hr);
	}
}

void Graphics::SetViewport()
{
	_viewport.TopLeftX = 0.0f;
	_viewport.TopLeftY = 0.0f;
	_viewport.Width = static_cast<float>(GAME->GetGameDesc().width);
	_viewport.Height = static_cast<float>(GAME->GetGameDesc().height);
	_viewport.MinDepth = 0.0f;
	_viewport.MaxDepth = 1.0f;
}

 

이렇게 만들어준 텍스처와 View는 OM, Output Merge에서 어떻게 사용할지 세팅해줄 수 있다. 

이렇게 해주면 우리가 만들어준 화면을 담고 있는 텍스처에 깊이값을 기록하게 된다. 이렇게 기록된 깊이 값을 통해 물체를

그려주게 된다. 

하지만 이렇게 실행하면 아무것도 나오지 않는데 이렇게 나오는 이유는 매프레임마다 깊이값을 다시 밀어줘야하는데 그것을 아직 안하고 있기 때문이다. 

이를 해결하기 위해 RenderBegin 함수에 스텐실뷰를 초기화해주는 함수를 추가해주자

_deviceContext->ClearDepthStencilView(_depthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);

 

이 초기화 함수는 같은 깊이 값에 다른 물체가 생성된다고 하면 중복되기 때문에 생성할 수 없기 때문에 이것을 방지하기 위하여 제일 끝인 1로 초기화를 해주는 것이다.

 

이렇게 해주면 깊이에 따른 물체의 순서와 크기가 제대로 그려진다.

 

 

 

+ Recent posts