평생 공부하는 빠타박스 블로그 : Learning is Happiness
article thumbnail
SMALL

ImGui 활용한 DirectX 11 3D를 이용한 2D를 만드는 내용입니다.
본 내용은 똑같이 사용할 수 없음을 알립니다. (참고및 공부자료로 만들어진 내용임을 밝힙니다.)
전 과정에 대해 이해를 해야 다음 이 부분에 대한 이해를 할 수 있음을 밝힙니다. 
이전과정) https://ppatabox.tistory.com/34

목차)

1)Array

2)Scale 

 

이번시간 다룰 것들

Array/UpdateSubResoucre/벡터/메모리단편화/memcpy

 

1) Array

 

일단

ConstantBuffer부터 만들자 관리할 수 있도록 

Buffer.h에다만들자 

 

지난시간UpdateSubResource[각주:1]

를 써봤는데 실제로는 이걸 사용하시 않고 다이나믹을 사용한다.

 

 DYNAMIC 다이나믹[각주:2] 을 사용한다. 

지난시간 

하면 서 월드 부분에서 41, 42, 43 이렇게 있었는데

동작이 되질 않았었다. 

43에 쓰레기 값이 들어가 있어서 동작을 안했던것 

 

Translation 자체가 41.42.43에 값을 넣어준다.

D3DXIdentity가 이것을 해주고 값을 넣어준다. 

 

Buffer.h

#pragma once

class VertexBuffer
{
public:
	ID3D11Buffer* GetBuffer() { return buffer; } //멤버

public:
	VertexBuffer(void* data, UINT count, UINT stride, bool bCpuWrite = false, bool bGpuWrite = false);
	~VertexBuffer();

	void Render();

private:
	ID3D11Buffer* buffer;

	void* data;
	UINT count;
	UINT stride;
	
	bool bCpuWrite;
	bool bGpuWrite;
};

///////////////////////////////////////////////////////////////////////////////

class ConstantBuffer
{
public:
	ConstantBuffer(void* data, UINT dataSize); //생성자에 들어갈 것 
	~ConstantBuffer(); //소멸자

	ID3D11Buffer* Buffer() { return buffer; } //버퍼도 리턴해주기 위함 

	void Render(); //렌더 

private:
	ID3D11Buffer* buffer; //버퍼 

	void* data; //쓸데이터
	UINT dataSize; //쓸데이터의 사이즈 
};

 

Buffer.cpp 

#include "stdafx.h"
#include "Buffer.h"

VertexBuffer::VertexBuffer(void * data, UINT count, UINT stride, bool bCpuWrite, bool bGpuWrite)
	: data(data), count(count), stride(stride)
	, bCpuWrite(bCpuWrite), bGpuWrite(bGpuWrite)
{
	D3D11_BUFFER_DESC desc = { 0 };

	if (bCpuWrite == false && bGpuWrite == false)
	{
		desc.Usage = D3D11_USAGE_IMMUTABLE;
	}
	else if (bCpuWrite == true && bGpuWrite == false)
	{
		desc.Usage = D3D11_USAGE_DYNAMIC;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; //RW - Map
	}
	else if (bCpuWrite == false && bGpuWrite == true)
	{
		desc.Usage = D3D11_USAGE_DEFAULT; //W - UpdateSubResource
	}
	else
	{
		desc.Usage = D3D11_USAGE_STAGING;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
	}

	desc.ByteWidth = count * stride;
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

	D3D11_SUBRESOURCE_DATA subResource = { 0 };
	subResource.pSysMem = data;

	HRESULT hr = Device->CreateBuffer(&desc, data != NULL ? &subResource : NULL, &buffer);
	assert(SUCCEEDED(hr));
}

VertexBuffer::~VertexBuffer()
{
	SAFE_RELEASE(buffer);
}

void VertexBuffer::Render()
{
	UINT offset = 0;
	DeviceContext->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
}

///////////////////////////////////////////////////////////////////////////////

ConstantBuffer::ConstantBuffer(void * data, UINT dataSize) 
	: data(data), dataSize(dataSize) //
{
	D3D11_BUFFER_DESC desc = { 0 }; //버퍼 생성할려고 하는 것 
	desc.Usage = D3D11_USAGE_DYNAMIC; //다이나믹은 CPU_ACCESS_WIRTE들어감 
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc.ByteWidth = dataSize; //데이터 사이즈 들어감 
	desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; // 

	HRESULT hr = Device->CreateBuffer(&desc, NULL, &buffer); //기본 널에 버퍼 
	assert(SUCCEEDED(hr));
}

ConstantBuffer::~ConstantBuffer()
{
	SAFE_RELEASE(buffer); 
}

void ConstantBuffer::Render()
{
	D3D11_MAPPED_SUBRESOURCE subResource; //데이터를 밀어주기 정점 바꿔주는 것
	DeviceContext->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource); //
	{
		memcpy(subResource.pData, data, dataSize); //멤카피 GPU메모리 영역 (우리데이터 복사(데이터사이즈만큼)) 
	}
	DeviceContext->Unmap(buffer, 0);

	DeviceContext->VSSetConstantBuffers(0, 1, &buffer); //버퍼가 들어감 
}

 

Rect로가서 

(물체하나가 월드 하나를 가진다)  그래서 Rect 물체에 넣을 것이다. (월드를) 

Rect.h

#pragma once

class Rect
{
public:
	Rect();
	~Rect();

	void Position(float x, float y, float z = 0);
	void Position(const D3DXVECTOR3& position);
	D3DXVECTOR3 Position();

	void Render();

private:
	float GetWidth(float position) { return position / (float)Width * 2.0f - 1.0f; }
	float GetHeight(float position) { return -(position / (float)Height * 2.0f - 1.0f); }

private:
	struct Vertex
	{
		D3DXVECTOR3 Location;
		D3DXCOLOR Color;

		Vertex()
		{
			Location = D3DXVECTOR3();
			Color = D3DXCOLOR();
		}

		Vertex(D3DXVECTOR3 location, D3DXCOLOR color)
		{
			Location = location;
			Color = color;
		}
	};

	struct WVP //World View Projection 의미 
	{
		D3DXMATRIX World;
		D3DXMATRIX View;
		D3DXMATRIX Projection;
	} wvp; //변수선언 

private:
	Shader* shader;

	Vertex vertices[6];
	VertexBuffer* vertexBuffer;
	ConstantBuffer* wvpBuffer; //
};

 

Rect.cpp 

#include "stdafx.h"
#include "Rect.h"

Rect::Rect()
{
	float minusX = -50;
	float plusX = +50;

	float minusY = -50;
	float plusY = +50;
	

	vertices[0] = Vertex(D3DXVECTOR3(minusX, minusY, 0), D3DXCOLOR(0, 1, 0, 1));
	vertices[1] = Vertex(D3DXVECTOR3(minusX, plusY, 0), D3DXCOLOR(1, 0, 0, 1));
	vertices[2] = Vertex(D3DXVECTOR3(plusX, minusY, 0), D3DXCOLOR(0, 0, 1, 1));

	vertices[3] = Vertex(D3DXVECTOR3(plusX, minusY, 0), D3DXCOLOR(0, 0, 1, 1));
	vertices[4] = Vertex(D3DXVECTOR3(minusX, plusY, 0), D3DXCOLOR(1, 0, 0, 1));
	vertices[5] = Vertex(D3DXVECTOR3(plusX, plusY, 0), D3DXCOLOR(1, 0, 0, 1));


	D3D11_INPUT_ELEMENT_DESC layoutDesc[] =
	{
		"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
	};
	UINT elementCount = ARRAYSIZE(layoutDesc);


	shader = new Shader(L"effect.hlsl");
	shader->CreateInputLayout(layoutDesc, elementCount);

	vertexBuffer = new VertexBuffer(vertices, 6, sizeof(Vertex));
	wvpBuffer = new ConstantBuffer(&wvp, sizeof(WVP)); //들어갈 데이터가 주소 , 데이터사이즈 

	//초기화 해놓고 
	D3DXMatrixIdentity(&wvp.World);
	D3DXMatrixIdentity(&wvp.View);
	D3DXMatrixIdentity(&wvp.Projection);


	D3DXVECTOR3 eye(0, 0, -1);
	D3DXVECTOR3 lookAt(0, 0, 0);
	D3DXVECTOR3 up(0, 1, 0);

	D3DXMatrixLookAtLH(&wvp.View, &eye, &lookAt, &up); //main.cpp에서 가져온것 수정 
	D3DXMatrixTranspose(&wvp.View, &wvp.View);

	D3DXMatrixOrthoOffCenterLH(&wvp.Projection, 0, (float)Width, 0, (float)Height, -1, +1);
	D3DXMatrixTranspose(&wvp.Projection, &wvp.Projection);

}

Rect::~Rect()
{
	SAFE_DELETE(vertexBuffer);
	SAFE_DELETE(wvpBuffer); // 지워야 된다. 

	SAFE_DELETE(shader);
}

void Rect::Position(float x, float y, float z)
{
	Position(D3DXVECTOR3(x, y, z));
}

void Rect::Position(const D3DXVECTOR3 & position)
{
	D3DXMatrixTranslation(&wvp.World, position.x, position.y, position.z);
	D3DXMatrixTranspose(&wvp.World, &wvp.World);
}

D3DXVECTOR3 Rect::Position()
{
	D3DXMATRIX matrix;
	D3DXMatrixTranspose(&matrix, &wvp.World);

	return D3DXVECTOR3(matrix._41, matrix._42, matrix._43);
}

void Rect::Render()
{
	DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	shader->Render();
	vertexBuffer->Render();
	wvpBuffer->Render(); //버퍼도 렌더링 해서 값 밀어주고 

	DeviceContext->Draw(6, 0);
}

 

main.cpp에서 이제 렉트를 만드는 것을 10개로 바꿀 것이다. 

Rect* rect[10];

 

그럼 지금 이것은 10개로 바뀐 것인데. (해당 그림이랑 좀 다름) 

 

Rect* rect[10];

void InitScene()
{
	for(int i = 0; i < 10; i++)
    	rect[i] = new Rect(); //Rect i 에 대한 new rect 
}

월드에 포지션을 사용할 수 있도록 할것인데.

 

Rect.h에서 

 

 

 

일반적으로 구조체의 변수를 보낼때는 레퍼런스를 쓰면 

복사방지가 된다. 

그쪽 주소를 참조하게 되니까

 

따로 만들지 않고 

복사방지를 하는데 만약에 얘가 참조니까 이 함수내에서 원본값이 바뀌어버리는 건데

그래서 그걸 막으라고 상수화 시킴 

 

	void Position(float x, float y, float z = 0); //0넣어줘서Default파라미터 사용가능 
	void Position(const D3DXVECTOR3& position); //구조체에서 변수선언시 레퍼런스 사용할때 const로 상수화 
	D3DXVECTOR3 Position();

 

복사방지 레퍼런스를 하는데 원본값은 건드릴 수 없도록 상수화 시킴 

그래서 일반적으로 구조체를 넘길때 이렇게 생각하면 됨 

 

 D3DXVECTOR3&

구조체에는 포인터 보다는 레퍼런스를 쓰는 이유가 포인터는 이 주소가 NULL 일수도 있기에 

근데 참조는 NULL일수가 없다. 거기 공간에 별명이 붙는 것이기에 

 

포인터는 주소공간을 가리키는 것이 되기 때문에 가리킴을 당하는애가 없을 수도 있으니까.

 

댕글리포인터(Dangling Pointer) [각주:3]

 

이럴때는 NULL체크를 다시한번 해줘야 하기 때문에 이럴경우 포인터를 따로 쓰지 않고

레퍼런스를 사용한다. 

 

포인터는 리턴형으로 쓴다. 

void Position(D3DXVECTOR3* outPosition);

 

Rect.cpp로가서 

void Rect::Position(float x, float y, float z)
{
	Position(D3DXVECTOR3(x, y, z)); //단지 밑에것을 콜할 것임
}

void Rect::Position(const D3DXVECTOR3 & position)
{
	D3DXMatrixTranslation(&wvp.World, position.x, position.y, position.z); //실제로 설정할 것/ 월드값 (z는 그냥둠) 
	D3DXMatrixTranspose(&wvp.World, &wvp.World); //행렬을 만들때 쉐이더 넘기는 애들은 항상 Transpose를 해주자 
}

D3DXVECTOR3 Rect::Position() //위치니까 
{
	
	D3DXMATRIX matrix; //Trandpose되어있으니까 
	D3DXMatrixTranspose(&matrix, &wvp.World); //&matrix 다시 Transpose해줘야 함 
    //그럼 행우선으로 바뀌는 데 행우선으로 바뀐 상태에서 빼야함 그래야 41,42,43나옴 

	return D3DXVECTOR3(matrix._41, matrix._42, matrix._43);
}

&matrix 다시 Transpose해줘야 함 
그럼 행우선으로 바뀌는 데 행우선으로 바뀐 상태에서 빼야함 그래야 41,42,43나옴 

(열우선이면 14,24,34,이다) 

다시 main에서 

Rect* rect[10];
void InitScene()
{
	for(int i = 0; i < 10; i++)
    	rect[i] = new Rect(); //Rect i 에 대한 new rect 
        
    for(int i = 0; i < 10; i++_
    {
    	float x = Math::RandomFloat(0, (float)Width);
    	float y = Math::RandomFloat(0, (float)Height);

		rect[i]->Position(x,y);
    }
       
}
void DestroyScene()
{
}
void Update()
{
}
void Render()
{	
	for (int i = 0; i < 10; i++)
	{
		rect[i]->Render();
	}	
}

 

그럼 랜덤하게 잘 나오는 것을 볼 수 있다. 

 

 

랜덤한 것을 할때 사용 할 수 있는 것이 있는데

벡터를 사용하면 된다.

 

배열은 시작에서 크기를 정해버리는데 

벡터는 가변이다 (필요한 만큼 넣다 뺄 수 있다) 

vector<Rect *> rects; //벡터 템플릿 자료형

vector<Rect *> rects; //벡터 이용 
void InitScene()
{
	for(int i = 0; i < 10; i++)
    	rects.push_back(new Rect()); 
        
    for(int i = 0; i < 10; i++_
    {
    	float x = Math::RandomFloat(0, (float)Width);
    	float y = Math::RandomFloat(0, (float)Height);

		rect[i]->Position(x,y);
    }
       
}
void DestroyScene()
{
}
void Update()
{
}
void Render()
{	
	for (int i = 0; i < 10; i++)
	{
		rects[i]->Render(); //rects변경 
	}	
}

 

그럼 또 이또한 똑같이 나오는 것을 볼 수 있다. 

이제는 버튼을 누르면 기존의 있던 데이터를 다제거하고

다시 가변으로 뿌려줄 것이다 (랜덤하게) 

 

리셋을 만들어 줄 것이다.

 

렉트의 갯수를 알 수 있는 방법은 사이즈 라는 애가 있다

가변형 배열에 몇개가 들어가 있는지 알 수 있다.

이게 0보다 크다 기존 데이터가 있다 라는 의미

렉트의 사이즈 만큼 지워 줄 것이다.

 

main.cpp

void Reset(int count)
{
	if (rects.size() > 0) // 가변형 배열 몇개 들어가있는지 알 수 있다(렉트의 갯수) 
	{
		//for (int i = 0; i < rects.size(); i++)
		for(Rect* rect : rects)
			delete rect;

		rects.clear(); //다 비울 것이다. 다시 0개로 바뀜 
	}


	for (int i = 0; i < count; i++) //그렇게 하고 count만큼 새로 만들어 주자 
	{
		rects.push_back(new Rect());

		float x = Math::RandomFloat(0, (float)Width);
		float y = Math::RandomFloat(0, (float)Height);

		rects[i]->Position(x, y);
	}
}

윗부분 main.cpp 

void Reset(int count); //전방선언 (미리선언한) 

vector<Rect *> rects;
void InitScene()
{
	Reset(Math::RandomInt(10, 20));
}

그럼 또한 이렇게 랜덤하게 생성된 것을 볼 수 있다.

 

 

 

(ImGui는 업데이트라 렌더 어디든 쓰여도 된다.)(리셋버튼 만들어줌)

int number = 0; //초기값
void Update()
{
	if(ImGui::Button("Reset"))
	Reset(Math::RandomInt(10, 20));

 

 

 

그렇다면 하나 더 넣어 볼까?

 

움직이는 것을 한번 해보자 (ImGui는 아래사항은 if영역에 들어가면 안된다= 만약에 라는 가정이 되야만 움직인다.) 

 

	ImGui::SliderInt("Number", &number, 0, rects.size() - 1); //갯수 라서 번호는 0번 부터 사이즈부터 -1까지 
		

	D3DXVECTOR3 position = rects[number]->Position(); //이걸 가져올 것 
		
	if (Key->Press('D'))
		position.x += 1; // 조작키 너무 빠르면 줄여도 됨  X
	else if (Key->Press('A'))
		position.x -= 1;

	if (Key->Press('W'))
		position.y += 1; // Y 
	else if (Key->Press('S'))
		position.y -= 1;

	rects[number]->Position(position); //다시 렉트[넘버에다가] 포지션에 넣어주자

 

 

번호키도 만들어 주었고 잘 움직이는 것을 볼 수 있다.

 

 

 

 

 

 

 

 

우리가 앞으로 

vector<Rect *> rects; //삽입 삭제가 빈번하지 않은경우 사용

이런형태로 쓰일 일이 많을 것이다. 

맵 등등 

이런 것을 STL이라고 부른다.

 

STL(Standard Template Library)표준 템플릿 라이브러리[각주:4]

 

 

 

사용방법이 거의 유사하다 

이중에 벡터가 어떻게 동작하는지 면접에 잘 나온다. 

Vector <int> a(10); //예를 들어 10을 넣어주었다

//그런다음 처음에 잡아둔 동적할당 메모리
a.push_back( 어떠한 값 ); //1
a.push_back( 어떠한 값 ); //2
a.push_back( 어떠한 값 ); //3
a.push_back( 어떠한 값 ); //4
a.push_back( 어떠한 값 ); //5
a.push_back( 어떠한 값 ); //6
a.push_back( 어떠한 값 ); //7
a.push_back( 어떠한 값 ); //8
a.push_back( 어떠한 값 ); //9
a.push_back( 어떠한 값 ); //10 동적할당 된 메모리 10개가 다 차버렸다
a.push_back( 어떠한 값 ); //11번째 수행  //그런데 이건 못들어감 

//그러면 슬랙의 크기만큼 공간을 더 만들어준다. 그럼 20개의 공간이 만들어지고
//그러고 나서 기존데이터를 이20개 넓힌 공간에 옮겨준다 
//그렇게 하면 10개가 차고 11에 있는 것을 넣어준다

처음에 벡터를 선언하면 /저런식으로 선언했다 하면 
그러면 슬랙이라는 공간이 잡힌다 

예를 들어 기본 10이라는 값이 잡혔다고 치자 

 

슬랙[각주:5]이라는 것은 얘가 그만큼 찾을때 가변동작을 하기위해서 무언가를 하겠다 라는 의미

어딘가에 더큰 슬랙만큼의 공간을 만들어주고 거기로 데이터를 옮겨주는 방식(내부적으로)

이런 연산이 메모리 단편화라는 문제를 일으킨다. 

메모리가 100사이즈가 있다고 쳐보자 

70짜리가 들어갔는데 40짜리가 들어갈 공간이 없어진다.

이게 메모리 단편화[각주:6]

이게 삭제되고 공간이 남아 있는데 일정시간이 지나야 없어진다

바로바로 그 공간을 사용할 수 없다 점점 뒤로 늘어나다가 이게 다 차버리면서

오버플로우가 일어나면서 게임같은경우는 꺼진다.

그래서 단편화가 중요

 

그래서 위처럼 필연적으로 이렇게 하다보니 나머지 공간이 남아 버리는데 

지워도 남아버리기에 메모리 단편화를 발생시킨다.

그래서 가능한 한 공간을 충분히 확보 시키는 것이 중요하다.

 

결국에 구조가 배열하고 똑같다.

(자료구조) 

list<int> a; //이런식으로 쓰이는 애가 있는데 배열하고 다르다 포인터인데

//삽입삭제가 빈번하고 검색이 많지 않은경우 사용

뒤에 계속 붙이고 필요하면 앞에 꺼 부터 제거한다. 

그래서 위처럼 큰 단위를 갖는 것이 아니기 때문에 단편화가 최소화가 된다.

 

대신에 아까 그것은 배열 구조 라서 다연결되어있는 구조라 검색이 빠른데

list는 포인터 여서 점프 점프 형식으로 간다. 

 

둘다 가변인데 어느걸 언제 쓰냐?

Vector <int> a[각주:7]

list<int> a;[각주:8]

 

 

예를 들어 게임에서 맵의 건물들 이런것은 거의 안바뀌 니까 Vector를 사용

온라인에서 플레이어들 접속한 플레이어들은 동접자 등

왔다 갔다 하니까 list를 사용한다.

 

STL 전체 STL에 분류된 애들은 

특징이 iterator[각주:9]라는 반복자를 가지고 있다. 


우리가 for문을 돌렸을때 

 

//for (int i=0; i< rects.size(); i++)
for(Rect* rect : rects) //Rect*에서 rect : retcs로 순서대로 반복 이 문법이 편함
	delete rect;

 

우리가 for문을 돌려서 지웠었는데

이렇게도 바꿀 수 있다.

지금 이시간 배우는 자료형은 rect에 포인터(rect*) 인데

rect : rects  이런식으로 써준다.

아까랑 똑같음 그냥 순서대로 반복한다. 

 

 

 

그래도 그냥 실행 되는 것을 볼 수 있다. 똑같기 때문에 

 

그런데 이게 또 함정이 있다.

벡터를 쓸때 우리가 임의의로 중간에 하나를 제거한다고 생각해보자 

 

어느 특정한 번호를 지울 것이다.

지우고 나면 0, 1, 2, 3, 4 가 있는데 

2를 지웠다.  그럼 가운데 2가 비는데

 

for문을 돌리는순간에 지울려고 하는 경우

for (int i = 0; size i; i++) //예시 0부터 사이즈 까지 가는것이다. i++로

0 1 하고 지우면서 3, 4, 가 땡겨졌다

그럼 다음꺼를 다룰 수 가 없을 것이다.

 

 

 

 

예를 들어 i = 2이면 2번에 대한 리무버를 했다하면 

i는 2인데 3이 2로 땡겨 졌으니까 3에 대한 어떤 걸 할 수가 없는것 

 

for문으로 어떤 특정한 데이터만 찾아서 지운다고 한다면 

이렇게 돌리면 안된다. (앞에서 부터 돌리면 안되고 뒤에서 부터 돌려야 한다)

 

 

 

어차피 리무브 를 해서 땡겨지면서 처음에 4, 3 이였고 3돌을때 

4쪽에 사이즈가 줄어든다 그러면서 2, 1,을 놓을 수 있다. (뒤에서 부터 생각해야함) 

 

그래서 벡터를 쓸때 중간에 지울때 조심해야 한다. 뒤에서 부터 와야한다.

 

 


한번 다뤄보자

어느 번호를 선택하고 지워버릴 것이다.

이곳안에서 선택된 번호를 지워버릴것이다. 

버튼 선택해야 하니까.

아까 반복자라는게 있다고 말했는데.

반복자를 가져다 쓸것이다.

 

vector<Rect *>::iterator iter = rects.begin() + number;
rects.erase(iter);

원자료형에 대한 iterator  iter= rects.begin() 

iterator반복자가 첫번째 요소를 가리킨다.  지울려고 하는 요소만큼 플러스를 하면 더하기 지울려고 하는 번호 

(배열은 자신의 크기만큼 점프를 한다 int형이면 4바이트점프 bool형이면 1바이트점프 이런식으로)

 

 

if (ImGui::Button("Delete"))
	{
		//vector<Rect *>::iterator iter = rects.begin() + number; //반복자 원자료형에 대한 iterator 만약 
		//rects.erase(iter); //이렇게 쓰면 길기 떄문에 

		rects.erase(rects.begin() + number); //흔히 이렇게 쓴다. 
	}

 

 

(Tip. Debug assertion failed -> 다시시도 -> 문제가 생긴지점으로 가게됨-> 호출스택 (디버그 창 호출스택) 걸린지점까지 어떻게 왔는지 알 수 있음 ./ 디버깅할때 사용법이 있음 ) 참고 

 

 

 

 

 

 

삭제 되는 것을 볼 수 있다. 

 

지워주면서 랜덤카운트도 원래 조정해줘야 한다.

 

 

 

 

 

 

 

 

 

 

 

 

 


사각형 끼리 충돌 되는지 어떻게 알 수 있을까?

우리가 크기가 100이다 .

넓이 100 높이 100

 

어떤 애가 겹쳤다면 이 4개의 점 중하나가 

이 영역 안에 들어가 있는 것인데

if로 판단할 수 있는데

 

가로세로 똑같고 

그렇게 판단하고 닿은애들을 지우는 방식의 예제를 만들어 보시오 

랜덤한 사각형을 뿌릴때 크기도 랜덤하게 


 

 

 

 

 

2) Scale 

이제 우리는 한 사각형의 크기를 1로 잡을 것이다

크기라는 것은 항상 비율이다.(몇배) 

크기를 1로 잡아야 몇배 계산하기가 편한데

(회사마다 크기를  어떤 기준으로 잡을 것이냐에 따라 다르다.) 

 

그래서 Rect의 크기를 1로 만들 것이다.

이곳을  중앙점을 만들어 줄 것이다. 

Vertex(D3DXVECTOR3(-0.5f(이곳을 0으로 시작하면 크기는 1이 되지만 기준점은 좌하단이 되버린다) , -0.5f, 0)

 

우측 그림 처럼 잡는 것이다. 

 

 

Rect.cpp 

	vertices[0] = Vertex(D3DXVECTOR3(-0.5f, -0.5f, 0), D3DXCOLOR(0, 0, 1, 1));RGBA순서 color
	vertices[1] = Vertex(D3DXVECTOR3(-0.5f, +0.5f, 0), D3DXCOLOR(0, 0, 1, 1)); //1, 3
	vertices[2] = Vertex(D3DXVECTOR3(+0.5f, -0.5f, 0), D3DXCOLOR(0, 0, 1, 1)); //2, 4

	vertices[3] = Vertex(D3DXVECTOR3(+0.5f, -0.5f, 0), D3DXCOLOR(0, 0, 1, 1));
	vertices[4] = Vertex(D3DXVECTOR3(-0.5f, +0.5f, 0), D3DXCOLOR(0, 0, 1, 1));
	vertices[5] = Vertex(D3DXVECTOR3(+0.5f, +0.5f, 0), D3DXCOLOR(0, 0, 1, 1));

크기가 1이라서 안보이는데

 

스케일을 적용해야하는데

Translation 이 Identity를 하고 위치를 넣는다

지금도 Identity를 하고 스케일링을 넣기 때문에 바꿔줘야 한다. (둘 중에 하나 밖에 못쓰게 됨) 

 

Rect.h

private:
	D3DXVECTOR3 position;
	D3DXVECTOR3 scale;

스케일은 0이 되면 안된다. 

class Rect
{
public:
	Rect();
	~Rect();

	void Position(float x, float y, float z = 0);
	void Position(const D3DXVECTOR3& position);
	void Position(D3DXVECTOR3* position); //주소를 받아서 자기값을 써줌

	void Scale(float x, float y, float z = 1); //스케일이 0이 되면 안된다 비율이 0이되서 안나온다. 
	void Scale(const D3DXVECTOR3& scale);
	void Scale(D3DXVECTOR3* scale);//주소를 받아서 자기값을 써줌

	void Render();

 

기존 포지션은 이곳에서 해줬는데 좀 바꿔주자 

멤버가 생겨서 이제 가능해짐

멤버가 없어서 임의의 리턴을 해줬는데

이제 주소를 받아서 자기값을 써줄 것이다. 

void Position(D3DXVECTOR3* position);

void Scale(D3DXVECTOR3* scale);

 

Rect.cpp

void Rect::Position(float x, float y, float z)
{
	Position(D3DXVECTOR3(x, y, z));
}

void Rect::Position(const D3DXVECTOR3 & position)
{
	this->position = position; //포지션은 받아서 게산 나중에 한번에 몰아서 

	//D3DXMatrixTranslation(&wvp.World, position.x, position.y, position.z);
	//D3DXMatrixTranspose(&wvp.World, &wvp.World);
}

void Rect::Position(D3DXVECTOR3 * position)
{
	//*position = this->position; //외부에서 주는 주소공간에 접근 그곳에 자신의 Position 이름이 같아서 this 하지만 
	memcpy(position, this->position, sizeof(D3DXVECTOR3)); //이게더 편해서 씀 sizeof 크기만큼 복사해주겠다.
}

void Rect::Scale(float x, float y, float z)
{
	Scale(D3DXVECTOR3(x, y, z));
}

void Rect::Scale(const D3DXVECTOR3 & scale)
{
	this->scale = scale;//자신이 가진 스케일에 스케일을 넣어줌 
}

void Rect::Scale(D3DXVECTOR3 * scale) //scale도 마찬가지 
{
	memcpy(scale, this->scale, sizeof(D3DXVECTOR3));
}

 

이제 렌더링 하기 전에 월드를 계산할 것이다. 

void Rect::Render()
{
	D3DXMATRIX S, T; //공간변환 

	D3DXMatrixScaling(&S, scale.x, scale.y, scale.z); //& 계산을 이곳에 
	D3DXMatrixTranslation(&T, position.x, position.y, position.z); //렌더링 직전에 

	wvp.World = S * T; //월드에는 이 2개를 곱한값 (정말 중요) 
	D3DXMatrixTranspose(&wvp.World, &wvp.World);


	DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	shader->Render();
	vertexBuffer->Render();
	wvpBuffer->Render();

	DeviceContext->Draw(6, 0);
}

 

만약 내가 크기도 적용할려고 스케일링 밑에서 

	D3DXMatrixTranslation(position.x, position.y, position.z); //렌더링 직전에 
    D3DXMatrixScaling(scale.x, scale.y, scale.z); //스케일링 밑에서

이런식으로 하면 

그러면 스케일링이 아이덴티티를 시키고 이미 기존에 있던 월드가 날라감

그래서 행렬곱은 공간변환이라고 했는데

공간변환을 사용한다. 

 

void Rect::Render()
{
	D3DXMATRIX S, T; //공간변환 

	D3DXMatrixScaling(&S, scale.x, scale.y, scale.z); //& 계산을 이곳에 
	D3DXMatrixTranslation(&T, position.x, position.y, position.z); //렌더링 직전에 

	wvp.World = S * T; //월드에는 이 2개를 곱한값 (정말 중요) 
	D3DXMatrixTranspose(&wvp.World, &wvp.World);

이렇게 행렬결합으로 두가지이상의 값이 동시에 적용될 수 있도록 함 

그렇게하고 Transpose시켜주면 

wvp버퍼에서 GPU로 복사되니까.

 

 

main.cpp

vector<Rect *> rects;
void InitScene()
{
	for (int i = 0; i < 10; i++)
	{
		rects.push_back(new Rect());

		rects[i]->Position((float)i * 100.0f, 300.0f); //랜덤하게 
		rects[i]->Scale(50, 50);
	}
		
}

void DestroyScene()
{
	for (Rect* rect : rects) //여기서 저 위에꺼 다 지워줘야 한다.  지울때는 뒤집어 줘야함 
		delete rect;

	rects.clear(); //있는거 그냥 다 지워버림 
}

int number = 0;
void Update()
{
	ImGui::SliderInt("Number", &number, 0, rects.size() - 1);
		

	D3DXVECTOR3 position; //포지션 가져오는 방법 바뀜 
	rects[number]->Position(&position); //종류 3개 포인터로 받는거니까 주소를 넘겨주면됨
		
	if (Key->Press('D'))
		position.x += 1;
	else if (Key->Press('A'))
		position.x -= 1;

	if (Key->Press('W'))
		position.y += 1;
	else if (Key->Press('S'))
		position.y -= 1;

	rects[number]->Position(position);


	D3DXVECTOR3 scale;
	rects[number]->Scale(&scale); //똑같은 방식으로 가져오자 

	ImGui::SliderFloat2("Scale", scale, 1, 100); //벡터 2까지 다룰때 이거많이씀
	rects[number]->Scale(scale); //조정된 값을 가져오면 됨 
}

void Render()
{	
	for (int i = 0; i < (int)rects.size(); i++) //렉츠의 사이즈만큼
	{
		rects[i]->Render();
	}	
}

 

원인이 뜨는 것중에 

Signed 또는 Unsigned 가 일치 하지 않다. 라고뜸 

 

더블클릭해서 보면 

 

이곳이 나오는데 

이것을 

void Render()
{	
	for (int i = 0; i < rects.size(); i++) //\
	{
		rects[i]->Render();
	}	
}
for (int i = 0; i < (int)rects.size(); i++)

Int형으로 바꿔주면 된다. 

rects.size() 이게 Unsigned int형이다 

 

오류없이 깨끗하게 나오는 걸 볼수 있다. 

왠만하며 경고 없는게 중요 

 

 

정상적으로 작동하는 걸  볼수 있다.

 

 

 

 

 

 

 

 

 

다음시간)

 

[C++] DirectX Constant_Buffer

ImGui 활용한 DirectX 11 3D를 이용한 2D를 만드는 내용입니다. 본 내용은 똑같이 사용할 수 없음을 알립니다. (참고및 공부자료로 만들어진 내용임을 밝힙니다.) 전 과정에 대해 이해를 해야 다음 이

ppatabox.tistory.com

 

 


각주)

 

  1. GPU에 어떤 자원을 업데이트 할 때 필요한 함수이며,

    src와 dest가 같은 포맷(단위)이 동일할 때 그 단위로 복사해주는 함수이다.  [본문으로]

  2. enum D3D11_USAGE 
    { 
    D3D11_USAGE_DEFAULT = 0, 
    D3D11_USAGE_IMMUTABLE = 1, 
    D3D11_USAGE_DYNAMIC = 2, 
    D3D11_USAGE_STAGING = 3 
    } D3D11_USAGE;

    기본적으로 보통 DEFAULT 혹은 DYNAMIC 을 사용하게 된다.

     DEFAULT 설정

    - UpdateSubResource 함수를 사용한다. (CPU->GPU 메모리로의 단반향 연산)

    - CPUAccessFlags 옵션은 0이다.

    (CPU접근권한)

    - 업데이트 하려는 데이터 단위가 대응될 경우, UpdateSubResource를 사용하는 것이 좋다.

    - ConstantBuffer는 보통 UpdateSubResource를 사용하는 것과 같다.

     

    DYNAMIC 설정

    - map을 사용한다. (양방향 가능)

    - CPUAccessFlags 옵션은 D3D11_CPU_ACCESS_WRITE 이다.

    - map은 양방향이 가능하기에 GPU 메모리를 Read 해야한다면, 무조건 버퍼를 DYNAMIC으로 만들어 map을 사용하는 수 밖에 없다. [본문으로]

  3. 포인터가 여전히 해제된 메모리 영역을 가리키고 있다  댕글링 포인터가 가리키는 메모리는 더는 유효하지 않다. 댕글링 포인터는 premature free(조숙한 해제, 너무 급한 해제)라고 부르기도 한다.  [본문으로]
  4. 이것은 알고리즘, 컨테이너, 함수자 그리고 반복자라고 불리는 네 가지의 구성 요소를 제공한다.

    STL은 컨테이너와 연관 배열 같은 C++을 위한 일반 클래스들의 미리 만들어진 집합을 제공하는데, 이것들은 어떤 빌트인 타입과도 그리고 어떤 사용자 정의 타입과도 같이 사용될 수 있다. STL 알고리즘들은 컨테이너들에 독립적인데, 이것은 라이브러리의 복잡성을 눈에 띄게 줄여주었다.

    STL은 결과를 템플릿의 사용을 통해 달성한다. 이 접근법은 전통적인 런타임 다형성에 비해 훨씬 효과적인 컴파일 타임 다형성을 제공한다. 현대의 C++ 컴파일러들은 STL의 많은 사용에 의해 야기되는 어떤 추상화 페널티도 최소화하도록 튜닝되었다.  [본문으로]

  5. 1)램 슬랙은 램에 저장된 데이터가 저장매체에 기록될때 나타나는 특성에 따라 붙여진 이름

    2)드라이브 슬랙은 클러스터의 사용으로 인해 낭비되는 공간을 나타내는 용어

    3)파일시스템의 할당 크기와 파티션 크기의 차이로 발생되는 낭비 공간을 파일시스템 슬랙

    4)볼륨 슬랙은 전체 볼륨 크기와 할당된 파티션 크기의 차이로 발생하는 낭비 공간 [본문으로]

  6. RAM에서 메모리의 공간이 작은 조각으로 나뉘어져 사용가능한 메모리가 충분히 존재하지만 할당(사용)이 불가능한 상태를 보고 메모리 단편화가 발생했다고 한다.

    메모리 단편화는 내부 단편화와 외부 단편화로 구분 [본문으로]

  7. 삽입 삭제가 빈번하지 않은경우 [본문으로]
  8. 삽입삭제가 빈번하고 검색이 많지 않은경우 [본문으로]
  9.   C++ 라이브러리는 반복자를 제공하는데 이것을 사용하면 라이브러리의 방식대로 자료구조를 액세스 할 수 있다.따라서 라이브러리가 효과적으로 동작한다는 것을 보장 할 수 있다는 장점이 있다.

    즉, 포인터와 상당히 비슷하며, 컨테이너에 저장되어 있는 원소들을 참조할 때 사용한다.

    추상적으로 말하자면, 반복자란 컨테이너에 저장되어 있는 모든 원소들을 전체적으로 훑어 나갈 때 사용하는, 일종의 포인터와 비슷한 객체라고 할 수 있다. 알고리즘 마다 각기 다른 방식으로 컨테이너를 훑어가기 때문에, 반복자에도 여러가지 종류가 있게 된다. [본문으로]

728x90
728x90
LIST
profile

평생 공부하는 빠타박스 블로그 : Learning is Happiness

@공부하는 PPATABOX

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!