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

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

이전자료) https://ppatabox.tistory.com/46

 

 

 

 

 

키워드 중요 내용 각주) - 맨 하단 글 하단이동[각주:1]

 

 


전 시간 Rect&State에서 

 

state를 정의 하지 못하고 끝냈다. 

state.cpp/h를 만들어 줄 것이다. 

 

이번시간은 state에 enum클래스를 사용할 것이다.

 

2D에서는 LInear보다 Point를 많이 사용한다. 그래서 우선으로 빼주자 

 

states.h

#pragma once

enum class ESamplerState
{
	Point_Wrap = 0, Linear_Wrap, Point_Border, Linear_Border,//사용할 것들 
};


class SamplerState
{

public:
	SamplerState(UINT slot = 0/*슬롯번호*/, ESamplerState state = ESamplerState::Linear_Wrap, D3DXCOLOR color = D3DXCOLOR(0, 0, 0, 1));
	~SamplerState();

	void Render();

private:
	ID3D11SamplerState* sampler;
	UINT slot; //멤버 하나 만들어주자 
};

 

SamplerState(ESamplerState state = ESamplerState::Linear_Wrap, D3DXCOLOR color = D3DXCOLOR(0, 0, 0, 1));

이것을 쓸때

ESamplerState(기본)  state = ESamplerState::Linear_Wrap (ESamplerState 안에 있는 Linear_Wrap 이다)

Default파라미터 정의 하는것 

 

 

switch[각주:2]

 

 

States.cpp 에 정의해둔 switch 문 헤더에서 사용할 것들을 정의해 두었다.

switch (state)
	{
		case ESamplerState::Point_Wrap:
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		}
		break;

		case ESamplerState::Linear_Wrap: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		}
		break;

		case ESamplerState::Point_Border: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
		}
		break;

		case ESamplerState::Linear_Border: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
		}
		break;
	}

 

컬러가 들어가야한다.

desc.BorderColor를 배열로 넣어줘야하는데 지금 이게 배열[4] 이다. 

 

한번 잘 생각해보자 Color = D3DXCOLOR 인데 

 

D3DXCOLOR에 들어가보자 

 

들어가보면 operator FLOAT* (); 이렇게 정의 되어있는데

이게 FLOAT의 포인터일 경우에 FLOAT의 포인터로 리턴해 주겠다 (리턴해줄때 자료형)

 그럼 저걸 사용하게 되면 FLOAT포인터 4개 나온다

RGBA 배열로 

 

포인터 할때 대입될때 쓰는 방식이다. 

 

desc.BorderColor = color; 그런데 이렇게 쓰면 BorderColor가

배열이 되고 color가 포인터 이기때문에 못받는다. 

 

이럴때는 memcpy를 사용하자 

memcpy(desc.BorderColor, color, sizeof(D3DXCOLOR));

memcpy는 배열 복사 할때 사용한다고 생각하면 된다.

그러면 중간에 있는 color가 D3DXCOLOR이지만 FLOAT형의 포인터로 리턴해준다. 

 

 

 

States.cpp 

#include "stdafx.h"
#include "States.h"

SamplerState::SamplerState(UINT slot, ESamplerState state, D3DXCOLOR color)
	: slot(slot) //대입 
{
	D3D11_SAMPLER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_SAMPLER_DESC));
	
	switch (state)
	{
		case ESamplerState::Point_Wrap:
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		}
		break;

		case ESamplerState::Linear_Wrap: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		}
		break;

		case ESamplerState::Point_Border: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
		}
		break;

		case ESamplerState::Linear_Border: //박은 state가 Linear_Wrap이랑 같다면  
		{
			desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
			desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
			desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
		}
		break;
	}

	memcpy(desc.BorderColor, color, sizeof(D3DXCOLOR));
	
	HRESULT hr = Device->CreateSamplerState(&desc, &sampler);
	assert(SUCCEEDED(hr));
}

SamplerState::~SamplerState()
{
	SAFE_DELETE(sampler);
}

void SamplerState::Render()
{
	DeviceContext->PSSetSamplers(slot, 1, &sampler);
}

 

 

sprite.h 로 가서 정의 해주자 

private:
	Shader* shader;
	SamplerState* samplerState;
    
	BlendState* blendState_None; //알파블렌드 그냥 둘다 처리하기 위해 만듬 
	BlendState* blendState_AlphaBlend;//

sprite.cpp 에서도 

Sprite::Sprite()
{
	shader = new Shader(L"Sprite.hlsl");
	samplerState = new SamplerState();// 기본값 다 넣어두었으니 기본값으로 ->new는 Delete해주기 -Render
	blendState_None = new BlendState(); //알파블렌드 처리를 위해 
	blendState_AlphaBlend = new BlendState(EBlendState::AlphaBlend);

 

Delete해주고 

SAFE_DELETE(samplerState);
    
SAFE_DELETE(blendState_None); //new로 할당했으니 지워줘야지? 
SAFE_DELETE(blendState_AlphaBlend);

Render불러오자 

	samplerState->Render();

 

이제 반투명도 있다 

반투명은 blendstate에 있다 

State.h

enum class EBlendState
{
	AlphaBlend, Addtive, Modulate,
};

class BlendState
{

public:
	BlendState(EBlendState state = EBlendState::AlphaBlend);
	~BlendState();

	void Render();

private:
	ID3D11BlendState* blend;
};

이건 실제로 렌더링 한다음에 렌더링이 끝날때 다시 원래대로 돌려줘야 한다

그래서 알파블렌드가 먹어 있으면 느려진다. 스테이트 꼬일 가능성이 있는데

원래 돌려줘야 하는데 이번에는 그냥 둘다 처리해주자. 

 

 

 

알파블렌딩때 SRC 블렌드에 무얼 넣어 주었는가? 

= 자기 소스의 알파값

DEST에는 INV_SRC_ALPHA값

 

States.cpp 

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

BlendState::BlendState(EBlendState state)
{
	D3D11_BLEND_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BLEND_DESC));

	desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	
	desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	
	desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
	desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;


	switch (state)
	{
		case EBlendState::None:
		{
			desc.RenderTarget[0].BlendEnable = false;
		}
		break;

		case EBlendState::AlphaBlend:
		{
			desc.RenderTarget[0].BlendEnable = true;
			desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
			desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;

		}
		break;

		case EBlendState::Addtive://2개를 더한거 
		{
			desc.RenderTarget[0].BlendEnable = false;
			desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; //두개를 더하니까 1 1 넣어주면 됨 
			desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
		}
		break;

		case EBlendState::Modulate: //앞에꺼에 Dest 넣어 놓고 뒤에꺼 zero
		{
			desc.RenderTarget[0].BlendEnable = false;
			desc.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR; 
			desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
		}
		break;
	}

	HRESULT hr = Device->CreateBlendState(&desc, &blend); //생성
	assert(SUCCEEDED(hr));
}

BlendState::~BlendState()
{
	SAFE_RELEASE(blend); //릴리즈 해서 블렌드 밀어주기
}

void BlendState::Render()
{
	DeviceContext->OMSetBlendState(blend, NULL, 0xFF);
}

 

 

 

 

State.꼬이면 다른건 알파가 들어가면 안되는데 

알파가 이미 앞에서 먹혀 있으면 관리가 힘들게 된다. 

 

 

이제 Texture도 만들 것이다. 

앞으로 기믹(속임수) 들을 사용할 것이라 만들어주자 

 

#include "stdafx.h" //미리 컴파일된 헤더 지금 우리 프로젝트에서는 꺼놨다

미리 컴파일된 헤더를 왜 쓰는가?

먼저 컴파일 되서 수정되기 전까지 컴파일이 필요없는애들 (공통적으로 들어가는애들) 

그애들을 모아둔거 개내들만 미리 컴파일 되서 컴파일 속도가 빨라짐 

 

2D에서는 거의 안쓸거임 

2D에서는 그렇게 많이 필요없는데 

3D에서는 컴파일 할게 많아서 그때부터는 사용할 것이다. 

 

시스템 안에 Texture.cpp/h를 만들어 줄 것이다. 

 

Texture.h

#pragma once

class Texture
{
public:
	Texture(wstring file, UINT slot = 0); //슬롯번호 있어야한다.
	~Texture();

	void Render();
private:
	UINT slot;
	wstring file;

	ID3D11ShaderResourceView* srv;
};

 

Texture.cpp 에서 HRESULT부분 

HRESULT hr = D3DX11CreateShaderResourceViewFromFile
(
	Device,
	file.c_str(),
	NULL,
	NULL,
	&srv,
	NULL 
);
assert(SUCCEEDED(hr));

 

HRESULT  return이 있어서 아래와 같이 쓸 수 있다. 

해당 HRESULT를 포인터로 받아 낼 수 있다. 

Texture::Texture(UINT slot, wstring file)
	: file(file) //멤버하고 파라미터 받은거 아래DXDX11CreatheShaderResourceViewFromFile에서
{
	
	//strcat() //문자 2개를 연결해주는 것 wstring으로 해줘서 wcs로 시작
	file = L"../_Texture/" + file; // 외부에서 정수 쓸 필요 없게됨 

	HRESULT hr; 
	D3DX11CreateShaderResourceViewFromFile
	(
		Device,
		file.c_str(), //파라미터 받은게 기본 
		NULL,
		NULL,
		&srv,
		&hr //포인터 리턴 
	);
	assert(SUCCEEDED(hr));
}

 

이걸 배열로 해도 되는데 실제로 최적화 할때는 

배열로 모은 다음 한번에 온다 

2D에서는 그렇게 최적화 할 것은 없다. 

 

Texture도 자주 쓸거니까 Stdafx.h에다 포함 시켜주자 .

또한 Sprite.h도 넣어주자 

 

 

 

 

 

 

 

sprite.h에 넣어주자 

이게 원래 Texture2D라고 쳐줘야 하는데

 

 

 

 

 

 


 

 

흔히 이미지가 2D이미지가 횡 열을 갖고 있는데 

우리가 흔히 아는 형식이 

 

1D도 있다. 1차원 배열  한줄만 있는 이미지 

 

셀쉐이딩(카툰렌더링)[각주:3]

단면을 표현 할때

 

3D텍스쳐도 있다 .겹겹이 

큐브 텍스쳐도 박스모양

3D에서는 다 쓰게 된다. 


 

 

 

 

sprite(wstring지정

 

Stprite.cpp에  texture = new Texture(L"SingleMario.png"); 입력해주자 

 

 

 

 

 

당연이 new해줬으니 삭제해주고 

 

 

Render까지 

 

 

이제 main으로 가자 

#include "stdafx.h"

Rect* rect = NULL;
Sprite* sprite = NULL; // sprite가 텍스쳐를 그려줄 애 
void InitScene()
{
	rect = new Rect();
	rect->Position(200, 200);
	rect->Scale(100, 100);
	rect->Color(0, 0, 1);

	sprite = new Sprite(L"SingleMario.png"); //이미지 파일 처리 
	sprite->Position(400, 400);
	sprite->Scale(100, 100);
}

void DestroyScene()
{
	SAFE_DELETE(rect);
}

void Update()
{
	rect->Update();
	sprite->Update(); //업데이트 받았으니까
}

void Render()
{	
	rect->Render();
	sprite->Render();//렌더해주고 
}

 

 

Sprite.hlsl에서 UV 받았으니까. 

 

 

 

 

 

 

 

 

 

Uv를 정점 쉐이더 (pass) output  input 나뉘 니까. 

 

 

 

 

 

아까 Sprite에서 여기로 들어온다.

 

 

여기 SamplerState렌더해준게 이게 저기로 들어간다. 

 

 

 

state.cpp 에서 

SamplerState 할때 slot 0번에 넣는다고 했다.

 

그다음에 

Texture2D 때문에 아까도 slot번호 안주었다 (t0) 

 

 

텍스처에서 셈플러 테

이곳에서 패스 시킨거 받기

  return Texture.Sampler(Sampler, input.Uv);

 

Sprite.hlsl

struct VertexInput
{
    float4 Position : POSITION0;
    float2 Uv : UV0;
};

struct PixelInput
{
    float4 Position : SV_POSITION;
    float2 Uv : UV0;
  
};


cbuffer VS_Wvp : register(b0)
{
    matrix World;
    matrix View;
    matrix Projection;
}

PixelInput VS(VertexInput input)
{
    PixelInput output;
    
    output.Position = mul(input.Position, World);
    output.Position = mul(output.Position, View);
    output.Position = mul(output.Position, Projection);
    output.Uv = input.Uv; //나뉨
  
    return output;
}

SamplerStae Sampler : register(s0);
Texture2D Texture : register(t0);

float4 PS(PixelInput input) : SV_TARGET
{
    return Texture.Sampler(Sampler, input.Uv);
}

 

 

그러면 실행해보자 정상적으로 나오는 걸 볼 수 있다. 

 

 

 

 

 

 

 

 

 

 

 

단축키 : 

Ctrl + spacebar 자동완성 (안보일때 자동완성 창 띄어주거나 완성시킴) 

Ctrl + 이동키 상하좌우 단어 단위로 뛰어넘기 됨 

Ctrl + Shift + 좌우 방향키  단어 하나 한번에 드래그 

 

 

 

 

다음시간) 

 

[C++] DirectX Rect&Sprite 3편

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

ppatabox.tistory.com

 

 

 

 

 

 

 

참고 해볼만한 자료)

 

셀 셰이딩 - 위키백과, 우리 모두의 백과사전

셀 셰이딩 위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

 

Unity Shader _ 셀 쉐이딩

1. 셀셰이딩 셀셰이딩의 대표적인 표현 방식인 외곽선 만들기 * 2pass 한 오브젝트를 2번 그리는 방식. 단...

blog.naver.com

 

 

 

 

 

 

 

 

 


각주)

 

  1. . [본문으로]
  2. if /else문과 마찬가지로 주어진 조건 값의 결과에 따라 다른 명령을 수행하도록 하는 조건문, if/else문보다 가독성이 더 좋다, 컴파일러가 최적화를 쉽게 할 수 있어 속도도 빠른편, 하지만! switch의 조건 값으로는 int형으로 승격할 수 있는(interger promotion)값만 사용될 수 있다. C++에서는 char, short, int변수나 리터럴, 열거체까지 사용할 수만 있다, 그래서 if/else문보다는 사용할 수 있는 상황이 적음 [본문으로]
  3. 애니메이션에서 셀 셰이딩, 이나 툰 셰이딩 , 툰 렌더링 은 비사실 적으로 묘사하는 그래픽 기법 중 하나로 손으로 그린 듯한 효과를 준다 최근에 비디오 게임을 제작하는데 많이 활용됨 [본문으로]
728x90
728x90
LIST
profile

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

@공부하는 PPATABOX

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