오늘은 삼각형을 그려볼 것이다. 

 

삼각형 하나를 그리더라도 전체 파이프라인은 한번 거쳐야한다. 

 

 

일단 기하학적인 도형을 만들어주는 함수를 만들고 정점을 나타내줄 구조체도 선언해준다.

 

Stuct.h

#pragma once
#include "Types.h"


struct Vertex
{
	Vec3 position;		//12바이트 0부터시작
	Color color;		//12부터시작
};

 

 

정점 선언해주기 

정점은 -1,1 사이의 범위에서 선언해준다. -> 화면 상의 좌표때문에 

정점 정보까지는 아직 CPU에서의 영역, 

RAM<->CPU VRAM<->GPU

->VRAM에도 같은 정보를 만들어줘야한다. 

-> _device->CreateBuffer(); 마지막인자로 정점버퍼를 받는다 -> 이를 통해 정점버퍼 초기화

 

D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Usage = D3D11_USAGE_IMMUTABLE;			//디폴트- GPU만 읽고 쓸수있음 
IMMUTABLE GPU만 읽을 수 있다. 
DYNAMIC- GPU : 읽기 CPU: 쓰기 
STAGING - CPU->GPU 데이터전송

 

메모리에 있는 정점 넘겨주기 전체코드 

이렇게 되면 이제 _vertices가 가지고있던 정점 정보를 gpu쪽으로 넘겨줘서 이후에는 gpu만 read only할 수 있도록 한다. 

//정점정보
{
	_vertices.resize(3);

	_vertices[0].position = Vec3(-0.5f, -0.5f, 0.f);
	_vertices[0].color = Color(1.f, 0.f, 0.f, 1.f);
	_vertices[1].position = Vec3(0.f, 0.5f, 0.f);
	_vertices[1].color = Color(1.f, 0.f, 0.f, 1.f);
	_vertices[2].position = Vec3(0.5f, -0.5f, 0.f);
	_vertices[2].color = Color(1.f, 0.f, 0.f, 1.f);
}

//정점버퍼
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.Usage = D3D11_USAGE_IMMUTABLE;			//gpu만 read only
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc.ByteWidth = (uint32)(sizeof(Vertex) * _vertices.size());

	D3D11_SUBRESOURCE_DATA data;
	ZeroMemory(&data, sizeof(data));
	data.pSysMem = _vertices.data();			//첫번째 시작주소 cpu값이 복사된다.

	_device->CreateBuffer(&desc,&data, _vertexBuffer.GetAddressOf());
}

 

 

이제 입력에 대한 정보를 명시해줘야한다. 

void Game::CreateInputLayout()
{
	//입력에 대한 정보 ~바이트부터 뛰면 칼러인지 
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
		{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}
	};

	const int32 count = sizeof(layout)/sizeof(D3D11_INPUT_ELEMENT_DESC);
	_device->CreateInputLayout(layout,count,);
}

 

하지만 CreateInputLayout 함수의 매개변수에는 쉐이더에 관한 변수가 필요하다. 

그럼 쉐이더를 만들어주자 

쉐이더 -> 관련 코드를 넣어두는 하나의 파일

 

필터를 새로만들고 HLSL파일을 만들어보자 

쉐이더파일 생성

 

쉐이더

-> 1. 먼저 컴파일/ 빌드해서 cso파일을 사용 2.실행 순간에 코드를 읽어서 컴파일 -> 동적으로 사용

->먼저 만들어진걸 사용하는게 좋다. -> 문법적인 /오타 같은 오류를 컴파일 단계에서 알 수 있다. 

 

쉐이더 입력으로 들어오는 구조체부터 선언해주자 이때 InputLayout과 이름을 맞춰주자

struct VS_INPUT
{
    float4 position : POSITIONT;
    float4 color : COLOR;
};

 

쉐이더관련 코드를 완성해보자

정점 쉐이더와 픽셀 쉐이더 단계에 필요한 함수를 추가해준다.

struct VS_INPUT
{
    float4 position : POSITION;
    float4 color : COLOR;
};

struct VS_OUTPUT
{
    float4 position : SV_POSITION;      //시스템 VALUE  무조건 있어야한다.
    float4 color : COLOR;
};

//정점 쉐이더- 위치관련 -> 레스터라이저-정점바탕으로 도형만들고 내/외부 판단 및 보간
VS_OUTPUT VS(VS_INPUT input)
{
    VS_OUTPUT output;
    output.position = input.position;
    output.color = input.color;
    
    return output;
}

//모든 픽셀단위 대상 - 색상관련
float4 PS(VS_OUTPUT input) : SV_Target  //랜더 타켓하는 곳으로 보내주기
{
    
    
    return float4(1, 0, 0, 0);
}

 

그리고 이제 이 쉐이더를 가져올 함수를 만들자

만들기전에 정점/ 픽셀 쉐이더 관련 변수를 추가해준다. 

	//VS
	ComPtr<ID3D11VertexShader> _vertexShader = nullptr;
	ComPtr<ID3DBlob> _vsBlob = nullptr;
	//PS
	ComPtr<ID3D11PixelShader> _pixelShader = nullptr;
	ComPtr<ID3DBlob> _psBlob = nullptr;
void Game::LoadShaderFromFile(const wstring& path, const string& name, const string& version, ComPtr<ID3DBlob>& blob)
{
	const uint32 compileFlag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;		//디버그 최적화 건너뛰기
	HRESULT hr = ::D3DCompileFromFile(
		path.c_str(),
		nullptr,
		D3D_COMPILE_STANDARD_FILE_INCLUDE,
		name.c_str(),
		version.c_str(),
		compileFlag,
		0,
		blob.GetAddressOf(),
		nullptr
	);

	CHECK(hr);
}

 

파일에 있던걸 가져와서 어떻게 작동하게 할지 건내줘야해서 가져오는 함수를

VS와 PS에서도 해야하기 때문에 관련 함수를 만들어 준다. 

void Game::CreateVS()
{
	LoadShaderFromFile(L"Default.hlsl", "VS", "vs_5_0", _vsBlob);

	HRESULT hr = _device->CreateVertexShader(_vsBlob->GetBufferPointer(),
		_vsBlob->GetBufferSize(), nullptr, _vertexShader.GetAddressOf());
	CHECK(hr);
}

void Game::CreatePS()
{
	LoadShaderFromFile(L"Default.hlsl", "PS", "ps_5_0", _psBlob);

	HRESULT hr = _device->CreatePixelShader(_psBlob->GetBufferPointer(),
		_psBlob->GetBufferSize(), nullptr, _pixelShader.GetAddressOf());
	CHECK(hr);
}

 

 

이제  Init 부분에 만든 함수들을 실행시키도록 넣어준다 .

 

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_width = GWinSizeX;
	_height = GwinSizeY;


	CreateDeviceAndSwapChain();
	CreateRenderTargetView();
	SetViewport();

	//삼각형 그리기 파트
	CreateGeometry();
	CreateVS();
	CreateInputLayout();
	CreatePS();
}

 

 

그리고 실제로 그리기 부분은 렌더부분에서 이루어 지기때문에 파이프라인 단계에 맞게 채워준다.

void Game::Render()
{
	RenderBegin();			//초기화

	// IA - VS - RS - PS -OM
	//TODO : 그리기
	{
		uint32 stride = sizeof(Vertex);
		uint32 offset = 0;
		//IA - 세팅부분
		_deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(),&stride, &offset);
		_deviceContext->IASetInputLayout(_inputLayout.Get());
		_deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);		//삼각형으로 만들어주기

		//VS
		_deviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0);		//이걸로 일하게 

		//RS

		//PS
		_deviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
		//OM

		_deviceContext->Draw(_vertices.size(), 0);
	}

	RenderEnd();			//제출
}

 

실행화면 

 

 

만약 PS 단계에서 입력의 색을 따라 만들고 세 점을 RGB 하나씩 가지고 있도록 한다면 

 

//모든 픽셀단위 대상 - 색상관련
float4 PS(VS_OUTPUT input) : SV_Target  //랜더 타켓하는 곳으로 보내주기
{
    
    
    return input.color;
}

 

이런식으로 그라데이션 삼각형이 나오게 된다. 

-> 보간이 잘 이루어졌다. 

 

정점 / 자원 -> cpu만 가지고 있었는데 createVertexBuffer을 통해 gpu로 복사가 이루어진다.

gpu도 해당 자원을 가지고 있게 된다. 

IA - createVertexBuffer를 통해 자원을 사용하겠다는 의미

VS- 정점을 gpu에서 연산 처리 -> 정점에 대한 정보가 확정되어서 나가게 된다.

Rasterize- 삼각형있을 때 내/외부 판단, 보간 진행

PS- 모든 픽셀에 대한 색상 - 빛 / 그림자

랜더타켓 - 어디에다 그려줘

 

VS,PS- 세부적인 옵션을 건드릴 수 있도록하는 툴로 만들면 -> 머테리얼 

 

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]3.장치초기화  (0) 2024.06.27
[Directx11]2.초기설정  (0) 2024.06.26
[Directx11]그래픽스OT  (0) 2024.06.25
[Directx11]2.랜더링 파이프라인2  (0) 2024.06.21
[Directx11]1. 렌더링 파이프라인-1  (0) 2024.06.19

https://www.youtube.com/watch?v=-5jCh22feJg&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=20

1. 인벤토리 카테고리에 해당하는 텍스트 만들어주기 

각 칸의 카테고리에 해당하는 텍스트를 옆에 붙여준다.

 

2. 활, 방패같은 장비는 등에 먼저 보이도록 로직 수정 

일단 활과 방패의 fbx 파일을 import 해주자 

https://sketchfab.com/3d-models/medieval-shield-b98b8f64d935415aab0fe9b70074511f#download

 

Medieval Shield - Download Free 3D model by Artem Mykhailov - Sketchfab

Artstation post: https://www.artstation.com/artwork/e012eY Based on the concept by Artyom Vlaskin: https://artyomvlaskin.cgsociety.org/okkw/928308

sketchfab.com

https://sketchfab.com/3d-models/wooden-bow-free-a762abcffc27478caf19dfaac086485d#download

 

Wooden Bow Free - Download Free 3D model by Red_Ilya - Sketchfab

Not the strongest bow, but when there is nothing at hand, it is the best

sketchfab.com

 

Fbx파일을 다운 받은 뒤 import 해주고 텍스처 설정을 해줘야한다. 

Shield 텍스처 설정
Bow 텍스처 설정

 

캐릭터 매쉬 골격에 소캣 추가해주기

등에 활과 방패가 들어올 수 있도록 소캣을 추가해준다. 

소캣
결과

이제 실제 매쉬에 스태틱 매쉬 컴포넌트를 추가해서 실제로 적용될 수 있도록 한다. 

스태틱매쉬를 만든 뒤 부모소캣을 위에서 만들어둔 소캣으로 적용하면 된다.

 

소캣 설정

 

이제 만든 매쉬에 들어갈 수  있도록 커스텀 이벤트를 만들어주자 

그리고 들어오는 아이템 타입에 따라 다른 이벤트가 호출되도록 스위치문을 구성해준다. 현재 아머는 없어서 그냥 스트링만 출력하도록 했다.

이벤트 구성 및 흐름도

 

DB 테이블에 방패를 추가해주고 스태틱매쉬와 소캣을 맞게 수정해준다.

DB테이블

 

결과화면

 

https://school.programmers.co.kr/learn/courses/30/lessons/131130

 

문제가 정말 길다. 

하지만 우리가 봐야할 것은

입력에서 주어지는 나열된 상자를 보면 된다.

이 상자를 순회하면서 문제에서 주어진 방식대로 상자 안의 숫자로 계속 상자를 방문처리하면서 그룹을 나누고 만약 그룹이 2개보다 작다면 0점이고 아니라면 그룹 수 중에 제일 많은 수와 그 다음으로 많은 수를 곱해서 반환해주면 된다. 

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

// 특정 시작점에서 그룹의 크기를 찾는 함수
int find_group(int start, vector<int>& cards, vector<bool>& visited) {
    int group_size = 0;  // 그룹 크기 초기화
    int current = start;  // 현재 상자를 시작 상자로 설정
    while (!visited[current]) {  // 현재 상자가 방문되지 않은 동안 반복
        visited[current] = true;            //방문처리 
        current = cards[current] - 1;  // 상자 안의 숫자를 통해 다음 상자 선택
        group_size += 1;  // 그룹 크기를 증가시킴
    }
    return group_size;  
}

int solution(vector<int> cards) {
    int n = cards.size();  // 상자의 수
    vector<bool> visited(n, false);  // 방문 여부를 저장
    vector<int> group_sizes;  // 각 그룹의 크기를 저장

    // 모든 상자에 대해 그룹을 찾음
    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {  // 현재 상자가 방문되지 않았다면
            int group_size = find_group(i, cards, visited);  // 그룹의 크기를 찾음
            group_sizes.push_back(group_size);  // 그룹 크기를 벡터에 추가
        }
    }

    // 그룹 크기를 내림차순으로 정렬
    sort(group_sizes.rbegin(), group_sizes.rend());

    // 최대 점수를 계산
    if (group_sizes.size() < 2) {  // 그룹이 2개 미만이면
        return 0;  // 점수는 0
    } else {
        return group_sizes[0] * group_sizes[1];  // 가장 큰 두 그룹의 크기를 곱한 값이 최대 점수
    }
}

 

 

함수,인자가 엄청 많기 때문에

다이렉트 11을 공부할 때는 나무보다는 숲을 봐야한다. 

옵션 - 특이점이 있는지만 파악하기 

틀을 공부하자 

 

유용한 팁 

// I: 인터페이스 Com객체-> 생성은 함수의 결과 값 제거-> release 
ID3D11Device* _device; 
ID3D11DeviceContext* _deviceContext;

이거보다는 

ComPtr<ID3D11Device> _device; 자동으로 관리해주는 스마트 포인터가 좋다

 

//Ref카운트조절 코드로 관리-> 안좋다
_device->AddRef();

_device->Release();

 

모두 다른 gpu연동, 관리는 어떻게? -> Com 인터페이스 

ComPtr<ID3D11Device> _device; -> 유닛생산
ComPtr<ID3D11DeviceContext> _deviceContext; -> 유닛에 일시키기

 

궁금한건 구글링하며 찾아보자 

	//DXGI : DX와는 독립적으로 하위 수준 작업 관리 => 시스템,하드웨어 통신
	//스왑체인-> 그리는 것과 보여주는 것 따로 해야 보통 방식 ->전면 후면 고속복사
	ComPtr<IDXGISwapChain> _swapChain = nullptr;

 

	_device.Get();			//내부적 디바이스 ID3D11Device를 가져오고 싶을때
	_device.GetAddressOf();		//그값의 주소값
	//HRESULT 일종의 bool값
	HRESULT hr = ::D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE,			//어떤걸 쓸건지 -> gpu or software로 gpu
		nullptr,
		0,
		nullptr,				//지원하는 dx 버전 레벨 기본값은 내가 지원할 수 있는 상위버전 골라줌 
		0,						//위에 배열 크기
		D3D11_SDK_VERSION,				//매크로
		&desc,
		_swapChain.GetAddressOf(),			//**이면 주소값 가져오는 GetAddressOf()
		_device.GetAddressOf(),
		nullptr,
		_deviceContext.GetAddressOf()
	);
	//성공했는지 체크해준다.
	assert(SUCCEEDED(hr));


오늘 완성 코드

Game.h

#pragma once


class Game
{
public:
	Game();
	~Game();
public:
	void Init(HWND hwnd);		//윈도우 핸들받아줌
	void Update();
	void Render();
private:
	/// <summary>
	/// 그리기 함수
	/// </summary>
	void RenderBegin();		
	void RenderEnd();
private:
	void CreateDeviceAndSwapChain();
	/// <summary>
	/// 버퍼를 묘사 Tag를 달아서 GPU에 알려주기 위함
	/// </summary>
	void CreateRenderTargetView();
	/// <summary>
	/// 뷰포트 묘사 
	/// </summary>
	void SetViewport();
private:
	HWND _hwnd;
	uint32 _width = 0;
	uint32 _height = 0;

private:
	//Device & SwapChain
	// I: 인터페이스 Comptr -> 스마트 포인터- 자동관리해줌, wrl에 있다.
	ComPtr<ID3D11Device> _device; 
	ComPtr<ID3D11DeviceContext> _deviceContext;
	//DXGI : DX와는 독립적으로 하위 수준 작업 관리 => 시스템,하드웨어 통신
	//스왑체인-> 그리는 것과 보여주는 것 따로 해야 보통 방식 ->전면 후면 고속복사
	ComPtr<IDXGISwapChain> _swapChain = nullptr;

	///RTV 렌더타켓뷰
	ComPtr<ID3D11RenderTargetView> _renderTargetView;

	//Misc 
	D3D11_VIEWPORT _viewport = { 0 };
	float _clearColor[4] = { 0.5f,0.5f,0.5f,0.5f };
	
};

 

 Game.cpp

#include "pch.h"
#include "Game.h"

Game::Game()
{
}

Game::~Game()
{
}

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_width = GWinSizeX;
	_height = GwinSizeY;

	//TODO
	CreateDeviceAndSwapChain();
	CreateRenderTargetView();
	SetViewport();
}

void Game::Update()
{

}

void Game::Render()
{
	RenderBegin();			//초기화

	//TODO : 그리기


	RenderEnd();			//제출
}


void Game::RenderBegin()		//랜더링 파이프라인에 우리가 만든거 묶어주기
{
	_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), nullptr);				//OM: OUTPUT Mege
	_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), _clearColor);		//색상으로 초기화해주기
	_deviceContext->RSSetViewports(1, &_viewport);
}

void Game::RenderEnd()
{
	HRESULT hr = _swapChain->Present(1, 0);			//제출 전면 <= 후면 
	CHECK(hr);
}

void Game::CreateDeviceAndSwapChain()
{
	DXGI_SWAP_CHAIN_DESC desc;
	ZeroMemory(&desc, sizeof(desc));		//다 0으로 초기화해줌 필요한 것만 따로 초기화
	{
		desc.BufferDesc.Width = _width;		//버퍼도 같은 크기로 800 x 600
		desc.BufferDesc.Height = _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 일종의 bool값
	HRESULT hr = ::D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE,			//어떤걸 쓸건지 -> gpu or software로 gpu
		nullptr,
		0,
		nullptr,				//지원하는 dx 버전 레벨 기본값은 내가 지원할 수 있는 상위버전 골라줌 
		0,						//위에 배열 크기
		D3D11_SDK_VERSION,				//매크로
		&desc,
		_swapChain.GetAddressOf(),			//**이면 주소값 가져오는 GetAddressOf()
		_device.GetAddressOf(),
		nullptr,
		_deviceContext.GetAddressOf()
	);

	CHECK(hr);
}

void Game::CreateRenderTargetView()
{
	HRESULT hr;

	ComPtr<ID3D11Texture2D> backBuffer = nullptr;
	hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());		//백버퍼 텍스처(PNG)로 가져오기
	CHECK(hr);

	//택스처라는건 그냥 쓰면 아무것도 없는데 거기에 최종 그림을 그리는 곳에 쓸거라는 태그를 달아주는 것 
	_device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());  //렌더타켓뷰라는것으로 텍스처를 명시해 만들어준다.
	CHECK(hr);
	
}

void Game::SetViewport()
{
	_viewport.TopLeftX = 0.f;
	_viewport.TopLeftY = 0.f;
	_viewport.Width = static_cast<float>(_width);
	_viewport.Height = static_cast<float>(_height);
	_viewport.MaxDepth = 0.f;
	_viewport.MaxDepth = 1.f;
}

 

오늘의 결과화면

함수 단위로 크게크게 보자

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]4. 삼각형 그리기  (1) 2024.07.01
[Directx11]2.초기설정  (0) 2024.06.26
[Directx11]그래픽스OT  (0) 2024.06.25
[Directx11]2.랜더링 파이프라인2  (0) 2024.06.21
[Directx11]1. 렌더링 파이프라인-1  (0) 2024.06.19

https://www.youtube.com/watch?v=Dh5ty_W8Ayg&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=19

오늘은 아이템슬롯에 클릭했을 때 무기를 장비할 수 있게하는 걸 구현해보자 

 

1. 캐릭터 이벤트그래프에서 커스텀 이벤트 추가

sword를 Equipped Weapon으로 바꿔서 이것의 static mesh를 바꿔주는 방식으로 만들 것이다.

선택한 아이템을 DB에서 찾아서 이에 해당하는  static mesh를 가져와서 그것으로 기존 weapon의 mesh를 바꿔준다. 

 

2. 기존 아이템슬롯 assign 이벤트 마지막에 1의 이벤트 추가

1번에서 만든 이벤트를 아이템 클릭할 때 작동할 수 있도록 assign item에서 추가해준다. 

 

3. 크기가 다른 무기를 Socket으로 맞춰주기 

기존 무기 구조체에서 변수를 하나 추가해서 Socket에 맞게 들어가도록 해주었다.

 

4. 카테고리 만들어주기 

각 아이템슬롯 자체에 기존에 만들어둔 카테고리 변수를 추가해서 어떤 아이템슬롯인지 전달할 수 있도록 한다.

그리고 Construct 될때가 아니라 custom event로 호출할 때만 아이템 목록이 뜨도록 한다. 

그리고 과정 중에 해당 카테고리만 가져오도록 bool 조건과 branch를 사용하여 준다.

카테고리에 해당하는 무기를 가져오는 과정에서 기존에 있던 목록이 계속 중첩되어 뜰 수 도 있어서 장비UI의 Open 이벤트에서 Item Vertical Box의 Children을 clear 해준다. 

 

오늘의 결과화면 

 

x값을 따른 원사이에 있는 최소y값과 최대 y을 계산하고 이를 대칭성을 통해 계산하면 되는 문제이다. 

 

#include <string>
#include <vector>
#include <cmath>
using namespace std;
//한 사분면의 값을 찾아서 4배 시켜주기 
long long solution(int r1, int r2) {
    long long answer = 0;
    
    for(int i=1;i<=r2;i++){
        int maxY = floor(sqrt((long long)r2 * r2 - (long long)i * i));            //최대 Y값 내림해야 경계안쪽의 값까지 구할 수 있다.
        int minY = ceil(sqrt((long long)r1 * r1 - (long long)i * i));             //최소 Y값 올림해야 경계바로 바깥쪽까지의 값을 구할 수 있다.
        
        if(r1<i) minY=0;            //x값이 r1보다 크면 사이이여서 최소y는 0부터 시작
        
        answer+=(maxY-minY+1);      // 
        
    }
    return answer*4;
}

 

1. 프로젝트 생성

프로젝트는 Visual Studio의 Windows 데스크톱 애플리케이션으로 만들어주면 된다.

 

2.필터로 파일정리 

헤더파일도 소스파일안에 필터폴더로 관리하는게 좋다.

 

프리 컴파일 설정

공용으로 쓸 헤더파일을 만들고 그것을 프리 컴파일하도록 설정

 

 

 

Types -자주 사용하게될 여러가지 변수 타입

Values - 윈도우 사이즈같은 고정 변수 값

Struct - 자주 사용하게 될 구조체 타입 저장

pch - 모든 헤더파일을 가지고 있는 헤더

 

3. 프로젝트 속성 변경

 

출력디렉토리 설정 및 라이브러리 추가를 대비하여 라이브러리 받아올 경로 설정

 

 

외부라이브러리 가져올 때 마지막 설정은 설정창에서 할 수 도 있지만 코드로 제어하는게 편하다.

#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dcompiler.lib")

#ifdef _DEBUG
#pragma comment(lib,"DirectXTex\\DirectXTex_Debug.lib")
#else
#pragma comment(lib,"DirectXTex\\DirectXTex.lib")
#endif

 

코드 정리

  // 기본 메시지 루프입니다: -> GetMessage는 게임에 
  while (GetMessage(&msg, nullptr, 0, 0))
  {
      if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
      {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
  }

//대신 사용할거 

   // 기본 메시지 루프입니다:
   while (msg.message!=WM_QUIT)
   {
       if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
       else //게임 프레임워크 시작
       {
           game.Update();
           game.Render();
       }
   }

 

게임 클래스를 만들어줘서 그곳에서 만드는 것으로 작동하도록 초기화해주기 

Game.h 및 Game.cpp

#pragma once


class Game
{
public:
	Game();
	~Game();
public:
	void Init(HWND hwnd);		//윈도우 핸들받아줌
	void Update();
	void Render();
private:
	HWND _hwnd;
	uint32 _width = 0;
	uint32 _height = 0;
};
#include "pch.h"
#include "Game.h"

Game::Game()
{
}

Game::~Game()
{
}

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_width = GWinSizeX;
	_height = GwinSizeY;

	//TODO
}

void Game::Update()
{
}

void Game::Render()
{
}

 

전체코드 

#include "pch.h"
#include "framework.h"
#include "GameCoding.h"
#include "Game.h"

#define MAX_LOADSTRING 100

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
HWND hWnd;

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
  

    // 1) 윈도우 창 정보 등록
    MyRegisterClass(hInstance);

    // 2) 윈도우 창 생성
    if (!InitInstance (hInstance, nCmdShow))
        return FALSE;

    Game game;
    game.Init(hWnd);

    MSG msg = {};

    // 기본 메시지 루프입니다:
    while (msg.message!=WM_QUIT)
    {
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else //게임 프레임워크 시작
        {
            game.Update();
            game.Render();
        }
    }

    return (int) msg.wParam;
}



//
//  함수: MyRegisterClass()
//
//  용도: 창 클래스를 등록합니다.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    //밑의 정보를 이용
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GAMECODING));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;     //옵션메뉴
    wcex.lpszClassName  = L"GameCoding";        //이 키를 이용하여 등록
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   함수: InitInstance(HINSTANCE, int)
//
//   용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
//   주석:
//
//        이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
//        주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)        //키를 바탕으로 정보를 통해 창을 만들어줌
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   //윈도우 사이즈 조절 
   RECT windowRect = { 0,0,GWinSizeX,GwinSizeY };
   //메뉴는 사이즈에 공간에 포함하지 않도록
   ::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, false);

   //나중에도 써야해서 전역변수로 만들어주기
   hWnd = CreateWindowW(L"GameCoding", L"Client", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, windowRect.right-windowRect.left,windowRect.bottom-windowRect.top, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   //::은 사용자 정의x 표준에서 가져옴
   ::ShowWindow(hWnd, nCmdShow);
   ::UpdateWindow(hWnd);

   return TRUE;
}

//
//  함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  용도: 주 창의 메시지를 처리합니다.
//
//  WM_COMMAND  - 애플리케이션 메뉴를 처리합니다.
//  WM_PAINT    - 주 창을 그립니다.
//  WM_DESTROY  - 종료 메시지를 게시하고 반환합니다.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 메뉴 선택을 구문 분석합니다:
            switch (wmId)
            {
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

 

결과 화면

무슨 물체를 띄우든 렌더링 파이프라인을 한번 거쳐야해서 함수가 많고 복잡하다

 

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]4. 삼각형 그리기  (1) 2024.07.01
[Directx11]3.장치초기화  (0) 2024.06.27
[Directx11]그래픽스OT  (0) 2024.06.25
[Directx11]2.랜더링 파이프라인2  (0) 2024.06.21
[Directx11]1. 렌더링 파이프라인-1  (0) 2024.06.19

https://www.youtube.com/watch?v=Bu-CiOvvYgY&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=18

오늘은 슬롯을 클릭했을 때 나오는 리스트목록을 클릭했을 때 해당 아이템이 슬롯에 표시되는 것을 구현해보자

 

1.캐릭터에 Spring Arm 및 SceneCaptureComponent2D추가 해주기 - 장비화면에서 가운데 캐릭터가 보일 수 있도록

 

이것을 통해 액터만 카메라에 보이게 한다. 배경 벽 제외 -> 캐릭터만 보일 수 있도록

Spring Arm 회전을 줘서 플레이어 앞에 위치하도록
렌더 모드를 변경하여 원하는 것만 해당 카메라에 보여줄 수 있도록 캐릭터가 생성될 때 해당액터의 부분만 보이도록 설정
고정된 카메라이고 멈추지않도록 하기위해 콜리전테스트실행 비활성화

 

SceneCaptureComponent2D의 텍스처 타킷을 장비 시스템 UI 폴더에 새롭게 만들어준다.

 

그리고 실행시켜보면 

게임실행 시 텍스처화면

이런식으로  해당 액터만 나오게 된다.

하늘을 지우기 위해

만들어진 텍스처를 우클릭해서 새 머터리얼 생성을 한 뒤 도메인과 모드를 바꿔주고 함수 흐름을 이렇게 바꿔주면 

 

머터리얼 설정
위의 설정이 적용된 화면

 

이제 만든 텍스처를 장비시스템 UI에 붙여주면 된다. 

가운데에 딱 맞게 이미지가 줄어들지 않도록 Scale Box를 만든 뒤 자식으로 넣어준다. 

이때 가운데에 맞도록 정렬값은 0.5 0.5가 되도록 설정한다.

인벤토리 UI설정


또한 SpringArm의 길이를 줄여서 UI를 켰을 때 적당한 크기로 캐릭터가 나오도록 조절해준다.

그리고 인벤토리 UI를 켰을 때는 UI를 켰다는 효과를 주기위해 뒤에 배경은 Blur처리와 불투명하게 이미지를 깔아준다.

이때 앵커는 모두를 덮어쓸 수 있도록 하며 나머지 값들은 0으로 해준다. 

 

2. 슬롯 선택시 적용되게 기능 추가

일단 Equipable Slot 그래프에 현재 장비한 슬롯 변수를 초기화해주는 코드를 추가해준다. 

그리고 장비슬롯에 선택된 장비의 이미지가 들어올 수 있도록 이미지를 추가해준다.

 

장비 UI의 그래프에서 커스텀 이벤트를 추가해서 해당 장비슬롯에 선택한 장비의 이미지가 들어올 수 있도록 만들어주고 이것을 Item Slot에서 호출하도록 한다. 

Equipment UI의 커스텀 이벤트-> 해당하는 아이템을 찾아서 그 아이템의 이미지로 장비슬롯의 이미지를 바꿔준다.
장비 UI에서 마지막에 장비슬롯의 Item 변수를 초기화 해주는 부분
아이템슬롯 그래프에서 만약 클릭했을 때 클릭한 아이템으로 커스텀이벤트 호출

 

완성!

https://school.programmers.co.kr/learn/courses/30/lessons/176962

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

이 문제에서 중요한 점은

1.시간순으로 과제를 정렬

2.stack 안에 pair 자료형으로 멈춰진 과제를 저장할 변수를 지정

3.과제를 진행하면서 멈춘 과제를 저장하고 할 수 있는 과제를 진행

-> stack과 pair를 적절히 잘 써야한다.

#include <string>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;

int Timecalc(string  t) {
    return (60 * stoi(t.substr(0, 2))) + stoi(t.substr(3));
}
//시간순으로 넣고 만약 겹치는거 처리
bool cmp(vector<string> a, vector<string> b) {
    return Timecalc(a[1]) < Timecalc(b[1]);
}
vector<string> solution(vector<vector<string>> plans) {
    vector<string> answer;
    stack<pair<string, int>> pause_plan;

    sort(plans.begin(), plans.end(), cmp);        //시간순으로 정렬

    int curTime = Timecalc(plans[0][1]);
    int nextTime = Timecalc(plans[1][1]);
    int SumSub = 0;           //지금까지 수행한 과제합
    while (SumSub < plans.size() || !pause_plan.empty()) {
        //만약 정지해둔 과제가 있다면
        if (!pause_plan.empty()) {
            //만약 마지막순서의 과제까지 다 했다면 멈춰둔 과제 수행
            if (SumSub == plans.size()) {
                answer.push_back(pause_plan.top().first);
                pause_plan.pop();
                continue;
            }
            //만약 다음에 수행해야할 과제까지 시간이 남아있다면
            if (curTime < nextTime) {
                int pauseTime = pause_plan.top().second;      //과제 수행해야하는 시간
                int availableTime = nextTime - curTime;        //과제 수행가능 시간

                if (pauseTime <= availableTime) {       //만약 남은 시간동안 멈춘과제를 수행가능하다면
                    answer.push_back(pause_plan.top().first);
                    pause_plan.pop();
                    curTime += pauseTime;
                }
                else {
                    pause_plan.top().second = pauseTime - availableTime;
                    curTime = nextTime;
                }
                continue;
            }
        }
        curTime = Timecalc(plans[SumSub][1]) + stoi(plans[SumSub][2]);      //과제수행
        nextTime = SumSub + 1 >= plans.size() ? 1440 : Timecalc(plans[SumSub + 1][1]);

        if (curTime > nextTime) {       //만약 과제수행하는 중에 새로운 과제가 들어온다면
            pause_plan.push({ plans[SumSub][0],curTime - nextTime });       //과제명 : 과제수행완료까지 남은시간
            curTime = nextTime;
        }
        else {
            answer.push_back(plans[SumSub][0]);
        }

        SumSub++;
    }


    return answer;
}

https://www.inflearn.com/course/directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98/dashboard

 

[게임 프로그래머 도약반] DirectX11 입문 | Rookiss - 인프런

Rookiss | 게임 프로그래머 공부에 있어서 필수적인 DirectX 11 지식을 초보자들의 눈높이에 맞춰 설명하는 강의입니다., [사진][사진] [사진] 게임 개발자는 Unreal, Unity만 사용할 줄 알면 되는 거 아닌

www.inflearn.com

 

1. CPU vs GPU

CPU : 고급인력 - 연산 중심, 범용적으로 모든 업무를 담당

GPU : 연산 중심 - ALU가 엄청 많지만 저장하는 공간은 비교적 작다

ex) 울트라 vs 저글링 -> 퀄리티 vs 수량

-> 중요한 연산은 cpu가 복잡하지 않지만 많은 연산은 gpu

gpu 사용되는 곳 - 암호화, 게임, 인공지능

 

Q) 서버  프로그래밍에서 mmorpg를 개발하고 있다면 gpu 사용할까?

A) 사용하지 않는다. 무조건 gpu를 쓴다고 좋은게 아니다. 병렬로 처리한 값을 다시 받아야하기도 하고

각 업무가 독립적이여야한다. 다시 동기화해서 받아오는 것도 어렵다.

클라이언트에서는 당연히 사용된다.

 

RAM,SSD- 데이터 저장

ALU - 코어. Cache - 임시 저장

 

2.게임 화면

800 x 600 -> 픽셀들의 조합 

물체 - 많은 삼각형들의 조합 - 많은 연산 - gpu가 적

 

unity

기존 물체 - 로컬 영역- 좌표고정

게임씬에 배치 - 월드  좌표 - 좌표 이동, 회전 가능

카메라

-뷰포트에 따라 달라진다. -> 어떻게 보는

-조명에 따라 달라진다.

-실시간 렌더링- 실시간 움직임에 따라 보이는 것이 바뀌어야 한다.

 

3.GPU 랜더링 파이프라인

 

파란부분 - 코딩 불가 - 옵션으로 제어

녹색 - 코딩으로 제어 가능

※기억해야할 것 -Input-Assembler , Vertex Shader, Rasterizer,Pixel Shader, Output-Merger

 

Input-Assembler

-물체에 대한 기하학적인 정보를 받아준다. 

Vertex Shader

-정점에 대한 연산 - 로컬영역에 있는 물체 - 월드좌표에 어떻게 배치할지

-행렬연산- 정점 단위 

Rasterizer

-삼각형- 정점단위- 보간하는 연산

-내부영역이면 보간 진행

Pixel Shader

-픽셀 단위 - 조명에 따른 색상을 결정

-셰이더가 그림의 풍을 정한다.

Output-Merger

-결과물을 보여준다.

 

랜더링, 코어와 관련된 기능들을 작업해둔 뒤 모아서 만든 툴 셋 - 게임 엔진

 

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]4. 삼각형 그리기  (1) 2024.07.01
[Directx11]3.장치초기화  (0) 2024.06.27
[Directx11]2.초기설정  (0) 2024.06.26
[Directx11]2.랜더링 파이프라인2  (0) 2024.06.21
[Directx11]1. 렌더링 파이프라인-1  (0) 2024.06.19

+ Recent posts