본문 바로가기

DirectX11 3D/기본 문법

<DirectX11 3D> 100 - ColorTone(PostEffect)

 

필요한 개념


Tip)

Shader코드 분석시 최대한 필요없어 보이는 것부터 제거후 다시 하나씩 추가하면 테스트 및 분석해라

RTV를 텍스쳐 파일로 저장했을때(SaveTexture() 함수로) 정확한 색이 나오지 않는 이유는 텍스처를 저장할 때, RGBA순으로 저장하지 않기 때문이다.

실시간으로 렌더링을 하면서 효과를 바꾼다.
화면 전체에 대한 크기에 맞게(Render2D class로 그리고) 기존 장면을 렌더링(기존 메쉬, 모델이나)하면서 효과를 줌

Render2D 처럼 PostEffect 클래스 만들어서 효과 PostEffect효과를 먹일 것이다.

PostEffect에서 가장 가벼운 것을 할거다.(ColorTone) -> ColorToneDemo.h로 파일 만듬
ColorTone(컬러톤)만 바꾸는 셰이더를 추가함(100_ColorTone.fx)


제일 쉬운 PostEffect는 


1. Inverse(색반전)
float4(1 - 기존값.rgb, 1)
해주면 색이 반전된다.


PostEffect 종류도 많고 PostEffect해서 렌더링된걸 또 합산해서 쓰기도 한다.

렌더 타킷의 크기와 뷰포트를 작게 낮추면 작게 그려진 결과를 메인 뷰포트에 그릴때 확대가 되어야 하므로 해상도가 떨어진다.(렌더링 속도는 빨라짐(->처리해야할 해상도가 줄어서)
반대의 경우는 정밀하게 선명하게 렌더링된다.(계단 효과를 억제해 주는 효과(AntiAliasing)를 낼 수 있다.)

예를 들어 해상도를 2080을 주게 되면 원래 화면 크기가 1040이라면, 축소가 일어나서 더 해상도가 깔끔하게 보인다.(계단현상이 사라지는 효과가 발휘됨, But 프레임은 낮아짐(-> 처리해야할 해상도가 늘어서)
AntiAlising 기법 중 Upscaling, DownScaling 이런 기법이다.

2. 흑백화(GrayScale)는 색상의 평균을 이용한다.
r, g, b를 더한후에 나누면 된다.(흑백은 평균값)

실제는 이 수식을 사용한다.
색상을 강하게 느끼는 정도가 다르기 때문에 만들어 놓은 수치를 사용한다.
예를 들어 빨간색은 덜 하게 느끼고, 녹색은 더 강하게 느끼고, 파란색은 완전 덜 하게 느끼고 그런식..
사람들이 판단하게 편한 수치를 과학자들이 만든 값이 있다. -> grayscale
float4 color = float4(DiffuseMap.Sample(LinearSampler, input.Uv));
이 수치는 사람들이 느끼는 정도를 나타냄float3 grayscale = float3(0.2627f, 0.6780f, 0.0593f);
color랑 grayscale이랑 (원래 색이랑 곱하고 더해야하니까) 내적하면된다.
내적이 서로 곱하고 더하고니까
float average = dot(color.rgb, grayscale);


3. Saturation(0일 때는 흑백,  1일 때는 원본 컬러가 나오는 방식, 색이 진해지는 효과이다. 채도라는 뜻)
Saturation = 0 : Grayscale (흑백)
0 < Saturation < 1 : desaturation(색이 음영에 가까운것, 흑백에서 원본 사이)
Saturation = 1 : original (원본 컬러)
Saturation > 1 : saturation (색이 더욱 진해짐)

1이 넘어갈 수록 색이 더욱 진해진다.

GrayScale, OriginalColor를 Saturation(float)으로 lerp한다.

4. Sharpness 외곽선 효과
Sharpness : 외곽선 샤프로 칠하는 느낌이어서, 외곽선을 탐지하는것

 

 

Sharpness


주변 픽셀들을 알아내고 변화량을 가지고 외곽선을 찾는다.
픽셀하나의 크기를 알아내야 쓸 수 있음
왼, 오, 위, 아래 픽셀의 변화량을 알아낸다.(외곽선을 구하기 위해)

센터 * 4하고 나머지 주변 픽셀들을 전부 빼준다.
외곽선 구한 값이 됨
외곽선을 구하려고 자기를 중심으로 왼, 오, 위, 아래 차를 한다.
변화량을 가지고 외곽선을 찾음
미분에 가까움
float edge = center * 4 - top - bottom - left - right;
색을 곱하면 외곽선만 색을 바꿀 수 있다.

 




요약
RenderTarget Size - 해상도
Sharpness - 외곽선(주변 픽셀과의 차)

5. Sepia(Sepia Matrix 수식 값으로 Sepia톤 색상 효과를 준다.)
공간행렬 처럼 컬러행렬(수식 값)으로 만들어서 mul()해서 색상을 공간으로 변환해서 색을 바꿔줌
mul(ColorToSepiaMatrix, color);


6. Vignette(비네티)
카메라를 찍었을때, 주변 외곽선이 검게 나오는것(색을 곱해서 원하는 색을 바꿀 수도 있음)
구글에 이미지 쳐볼것(사각에 검은색 카메라 처럼 외곽효과 나오는것)
게임에 많이 쓰인다.

7. LensDistortion(색상을 오래된 필름처럼 만들어주는 기법)
수식은 코드에 설명되어있다.
float3 Distortion, float LensPower를 조정해서
공포영화, 옛날 필름 처럼 가능하다.

8. Interace(가로줄 가는 옛날 TV느낌)
게임에서 많이 쓰임
Y픽셀만 해서 가로줄 가게함

Strength가 더 올라가면 강해짐
InteraceValue를 올리면 가로줄 더 진해짐


9. Blur(주변 픽셀의 평균을 이용하여 흐리게 보이도록 하는 효과)
사방픽셀을 더하고 평균을 냄
Blur도 많은 기법이 있다. GuassianBlur도 있고...
ScaleSourceSize값을 낮추면 확 벌어진다.
반면 올리면 더 선명해짐(주변 픽셀을 합산을 해서)


10. Wiggle(Sin, Cos의 회전을 이용하여 울렁거리게 만드는 효과)
마법 같은 거 쓸때 은근히 많이씀
Offset으로 울렁대는거 범위 조절
Amount x,y로 강도 조절
보통 Amount를 줄여서 UV가 역전되는 것을 막어서 사용한다.(그냥도 많이씀)

 

 

 

 

 

PostEffect.h (Render2D와 유사)


더보기
#pragma once

// PostEffect효과를 먹일 것이다.

class PostEffect : public Renderer
{
public:
	// 셰이더를 따로 내부에서 만들어서 쓸거여서
	// cBuffer에 넘어가야하는게 있어서 이렇게 처리
	PostEffect(wstring shaderFile);
	~PostEffect();

	void Update();
	void Render();

	void SRV(ID3D11ShaderResourceView* srv);

//	struct Desc
//	{
//		Matrix View;
//		Matrix Projection;
//	} desc;
//
//private:
//	ConstantBuffer* buffer;


private:
	ID3DX11EffectShaderResourceVariable* sDiffuseMap;
};

 

 

 

 

 

 

 

 

 

 

PostEffect.cpp (Render2D와 유사)


더보기
#include "Framework.h"
#include "PostEffect.h"

PostEffect::PostEffect(wstring shaderFile)
	: Renderer(shaderFile)
{
	/*buffer = new ConstantBuffer(&desc, sizeof(Desc));
	shader->AsConstantBuffer("CB_PostEffect")->SetConstantBuffer(buffer->Buffer());*/

	Vertex vertices[6];
	// 0.5를 1.0으로 늘려줌(NDC 좌표값으로 1.0을 줌)	
	vertices[0].Position = Vector3(-1.0f, -1.0f, 0.0f);
	vertices[1].Position = Vector3(-1.0f, +1.0f, 0.0f);
	vertices[2].Position = Vector3(+1.0f, -1.0f, 0.0f);
	vertices[3].Position = Vector3(+1.0f, -1.0f, 0.0f);
	vertices[4].Position = Vector3(-1.0f, +1.0f, 0.0f);
	vertices[5].Position = Vector3(+1.0f, +1.0f, 0.0f);

	vertexBuffer = new VertexBuffer(vertices, 6, sizeof(Vertex));
	sDiffuseMap = shader->AsSRV("DiffuseMap");

	// 효과를 적용하기 위해 화면 크기로 고정
	transform->Scale(D3D::Width(), D3D::Height(), 1);
	transform->Position(D3D::Width() * 0.5f, D3D::Height() * 0.5f, 0);
}

PostEffect::~PostEffect()
{
}

void PostEffect::Update()
{
	Super::Update();
}

void PostEffect::Render()
{
	Super::Render();

	//buffer->Render();
	shader->Draw(0, Pass(), 6);
}

void PostEffect::SRV(ID3D11ShaderResourceView* srv)
{
	sDiffuseMap->SetResource(srv);
}

 

 

 

 

 

 

 

 

 

 

 

ColorToneDemo.h


더보기
#pragma once
#include "Systems/IExecute.h"

class ColorToneDemo : public IExecute
{
public:
	virtual void Initialize() override;
	virtual void Ready() override {}
	virtual void Destroy() override {}
	virtual void Update() override;
	virtual void PreRender() override;
	virtual void Render() override;
	virtual void PostRender() override;
	virtual void ResizeScreen() override {}

private:
	void Billboards();
	void Mesh();
	void Airplane();
	void Kachujin();
	void KachujinCollider();
	void KachujinWeapon();
	void PointLighting();
	void SpotLighting();

	void Pass(UINT mesh, UINT model, UINT anim);

private:
	Shader* shader;

	RenderTarget* renderTarget;
	DepthStencil* depthStencil;
	// 메인의 Viewport를 사용해도 상관은 없지만,
	// RTV, DSV, Viewport를 1:1:1로 만드는 것이 편하다.
	Viewport* viewport;
	Render2D* render2D;
	// PostEffect 효과를 먹이기위해
	PostEffect* postEffect;


	Billborad* billboard;

	CubeSky* sky;

	Material* floor;
	Material* stone;
	Material* brick;
	Material* wall;

	MeshRender* cube;
	MeshRender* cylinder;
	MeshRender* sphere;
	MeshRender* grid;

	ModelRender* airplane = NULL;


	ModelAnimator* kachujin = NULL;
	Transform* colliderInitTransforms;
	ColliderObject** colliders;

	ModelRender* weapon = NULL;
	Transform* weaponInitTransform;

	vector<MeshRender*> meshes;
	vector<ModelRender*> models;
	vector<ModelAnimator*> animators;
};

 

 

 

 

 

 

 

ColorToneDemo.cpp


더보기
#include "stdafx.h"
#include "ColorToneDemo.h"

void ColorToneDemo::Initialize()
{
	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
	Context::Get()->GetCamera()->Position(1, 36, -85);

	shader = new Shader(L"96_Billboard.fxo");

	float width = D3D::Width(), height = D3D::Height();

	renderTarget = new RenderTarget((UINT)width, (UINT)height);
	depthStencil = new DepthStencil((UINT)width, (UINT)height);
	viewport = new Viewport(width, height);
	
	render2D = new Render2D();
	// 좌상단에 이 크기로 보여줌 
	render2D->GetTransform()->Scale(355, 200, 1);
	render2D->GetTransform()->Position(200, 120, 0);
	// 바뀔일이 없으니 Render()에서 안하고, 여기서 한번만 해도 됨
	render2D->SRV(renderTarget->SRV());
	
	postEffect = new PostEffect(L"100_ColorTone.fxo");
	postEffect->SRV(renderTarget->SRV());

	sky = new CubeSky(L"Environment/GrassCube1024.dds");

	Billboards();

	Mesh();
	Airplane();

	Kachujin();
	KachujinCollider();
	KachujinWeapon();

	PointLighting();
	SpotLighting();
}

void ColorToneDemo::Update()
{
	// PassCount() : pass 개수 구해옴
	static UINT Pass = postEffect->GetShader()->PassCount() - 1;
	ImGui::InputInt("ColorTone Pass", (int*)&Pass);
	Pass %= postEffect->GetShader()->PassCount();
	postEffect->Pass(Pass);

	// 픽셀 사이즈 넘김
	Vector2 PixelSize = Vector2(1.0f / D3D::Width(), 1.0f / D3D::Height());
	postEffect->GetShader()->AsVector("PixelSize")->SetFloatVector(PixelSize);
	
	// Saturation
	{
		ImGui::Separator();

		static float Saturation = 0.0f;
		ImGui::InputFloat("Saturation", &Saturation, 0.1f);
		postEffect->GetShader()->AsScalar("Saturation")->SetFloat(Saturation);
	}

	//Sharpness
	{
		ImGui::Separator();

		static float Sharpness = 0.0f;
		ImGui::InputFloat("Sharpness", &Sharpness, 0.1f);
		postEffect->GetShader()->AsScalar("Sharpness")->SetFloat(Sharpness);
	}

	//Vignette
	{
		ImGui::Separator();

		static float Power = 1.0f;
		ImGui::InputFloat("Power", &Power, 0.1f);
		postEffect->GetShader()->AsScalar("Power")->SetFloat(Power);

		static float ScaleX = 1.0f;
		ImGui::InputFloat("ScaleX", &ScaleX, 0.1f);

		static float ScaleY = 1.0f;
		ImGui::InputFloat("ScaleY", &ScaleY, 0.1f);
		postEffect->GetShader()->AsVector("Scale")->SetFloatVector(Vector2(ScaleX, ScaleY));
	}

	//LensDistortion
	{
		ImGui::Separator();

		static float LensPower = 1.0f;
		ImGui::InputFloat("LensPower", &LensPower, 0.01f);
		postEffect->GetShader()->AsScalar("LensPower")->SetFloat(LensPower);

		static Vector3 Distortion = Vector3(-0.02f, -0.02f, -0.02f);
		ImGui::InputFloat("DistortionX", &Distortion.x, 0.001f);
		ImGui::InputFloat("DistortionY", &Distortion.y, 0.001f);
		ImGui::InputFloat("DistortionZ", &Distortion.z, 0.001f);
		postEffect->GetShader()->AsVector("Distortion")->SetFloatVector(Distortion);
	}

	//Interace
	{
		ImGui::Separator();

		static float Strength = 1.0f;
		ImGui::InputFloat("Strength", &Strength, 0.01f);
		postEffect->GetShader()->AsScalar("Strength")->SetFloat(Strength);

		static int InteraceValue = 2;
		ImGui::InputInt("InteraceValue", &InteraceValue);
		postEffect->GetShader()->AsScalar("interaceValue")->SetInt(InteraceValue);
	}

	//Down Scale
	{
		ImGui::Separator();

		static float ScaleX = D3D::Width();
		ImGui::InputFloat("DownScaleX", &ScaleX, 1.0f);

		static float ScaleY = D3D::Height();
		ImGui::InputFloat("DownScaleY", &ScaleY, 1.0f);
		postEffect->GetShader()->AsVector("ScaleSourceSize")->SetFloatVector(Vector2(ScaleX, ScaleY));
	}

	//Wiggle
	{
		ImGui::Separator();

		static float OffsetX = 10;
		ImGui::InputFloat("OffsetX", &OffsetX, 0.1f);

		static float OffsetY = 10;
		ImGui::InputFloat("OffsetY", &OffsetY, 0.1f);
		postEffect->GetShader()->AsVector("WiggleOffset")->SetFloatVector(Vector2(OffsetX, OffsetY));

		static float AmountX = 0.01f;
		ImGui::InputFloat("AmountX", &AmountX, 0.001f);

		static float AmountY = 0.01f;
		ImGui::InputFloat("AmountY", &AmountY, 0.001f);
		postEffect->GetShader()->AsVector("WiggleAmount")->SetFloatVector(Vector2(AmountX, AmountY));
	}

	sky->Update();

	cube->Update();
	grid->Update();
	cylinder->Update();
	sphere->Update();

	airplane->Update();
	kachujin->Update();

	Matrix worlds[MAX_MODEL_TRANSFORMS];
	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
	{
		kachujin->GetAttachTransform(i, worlds);

		weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]);
	}

	weapon->UpdateTransforms();
	weapon->Update();

	billboard->Update();

	render2D->Update();
	postEffect->Update();
}

void ColorToneDemo::PreRender()
{
	// SRV, DSV를 세팅하고 그쪽 backBuffer를 클리어함
	// 다른 렌더타킷으로 교체하기까지 여기다가 계속 그려짐
	renderTarget->PreRender(depthStencil);

	// 렌더타겟용 뷰포트로 설정해줌
	viewport->RSSetViewport();
	
	// Render
	{
		sky->Render();


		Pass(0, 1, 2);

		wall->Render();
		sphere->Render();

		brick->Render();
		cylinder->Render();

		stone->Render();
		cube->Render();

		floor->Render();
		grid->Render();

		airplane->Render();

		kachujin->Render();
		weapon->Render();

		billboard->Render();
	}
}

void ColorToneDemo::Render()
{
	// 여기다가 메인 렌더링을 함
	
	// 파일로 저장해봄, 파일 텍스쳐 포맷이 달라져서 색이 다르게 나옴(나중에 옵션 찾아서 해결하면 됨)
	//if (Keyboard::Get()->Down(VK_SPACE))
	//	renderTarget->SaveTexture(L"../RenderTarget.png");

}
void ColorToneDemo::PostRender()
{
	// 주 렌더링이 끝난후 효과 처리 등 가능(PostRender()에서)
	
	// 이렇게 세팅해주면 화면이 그대로 나온다. 왜 화면이 그대로 나오냐면?
	// 렌더링 한거를 RTV에다가 넣고 그 RTV를 render2D에서 그리니깐
	// 똑같이 나옴


	// 깊이 끄고 그릴것이다. 그러면 순서대로 그려진다.
	postEffect->Render();
	render2D->Render();
}


void ColorToneDemo::Billboards()
{
	billboard = new Billborad(shader);
	billboard->Pass(4);
	billboard->AddTexture(L"Terrain/grass_14.tga");
	billboard->AddTexture(L"Terrain/grass_07.tga");
	billboard->AddTexture(L"Terrain/grass_11.tga");
	
	for (UINT i = 0; i < 1200; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		// 5(밑에 판 크기가 -5 ~ 5 -> 10) * 12(grid 크기) = 60
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 0);
	}

	for (UINT i = 0; i < 300; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 1);
	}

	for (UINT i = 0; i < 700; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 2);
	}

}

void ColorToneDemo::Mesh()
{
	//Create Material
	{
		floor = new Material(shader);
		floor->DiffuseMap("Floor.png");
		floor->Specular(1, 1, 1, 20);
		floor->SpecularMap("Floor_Specular.png");
		floor->NormalMap("Floor_Normal.png");

		stone = new Material(shader);
		stone->DiffuseMap("Stones.png");
		stone->Specular(1, 1, 1, 20);
		stone->SpecularMap("Stones_Specular.png");
		stone->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		stone->NormalMap("Stones_Normal.png");


		brick = new Material(shader);
		brick->DiffuseMap("Bricks.png");
		brick->Specular(1, 0.3f, 0.3f, 20);
		brick->SpecularMap("Bricks_Specular.png");
		brick->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		brick->NormalMap("Bricks_Normal.png");

		wall = new Material(shader);
		wall->DiffuseMap("Wall.png");
		wall->Specular(1, 1, 1, 20);
		wall->SpecularMap("Wall_Specular.png");
		wall->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		wall->NormalMap("Wall_Normal.png");
	}

	//Create Mesh
	{
		Transform* transform = NULL;

		cube = new MeshRender(shader, new MeshCube());
		transform = cube->AddTransform();
		transform->Position(0, 5, 0);
		transform->Scale(20, 10, 20);

		grid = new MeshRender(shader, new MeshGrid(5, 5));
		transform = grid->AddTransform();
		transform->Position(0, 0, 0);
		transform->Scale(12, 1, 12);

		cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
		sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
		for (UINT i = 0; i < 5; i++)
		{
			transform = cylinder->AddTransform();
			transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = cylinder->AddTransform();
			transform->Position(30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);


			transform = sphere->AddTransform();
			transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = sphere->AddTransform();
			transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);
		}
	}

	sphere->UpdateTransforms();
	cylinder->UpdateTransforms();
	cube->UpdateTransforms();
	grid->UpdateTransforms();

	meshes.push_back(sphere);
	meshes.push_back(cylinder);
	meshes.push_back(cube);
	meshes.push_back(grid);
}

void ColorToneDemo::Airplane()
{
	airplane = new ModelRender(shader);
	airplane->ReadMesh(L"B787/Airplane");
	airplane->ReadMaterial(L"B787/Airplane");

	Transform* transform = airplane->AddTransform();
	transform->Position(2.0f, 9.91f, 2.0f);
	transform->Scale(0.004f, 0.004f, 0.004f);
	airplane->UpdateTransforms();

	models.push_back(airplane);
}

void ColorToneDemo::Kachujin()
{
	kachujin = new ModelAnimator(shader);
	kachujin->ReadMesh(L"Kachujin/Mesh");
	kachujin->ReadMaterial(L"Kachujin/Mesh");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
	kachujin->ReadClip(L"Kachujin/Salsa Dancing");


	Transform* transform = NULL;

	transform = kachujin->AddTransform();
	transform->Position(0, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(0, 0, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(1, 1, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-30, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(2, 2, 0.75f);

	transform = kachujin->AddTransform();
	transform->Position(15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayBlendMode(3, 0, 1, 2);
	kachujin->SetBlendAlpha(3, 1.5f);

	transform = kachujin->AddTransform();
	transform->Position(30, 0, -32.5f);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(4, 4, 0.75f);

	kachujin->UpdateTransforms();

	animators.push_back(kachujin);
}

void ColorToneDemo::KachujinCollider()
{
	UINT count = kachujin->GetTransformCount();
	colliders = new  ColliderObject * [count];

	colliderInitTransforms = new Transform();
	colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f);
	colliderInitTransforms->Scale(5, 5, 75);

	for (UINT i = 0; i < count; i++)
	{
		colliders[i] = new ColliderObject();

		//colliders[i]->Init = new Transform();
		//colliders[i]->Init->Position(0, 0, 0);
		//colliders[i]->Init->Scale(10, 30, 10);

		colliders[i]->Transform = new Transform();
		//colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
		colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms);
	}
}

void ColorToneDemo::KachujinWeapon()
{
	weapon = new ModelRender(shader);
	weapon->ReadMesh(L"Weapon/Sword");
	weapon->ReadMaterial(L"Weapon/Sword");

	UINT count = kachujin->GetTransformCount();
	for (UINT i = 0; i < count; i++)
		weapon->AddTransform();

	weapon->UpdateTransforms();
	models.push_back(weapon);


	weaponInitTransform = new Transform();
	weaponInitTransform->Position(-2.9f, 1.45f, -6.45f);
	weaponInitTransform->Scale(0.5f, 0.5f, 0.75f);
	weaponInitTransform->Rotation(0, 0, 1);
}

void ColorToneDemo::PointLighting()
{
	PointLight light;
	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient
		Color(0.0f, 0.0f, 1.0f, 1.0f), //Diffuse
		Color(0.0f, 0.0f, 0.7f, 1.0f), //Specular
		Color(0.0f, 0.0f, 0.7f, 1.0f), //Emissive
		Vector3(-30, 10, -30), 5.0f, 0.9f // 위치, 범위, 강도
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f),
		Color(1.0f, 0.0f, 0.0f, 1.0f),
		Color(0.6f, 0.2f, 0.0f, 1.0f),
		Color(0.6f, 0.3f, 0.0f, 1.0f),
		Vector3(15, 10, -30), 10.0f, 0.3f
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient
		Color(0.0f, 1.0f, 0.0f, 1.0f), //Diffuse
		Color(0.0f, 0.7f, 0.0f, 1.0f), //Specular
		Color(0.0f, 0.7f, 0.0f, 1.0f), //Emissive
		Vector3(-5, 1, -17.5f), 5.0f, 0.9f
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f),
		Color(0.0f, 0.0f, 1.0f, 1.0f),
		Color(0.0f, 0.0f, 0.7f, 1.0f),
		Color(0.0f, 0.0f, 0.7f, 1.0f),
		Vector3(-10, 1, -17.5f), 5.0f, 0.9f
	};
	Lighting::Get()->AddPointLight(light);
}

void ColorToneDemo::SpotLighting()
{
	SpotLight light;
	light =
	{
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Color(0.7f, 1.0f, 0.0f, 1.0f),
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Vector3(-15, 20, -30), 25.0f,
		Vector3(0, -1, 0), 30.0f, 0.4f
	};
	Lighting::Get()->AddSpotLight(light);

	light =
	{
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Vector3(0, 20, -30), 30.0f,
		Vector3(0, -1, 0), 40.0f, 0.55f
	};
	Lighting::Get()->AddSpotLight(light);
}

void ColorToneDemo::Pass(UINT mesh, UINT model, UINT anim)
{
	for (MeshRender* temp : meshes)
		temp->Pass(mesh);

	for (ModelRender* temp : models)
		temp->Pass(model);

	for (ModelAnimator* temp : animators)
		temp->Pass(anim);
}

 

 

 

 

 

 

 

 

 

 

100_ColorTone.fx


더보기
#include "00_Global.fx"
#include "00_Light.fx"


float2 PixelSize; // 픽셀 사이즈

struct VertexOutput
{
    float4 Positon : SV_Position;
    float2 Uv : Uv;
};

// 구조체 선언안하고 Position만 넘기도록 해도 된다.
VertexOutput VS(float4 Position : Position)
{
    VertexOutput output;
    
    // 정점의 위치를 NDC좌표로 주었기 때문에 그냥 그대로 주면 된다.
    output.Positon = Position;
    // 이렇게 하면 Uv 좌표가 만들어진다.(0 ~ 1 범위로)
    output.Uv.x = Position.x * 0.5f + 0.5f;
    // v는 뒤집혀져 있으니까
    output.Uv.y = -Position.y * 0.5f + 0.5f;

    return output;
}

float4 PS_Diffuse(VertexOutput input) : SV_Target
{
    return DiffuseMap.Sample(LinearSampler, input.Uv);
}

// PostEffect 중에 가장 간단한 -> 색상반전
float4 PS_Inverse(VertexOutput input) : SV_Target
{
    return float4(1.0f - DiffuseMap.Sample(LinearSampler, input.Uv).rgb, 1.0f);
}

// 흑백화(GrayScale)
float4 PS_Grayscale(VertexOutput input) : SV_Target
{
    // 밑의 수식은 이론상 표현(이론상으로는 색의 평균을 한다.)
    float4 color = float4(DiffuseMap.Sample(LinearSampler, input.Uv));
    
    float average = (color.r + color.g + color.b) / 3.0f;
    
    // 세개를 더한 후 평균을 낸다.
    return float4(average, average, average, 1.0f);
}

// 흑백화(GrayScale)
float4 PS_Grayscale2(VertexOutput input) : SV_Target
{
    // 실제는 이 수식을 사용한다.
    // 색상을 강하게 느끼는 정도가 다르기 때문에 만들어 놓은 수치를 사용한다.
    // 예를 들어 빨간색은 덜 하게 느끼고, 녹색은 더 강하게 느끼고, 파란색은 완전 덜 하게 느끼고 그런식..
    // 사람들이 판단하게 편한 수치를 과학자들이 만든 값이 있다. -> grayscale
    float4 color = float4(DiffuseMap.Sample(LinearSampler, input.Uv));
    // 이 수치는 사람들이 느끼는 정도를 나타냄
    float3 grayscale = float3(0.2627f, 0.6780f, 0.0593f);
    
    // color랑 grayscale이랑 (원래 색이랑 곱하고 더해야하니까) 내적하면된다.
    // 내적이 서로 곱하고 더하고니까
    float average = dot(color.rgb, grayscale);
    
    // 세개를 더한 후 평균을 낸다.
    return float4(average, average, average, 1.0f);
}

// Saturation
float Saturation = 0;
// Saturation이 0일때(Grayscale(흑백화)), 1일떄 Original(원래 색), 1보다 크면 Saturation이 일어남
// Saturation = 0 : Grayscale
// 0 < Saturation < 1 : desaturation
// Saturation = 1 : original
// Saturation > 1 : saturation
float4 PS_Saturation(VertexOutput input) : SV_Target
{
    float4 color = float4(DiffuseMap.Sample(LinearSampler, input.Uv));
    float3 grayscale = float3(0.2627f, 0.6780f, 0.0593f);
    
    float temp = dot(color.rgb, grayscale);
    
    // Saturation이 0일떄 temp(흑백), 1일떄 color.rgb 그 사이값일때 중간값이다.
    // 커지면 이거 보다 큰 값이 나온다.
    color.rgb = lerp(temp, color.rgb, Saturation);
    color.a = 1.0f;
    
    return color;
}

float Sharpness = 0;
float4 PS_Sharpness(VertexOutput input) : SV_Target
{
    // 현재 처리하고 있는 픽셀
    float4 center = float4(DiffuseMap.Sample(LinearSampler, input.Uv));
    // PixelSize : 한픽셀의 크기이다. 이 한픽셀의 크기를 뺴면 바로 위 픽셀을 얻어올 수 있다.
    float4 top = float4(DiffuseMap.Sample(LinearSampler, input.Uv + float2(0, -PixelSize.y)));
    float4 bottom = float4(DiffuseMap.Sample(LinearSampler, input.Uv + float2(0, +PixelSize.y)));
    float4 left = float4(DiffuseMap.Sample(LinearSampler, input.Uv + float2(-PixelSize.x, 0)));
    float4 right = float4(DiffuseMap.Sample(LinearSampler, input.Uv + float2(+PixelSize.x, 0)));
   
    // 주변 픽셀을 부드럽게 만드는 효과 블러(blur)도 이 방식을 쓴다.
    
    // uv는 0 ~ 1까지를 만든다.
    // 0 ~ 1까지를 한면이라고 보면
    // 우리는 크기를 화면의 넓이로 두고 있다.
    // 화면의 크기로 이미지가 발라진다.
    // 1 / 크기로 하면 픽셀 하나하나를 알아올 수 있다.
    // 정리 : UV가 크기로 나눠 0 ~ 1까지의 범위를 만드는 것이므로
    // 같은 방식으로 1/크기를 이용해 픽셀의 크기를 알아낼 수 있다.
    
    // 센터 * 4하고 나머지 주변 픽셀들을 전부 빼준다.
    // 외곽선 구한 값이 됨
    // 외곽선을 구하려고 자기를 중심으로 왼, 오, 위, 아래 차를 한다.
    // 변화량을 가지고 외곽선을 찾음
    // 미분에 가까움
    float edge = center * 4 - top - bottom - left - right;
    
    // 센터에다가 받은 값을 더해주고 * 외곽선 구한 값을 곱한다.
    // 
    return (center + Sharpness) * edge;
}

// 구글에 SepiaMatrix라고 치면 수식 나옴(GrayScale과 비슷한 얘기임)
// 그 중에 하나를 선택해서 가져온 것이다.
// 이제까지는 공간 변환 행렬으로 사용했는데(어떤 공간으로 이동시키기 위해) 행렬을
// 지금은 그게 아니라, 이 공간으로 색을 바꾸는 것이다.(색상 공간을 다룬다.)
// 역시 어떤 컬러를 이 색상 공간으로 변환하며, 그 컬러가 이 색상공간으로 전환된다.
// 이 행렬 부분 값을 조정한다면 sepia톤 뿐만아니라 여러 컬러 톤을 만들 수 있다.
// 실제로 MonoScale처럼 색상 vector를 사용하는게 아니라, 아래처럼 컬러 행렬을 더 많이 사용한다.
float4x4 ColorToSepiaMatrix = float4x4
(
    0.393, 0.769, 0.189, 0,
    0.349, 0.686, 0.168, 0,
    0.272, 0.534, 0.131, 0,
    0, 0, 0, 1
);

float4 PS_Sepia(VertexOutput input) : SV_Target
{
    float4 color = DiffuseMap.Sample(LinearSampler, input.Uv);
    
    // 공간변환은 mul()로 했다.
    // 위치를 공간으로 변환해서 사용했듯이, 색상을 공간으로 변환해서 색을 바꿔준다.
    return mul(ColorToSepiaMatrix, color);
}


// 외곽선만 잘 결합하면 된다.
float Power = 2; // 얼마나 강도를 줄지
// 1 : Linear
// 1 > : NoneLinear

float2 Scale = float2(2, 2); // 얼마만큼 Vignette를 적용할지 스케일이다.
float4 PS_Vignette(VertexOutput input) : SV_Target
{
    float4 color = DiffuseMap.Sample(LinearSampler, input.Uv);
    
    // 범위를 구함(구를 만들 값)
    // uv를 이용해서 반을 뺴고 늘린다음 scale로 나눠서 Vignette영역을 만듬
    float radius = length((input.Uv - 0.5f) * 2 / Scale);
    // 0이면 power해서 무조건 1이되어버리니까, 오차값을 준다.
    // 구를 만들 수 있다.
    float vignette = pow(abs(radius + 1e-6f), Power);
    
    
    // 일부러 값을 반전시킴 * color
    return saturate(1 - vignette) * color;
}


// 색상을 오래된 필름처럼 만들어주는 기법(색을 섞는다.)
float LensPower = 1;
float3 Distortion = -0.02f;

float4 PS_LensDistortion(VertexOutput input) : SV_Target
{
    // uv를 -1 ~ 1까지 범위로 만든다.
    float2 uv = input.Uv * 2 - 1;
    
    float2 vpSize = float2(1.0f / PixelSize.x, 1.0f / PixelSize.y); // 원래 픽셀 사이즈로 돌아옴, 화면 넓이가 됨(ViewportSize)
    float aspect = vpSize.x / vpSize.y; // 화면 비율
    float radiusSquared = aspect * aspect + uv.x * uv.x + uv.y * uv.y; // 화면 비율에 제곱해줌
    float radius = sqrt(radiusSquared); // 루트 연산이용해서 대각선 길이 구함

    float3 f = Distortion * pow(abs(radius + 1e-6f), LensPower) + 1; // 범위를 만듬
    
    // 값을 하나씩 빼서 섞어준 것이라 보면 된다.
    float2 r = (f.r * uv + 1) * 0.5f;
    float2 g = (f.g * uv + 1) * 0.5f;
    float2 b = (f.b * uv + 1) * 0.5f;
    
    float4 color = 0;
    // 색 별로 분할해서 쓸려고 했음
    // a는 안써도 되는데 사람이 가장 강하게 느끼니까
    color.r = DiffuseMap.Sample(LinearSampler, r).r;
    color.ga = DiffuseMap.Sample(LinearSampler, g).ga;
    color.b = DiffuseMap.Sample(LinearSampler, b).b;
    
    return color;
}


float Strength = 1.0f;
int interaceValue = 2;

// 가로줄 가는 옛날 TV느낌
float4 PS_Interace(VertexOutput input) : SV_Target
{
    float4 color = DiffuseMap.Sample(LinearSampler, input.Uv);
    float height = 1.0f / PixelSize.y;
    
    // round : 반올림, ceil : 올림, floor : 내림
    int value = (int) ((floor(input.Uv.y * height) % interaceValue) / (interaceValue / 2));
    
    [flatten]
    if (value)
    {
        float3 grayScale = float3(0.2126f, 0.7152f, 0.0722f);
        float luminance = dot(color.rgb, grayScale);
        
        luminance = min(0.999f, luminance);
        
        // 안곱한값, 곱한값 lerp 해줌
        color.rgb = lerp(color.rgb, color.rgb * luminance, Strength);
    }
    return color;
}

// Blur를 최대한 간단하게 만든것
// Blur : 주변 픽셀의 평균을 이용하여 흐리게 보이도록 하는 효과
float2 ScaleSourceSize; // 화면의 원본 사이즈
float4 PS_Blur(VertexOutput input) : SV_Target
{
    float2 size = 1.0f / ScaleSourceSize;
    
    float4 s0 = DiffuseMap.Sample(LinearSampler, input.Uv + float2(-size.x, -size.y));
    float4 s1 = DiffuseMap.Sample(LinearSampler, input.Uv + float2(+size.x, -size.y));
    float4 s2 = DiffuseMap.Sample(LinearSampler, input.Uv + float2(-size.x, +size.y));
    float4 s3 = DiffuseMap.Sample(LinearSampler, input.Uv + float2(+size.x, +size.y));
    
    return (s0 + s1 + s2 + s3) / 4;
}

float2 WiggleOffset = float2(10, 10);
float2 WiggleAmount = float2(0.01f, 0.01f);

// Sin, Cos의 회전을 이용하여 울렁거리게 만드는 효과
float4 PS_Wiggle(VertexOutput input) : SV_Target
{
    float2 uv = input.Uv;
    uv.x += sin(Time + uv.x * WiggleOffset.x) * WiggleAmount.x;
    uv.y += cos(Time + uv.y * WiggleOffset.y) * WiggleAmount.y;
    
    return DiffuseMap.Sample(LinearSampler, uv);
}


technique11 T0
{
    P_VP(P0, VS, PS_Diffuse)
    P_VP(P1, VS, PS_Inverse)
    P_VP(P2, VS, PS_Grayscale)
    P_VP(P3, VS, PS_Grayscale2)
    P_VP(P4, VS, PS_Saturation)
    P_VP(P5, VS, PS_Sharpness)
    P_VP(P6, VS, PS_Sepia)
    P_VP(P7, VS, PS_Vignette)
    P_VP(P8, VS, PS_LensDistortion)
    P_VP(P9, VS, PS_Interace)
    P_VP(P10, VS, PS_Blur)
    P_VP(P11, VS, PS_Wiggle)
}

 

 

 

 

 

 

 

 

 

 

 

결과


100_ColorTone.fx 렌더링 Pass 순서와 동일