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

ImGui활용한 DirectX 11 3D 를 이용한 2D 를 만드는 내용입니다.
본 내용은 똑같이 사용할 수 없음을 알립니다. (참고및 공부자료로 만들어진 내용임을 밝힙니다.)
[C++ ] DirectX 점과 선과 사각형의 원리/ #define /VSPS 이후 이야기
https://ppatabox.tistory.com/16

목차)
1) Line 실시간 처리 Dynamic
2) 범위지정(rand) Class_Math 만들기

========================================================================

1) Line 실시간 처리 Dynamic

라인을 드로잉 하는 것을 좀 바꿔보자 현재 전시간에 배운것은 실시간으로 라인이 그리는 것을 할 수가 없다. 미리 데이터를 설정해놓고 그리는 것 밖에 안된다.

 

게임은 실시간으로 계속 움직여야 하는데 그걸 이용한 개념을 이해할려면 USAGE 라는 것을 이해해봐야한다.

 

USAGE 에서 F12를 보면

enum D3D11_USAGE 
{ 
	D3D11_USAGE_DEFAULT = 0, 
	D3D11_USAGE_IMMUTABLE = 1, 
	D3D11_USAGE_DYNAMIC = 2, 
	D3D11_USAGE_STAGING = 3 
} D3D11_USAGE; // enum에서 0이나 and에 있는 값이 기본 아무것도 안들어가면 그것들이 기본값으로 들어감


표-1) enum 에 대해서 CPU와 GPU의 처리과정에서
RAM < -- > VRAM 에서 데이터 등을 가져올때
VRAM에서도 GPU를 이용해서 연산해서 가져올 수 있다. ex)알파고, 비트코인 등등 부수적인 것들

  CPU GPU 특성
Default X (RAM->VRAM) 불가 O (VRAM->RAM) 가능 CPU부분(초기화 (CreateBuffer) 할때 넣어주면 그때 한번만 가능
Immutable X (RAM->VRAM) 불가 X (VRAM->RAM) 불가 상수 인데 한번만 밀어넣으면 처음 초기화 시킬때 보내고 더이상 변경이 없을때 거의 이걸 사용 (빠름)
Dynamic O (RAM -> VRAM) 가능 X (VRAM->RAM) 불가 Default와는 속도 거의비슷
/바뀔때 마다 보낼 수 있는 것
Staging(느림) O (RAM -> VRAM) 가능 O (VRAM->RAM) 가능 둘다 가능하나 느림(잘사용안함)


그럼 우리는 Dynamic으로 일단 바꿔주고 이걸로 바꿔 준다고 끝나는게 아니다.
CPU_ACCESS_WRITE를 써주어야 한다.(CPU쪽에서 쓴다라는 말)

1) Line.cpp에서 Dynamic 과 CPUAccess처리
2) main.cpp 처음에 초기화 하고 그냥 Render넣을것
3)Line.h CreateBuffer필요없음 생성자 쪽으로 올리면됨
4)Line.cpp부분에서 void Line::CreateBuffer()부분을 생성자 쪽으로 올린다.
5)Line.h에서 D3DXVECTOR3 Location[MAX_LINE_COUNT]; 실제로는 0이 들어가 있긴하지만
생성자 입장에서는 몰라서 초기화를 진행해준다.
6) Line.cpp 에 생성자 에서 ZeroMemory를 사용한다 (이걸 사용하면 전멤버에 전부다 0 이 들어간다)
7) &data GPU에 초기데이터를 넣어주었다. 우리가 바뀔때 마다 넣어줘야 하니까.
렌더때 계속 바뀐다. 그래서 CPU에서 GPU로 데이터를 보내는 코드가 필요하다.
8) Line.cpp 아래 Render 부분
9) desc.ByteWidth = sizeof(D3DXVECTOR3) * MAX_LINE_COUNT; //처음 VertexBuffer만들때 ByteWidth를 사용하였었다.
Location[ ] 배열 -> RAM에 잡혀있음
VertexBuffer의 공간은 -> VRAM에 영역에 잡혀있음
Location에 있는 값을 VertextBuffer에 주는 것
최초로 값을 준것은 createBuffer할때 Location값을 vertexbuffer에 준것이고 이건 초기화만 되어있는값

전에는 라인의 위치였지만 빼버렸다. location의 위치를 지금 CreateBuffer일때 VertextBuffer한번 복사해 주었다.
이때 MAP이라는 명령이 RAM에 있던 데이터를 GPU로 보낼 준비를 해주는 것
VertexBuffer가 어느영역인지 모르는데 MAP이 걸리면 VertexBuffer의 공간을 다른애들이 침범을 못하게 묶어 버린다.

묶어 놓은 상황에서 Location의 값을 VertexBuffer넣어야 한다. 그런데 MAP이 묶어둔 VertexBuffer의 주소를 알아야 하지 않은가? - VRAM에 있는 주소가 &subResource에 넘어온다 그러면 우리가 필요한 양만큼 VertexBuffer에 복사를 해주면 된다.



※우리가 저 VertexBuffer묶는다고 했는데 MAP을 하고 안풀어주면 그냥 터져버린다.
공간에 접근을 못해서 렌더링 하는데 저기 공간에 접근해야 렌더링을 하는데(그리는것)
이런 묶는 코드를 작성할때는 푸는 코드 부터 먼저 작성 하는 습관을 해야한다.

 D3D11_MAPPED_SUBRESOURCE subResource; 
 DeviceContext-> Map(VertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,&subResource); 
 { 
  	(VertexBuffer 묶인부분) 
 } 
 DeviceContext->Unmap(VertexBuffer, 0); //묶인 버텍스 풀어주는 것

 

 

 

사이에서 묶어주고 Unmap으로 다른애들이 접근할 수 있도록
다시 푸는과정 중간과정은 location데이터를 여기로 복사해준다.
메모리 복사는 memcpy()라는 것을 사용함 메모리카피 약자

 


memcpy(복사를 받을애, GPU쪽에 공간시작주소, 우리꺼의 시작주소(Location), 얼마만큼(sizeof) * 복사해줄것)
작성후 DrawCount = 0; 이 되어야 한다.

// 다 초기화 시키고 GPU로 보내고 그린다음 다시 초기화 시키고
우리가 다시받아와서 다시그려줘야 그래야 선이 실시간으로 움직일수 있기에

그다음 Main으로 넘어가자 line->CreateBuffer(); 이부분은 필요없어서 제거

실행하게 되면 전이랑 비슷하게 출력되는 걸 볼 수 있다.


 

 

 

 



그다음 test용으로 한번 main.cpp에 값을 넣어보자 /아래로 떨어지는 것을 볼 수 있다. 끝도 없이..

void Update()
{ 

} 

float y = 0;

void Render() 
{ 
	y+=0.1f;
    line->Add(100, 200, 500, y)
    line->Add(100, 400, 500, 400)
    line->Add(100, 600, 500, 600)
	
    
    line->Render();
}

 

 

 

 

 

 


10) 이제 다시 main.cpp부분을 바꿔 줄 것이다.
main.cpp 를바꿔 주었다 //실시간으로 작동되는 것을 볼 수 있다.
끝에 GIF에서 작동안하는 부분은 내가 변수를 잘못 입력하였다
line 부분 Start가 들어가있는데 End로 바꿔주어야 한다.

float Start[2] = { 0, 0 };
float End[2] = { 0, 0 };
void Update()
{ 

} 

void Render() 
{ 
	ImGui::SliderFloat2("Start", Start, 0 , 600 );
	ImGui::SliderFloat2( "End", End, 0 , 600 ); 
	line->Add(Start[0], Start[1], End[0], End[1]); 
    //끝에 2개는 
    
    End line->Render();
}

 

 

 

 

 

11) 근데 이거 쓰기 보다는 변경해보자

D3DXVECTOR2 Start = { 0, 0 }; //이것은 배열처럼 사용할 수 있다. 
D3DXVECTOR2 End = { 0. 0 }; //구조체 이긴 한데 내부적으로 배열처리 되어있다.
void Update() 
{ 
} 
void Render() 
{ 
	ImGui::SliderFloat2("Start", Start, 0 , 600 ); 
	ImGui::SliderFloat2( "End", End, 0 , 600 ); 
	line->Add(Start.x, Start.y, End.x, End.y); 
	line->Render(); 
}

 

 

 

 

 

 

 

 

 

 


12) 한개더 진행해 보겠다 main.cpp 이렇게 단축키로 움직이는 것이 보인다.
너무 빠르다고 생각하면 0.1f로 줄여도 됨 (ImGui창에서는 변동은 안보임)

D3DXVECTOR2 Start = { 0, 0 }; 
D3DXVECTOR2 End = { 0, 0 }; 
D3DXVECTOR2 Start2 = { 0, 0 }; 

void Update() 
{ 
	if (Key->Press('D')) 
		Start2.x += 1; 
	else if (Key->Press('A')) 
		Start2.x -= 1; 

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

} void Render()
{ 
	ImGui::SliderFloat2("Start", Start, 0, 600); 
	ImGui::SliderFloat2("End", End, 0, 600); 
	line->Add(Start.x, Start.y, End.x, End.y);
	line->Add(Start2.x, Start2.y, Start2.x + 100, Start2.y); 

	ine->Render(); 
}

 

 

 

 

 

 

 

 

 

 

Line.cpp

#include "stdafx.h" 
#include "System/Device.h" 
#include "Line.h" 

Line::Line() 
{ 

	ZeroMemory(Location, sizeof(D3DXVECTOR3) * MAX_LINE_COUNT);
	//얼마만큼 크기냐, 자료형의 크기 전체) //sizeof크기의 배열이 니까 max_line_count만큼 간다. 

D3D11_INPUT_ELEMENT_DESC layoutDesc[] = { 
	"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 
};

UINT elementCount = ARRAYSIZE(layoutDesc); 
Device->CreateInputLayout(layoutDesc, elementCount, 
VsBlob->GetBufferPointer(), VsBlob->GetBufferSize(), &InputLayout); 

D3D11_BUFFER_DESC desc = { 0 }; 

desc.Usage = D3D11_USAGE_DYNAMIC; //USAGE부분! Default -> Dynamic교체 
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // cpu쪽에서 쓴다CPU ACCESS를 GPU쪽에서 읽어온다는 말 
desc.ByteWidth = sizeof(D3DXVECTOR3) * MAX_LINE_COUNT; //처음 VertexBuffer만들때 ByteWidth를 사용하였었다. 
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 
D3D11_SUBRESOURCE_DATA data = { 0 }; // == D3D11_MAPPED_SUBRESOURCE 비슷한 
data.pSysMem = Location;
Device->CreateBuffer(&desc, &data, &VertexBuffer); 
//data에 이미 data가 들어가있다. 초기화가 } //생성자 쪽으로 올렸음 기존에 CreatBuffer에 있던 

void Line::Add(float x, float y, float x2, float y2) 
{
Location[DrawCount++] = { GetWidth(x), GetHeight(y), 1 };
Location[DrawCount++] = { GetWidth(x2), GetHeight(y2), 1 };
} 

Line::~Line() 
{
VertexBuffer->Release();
InputLayout->Release();
}

void Line::Render() 
{

D3D11_MAPPED_SUBRESOURCE subResource;
DeviceContext-> Map(VertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0,&subResource); 
//CreateBuffer할때 넣어준것 그것과 대응한다고 생각하면됨 
{
memcpy(subResource.pData, Location, sizeof(D3DXVECTOR3) * MAX_LINE_COUNT); 
//이렇게 하고 다그리면 DrawCount는 0 
} 
DeviceContext->Unmap(VertexBuffer, 0); 
UINT stride = sizeof(D3DXVECTOR3); 
UINT offset = 0; 
DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &stride, &offset); 
DeviceContext->IASetInputLayout(InputLayout);
DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); 

DeviceContext->Draw(DrawCount, 0); 

DrawCount = 0; 
// 다 초기화 시키고 GPU로 보내고 그린다음 다시 초기화 시키고 우리가 다시받아와서 다시그려줘야 
//그래야 선이 실시간으로 움직임 

}

 

Line.h

#pragma once

#define MAX_LINE_COUNT 1000 

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

	void Add(float x, float y, float x2, float y2); 
    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: 
	UINT DrawCount = 0; 
	D3DXVECTOR3 Location[MAX_LINE_COUNT]; 
	//실제로 0이 들어가 있긴한데 생성자 입장에서는 몰라서 
	
    ID3D11Buffer* VertexBuffer;
	ID3D11InputLayout* InputLayout; 
};

 

main.cpp


void InitScene 안에 생성자 CreateBuffer이후로 렌더링때 계속 그리도록

#include "stdafx.h"
#include "System/Device.h" 
#include "Text.h" 
#include "Line.h" 

Line* line = NULL; 

void InitScene() 
{
	line = new Line(); 
} 

void DestroyScene() 
{ 
	delete line; line = NULL; 
} 

	D3DXVECTOR2 Start = { 0, 0 }; 
	D3DXVECTOR2 End = { 0, 0 }; 
	D3DXVECTOR2 Start2 = { 0, 0 }; 

void Update() 
{ 

	if (Key->Press('D')) 
		Start2.x += 1; 
	else if (Key->Press('A'))
		Start2.x -= 1;

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

void Render()
{

	ImGui::SliderFloat2("Start", Start, 0, 600); 
	ImGui::SliderFloat2("End", End, 0, 600); 
	line->Add(Start.x, Start.y, End.x, End.y); 
	line->Add(Start2.x, Start2.y, Start2.x + 100, Start2.y);
	
    line->Render(); 

}

 

2) rand Class_Math 형식

 

기존에는 라인을 쓸때는 객체를 만들어 썼는데
근데 객체를 만들지 않고 쓸수 있는 함수를 만들 수 있다
단. 멤버변수를 쓸 수는 없다
그러나 편의기능을 제공하기 위해서 만드는 기능

파일 추가 => 새필터 Utilites 생성 => Project폴더 => 폴더에 Utilities 폴더 생성
=> Utilities 필터에서 => 헤더파일 추가 (Math.h) => (Math.cpp)생성 => 설정

rand()가 값의 범위는 0~32767까지 나온다.
unsigned int = 4byte
short int = 2byte = 0~65535범위 = 2^16 / Unsigned제외하면 -32768~32767 이 나오게 되는데
long int = 8byte (잘 안씀)

이곳에서 마이너스는 버리고 rand()함수가 플러스 범위만 리턴해준다.
내가 원하는 수를 범위안에서 리턴을 받고 싶다면
0 1 2 3 4 5 % 3 하면 어떤 수가 나오든 3보다 작은 수가 나올것이다.

ex) r1 = 2 r2 = 5 일때
rand()안에 나머지로 나눈값을 구하기 위해 출력식은 (5 - 2 + 1) = 0~4까지의 범위가 나오고 여기다가 r1을 한번더 더해주면 시작범위 부터 그 범위가 나올 것이다.

rand에 갖다 대면 int형으로 나오는데 float형은 저걸로 못만드는데 float랜덤을 만들기 위해서 쓰는 식
rand() = 0~32767 => 16진수로 바꿔주면
2진수 - 10진수 - 16진수 알고 있어야한다.
16진수 => 0xF => 15 / 0xFF => 15*15 = 255

win + R = 실행창 => calc (계산기) => 프로그래머 => DEC(10진수) => HEX(16진수)

C에서 주소체계를 다룰때 색상같은 것도 16진수로 다룬다
char() 은 10진수를 다루는데 Unsigned하면 1바이트
둘다 1바이트 이긴 한데
signed 가 붙으면 -128~ 127
unsigned가 붙으면 0~255 => 16진수 FF가 됨
기호로 다루는게 편해지기 때문에 이 코드로 많이 다룬다. 16진수 주소체계 뿐만아니라
게임프로그래밍에서 많이 사용된다.

signed short = 이중에 플러스 범위만 32767 = 7FFF(16진수)

RAND_MAX에 마우스 갖다대면 7FFF가 나옴
어떤 최대범위가 있다고 치자
0~ 32767 인데 0~32767 / 32767 로 나누면 이 값중에 하나를 하면 비율이 된다.
0 ~ 1까지의 비율
그 비율을 만들려고 나눈 것
rand() 에 들어가는게 0~32767 사이의 값이다. 그렇게 하고
diff = r2 - r1 을 빼준다 그다음
diff와 random을 곱해준다.
ex )
r1 = 2.0
r2 = 3.5
3.5 - 2.0 = 1.5
diff = 1.5
random = 0 ~ 1범위
0 ~ 1 * 1.5 를 곱하고
r1 + val 에 더한다 2.0 을

범위가 char만큼의 범위에 원래 값을 더하니까.
2.0 과 3.5의 범위안에 값이 나올 것이다.

생각)
1) 가장 큰수를 가지고 기준을 잡음
0 이면 곱하면 0이고 2 고
0.5 면 가운데 값
0~ 1 * 1.5 범위 안에서의 랜덤값이 나올 것이다.
이렇게 해서 float랜덤값이 나오는 것


자 한번 써보자
main.cpp 에서 #Utilies안에 Math.Cpp 를 불러주고 실행해 보았다.
숫자가 계속 실시간으로 바뀌는 걸 볼 수 있다. (저거 움직이는건 신경쓰지마시오)

void Render()
{ 
	ImGui::Text("%d", Math::RanderInt(0, 100));
    ImGUi::Text("%f", Math::RandomFloat(0.0f, 100.0f)); 
    ImGui::SliderFloat2("Start", Start, 0, 600); 
    ImGui::SliderFloat2("End", End, 0, 600); 
    
    line->Add(Start.x, Start.y, End.x, End.y); 
    line->Add(Start2.x, Start2.y, Start2.x + 100, Start2.y); 
    
    line->Render(); 
}

 

 

 

 

 

 

 

 

 

 

 


그리고 또 추가적으로 main.cpp안에서

void InitScean() 위에 D3DXVECTOR2 line[10]; 지정후
InitScean안에 for문으로 만들어 넣어준다.
Render 함수 안에서도 마찬가지로 for 문으로 넣어준다.
범위 는 10으로 정해두고 뽑아 주면 출력은 어떻게 될까?

선이 5개가 나오는 것을 볼 수 있다.


빠져나가는 방식을 봐야할 듯 하지만 본인도 정확히 이해못함

 

 

 

 




1)main.cpp

#include "stdafx.h"
#include "System/Device.h"
#include "Utilities/Math.h" 
#include "Text.h" 
#include "Line.h"

Line* line = NULL;

D3DXVECTOR2 lines[10]; 

void InitScene() 
{ 
	line = new Line(); 
    
    for(int i = 0; i < 10; i++) 
    	lines[i] = { Math::RandomFloat(0.0f, (float)Width), 
        Math::RandomFloat(0.0f, (float)Height)}; } 

void DestroyScene() 
{ 
 	delete line; 
    line = NULL;
} 

//float Start[2] = { 0, 0 };
//float End[2] = { 0, 0 };

D3DXVECTOR2 Start = { 0, 0 }; 
D3DXVECTOR2 End = { 0, 0 };
D3DXVECTOR2 Start2 = { 0, 0 }; 

void Update()
{ 
	if (Key->Press('D'))
		Start2.x += 0.1f;
	else if (Key->Press('A'))
    	Start2.x -= 0.1f; 
        
    if (Key->Press('W')) 
   		Start2.y -= 0.1f; 
    else if (Key->Press('S')) 
    	Start2.y += 0.1f;
    
}

void Render() 
{ 
	ImGui::Text("%d", Math::RandomInt(0, 100)); 
    ImGui::Text("%f", Math::RandomFloat(0.0f, 100.0f));
    ImGui::SliderFloat2("Start", Start, 0, 600); 
    ImGui::SliderFloat2("End", End, 0, 600); 
    
    line->Add(Start.x, Start.y, End.x, End.y);
    line->Add(Start2.x, Start2.y, Start2.x + 100, Start2.y);
    
    for (int i = 0; i < 10; i += 2) 
    	line->Add(lines[i + 0].x, lines[i + 0].y, lines[i + 1].x, lines[i + 1].y); 
    line->Render(); }

 


Math.cpp

#include "stdafx.h"
#include "Math.h"

int Math::RandomInt(int r1, int r2)
{ 
	return rand() % (r2 - r1 + 1) + r1; //괄호로 묶은 이유 나눈 나머지 연산에서 +1 해줘야 자기숫자까지 나오니까.
} 
   
float Math::RandomFloat(float r1, float r2) 
{ 
   	float random = ((float)rand()) / (float)RAND_MAX; 
    
   //RAND_MAX = 7FFF float diff = r2 - r1;
   float val = random * diff; 
   return r1 + val; 
}

//static이 붙은 함수는 외부에 클래스명으로 접근하면 되고 대신에 프라이빗에서 int a쓰면 멤버 변수 되는데

public:
static in RandomInt...

private:
int a;

이런식으로 쓰면 a가 멤버변수가 되는데 static이라는
함수가 붙은것은 자기 클래스 멤버지만 접근 불가 이다.
즉 Math.cpp에서 확인해보면 접근 불가라고 뜬다.
static은 간단한 기능을 모아둘때 많이 사용한다. 굳이 멤버변수가 필요 없을때


Math.h

#pragma once class Math 

{ 
public: //안붙이면 기본 프라이빗 (외부접근불가됨) 

	static int RandomInt(int r1, int r2); 
    static float RandomFloat(float r1, float r2); //하나더 만들어주자
    
};








일반 프로그래밍에 비해서 게임은 실시간이라서 값의 범위가 안에서만
돌아야 하니까 익숙해져야 한다. !
수학에서 sin 하고 cos이라고 하면 둘다 -1 ~ 1까지 의 값을 갖는데
계속 흘러가는 값을 넣으면 원이 생기는 데 이것도 값의 범위다 얼마를 넣던간에
게임의 프로그래밍의 시작은 범위
깜빡깜빡하는것도 sin cos 을 사용 어차피 범위안에 갇혀 있으니까.
0 보다 크면 켜져라 0보다 작으면 꺼져라 형식
익숙해져야 한다.
개발자의 생각하는 습관을 길러야 한다.

 

 

다음시간)

 

[C++] DirectX 색상과 쉐이더 / Color with Shader

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

ppatabox.tistory.com

 

 

728x90
728x90
LIST
profile

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

@공부하는 PPATABOX

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