https://www.inflearn.com/course/lecture?courseSlug=directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98&unitId=161133

 

학습 페이지

 

www.inflearn.com

 



1. 빛 통합

오늘은 앞에 만들어줬던 여러 조명을 하나로 통합해주자 이때 쉐이더코드를 만들어줄텐데 구조체를 선언하고 만들때 16바이트 정렬을 지켜주는 것이 좋은데 이것은 나중에 조명이 여러개 등장할 때, 상수버퍼에 배열형태로 넘겨주게 되는데 이때 

16바이트 정렬이 지켜지지않으면 데이터가 깨질수도 있다.

 

일단 공통으로 쓸 빛 코드를 하나의 쉐이더 파일에 넣어주자. 이때 전에 사용했던 빛을 구하는 공식도  하나로 모아서 함수로 만들어주자. 이때 최종적인 빛은 각 빛의 값을 다 더해주면 된다.

 

Light.fx

#ifndef _LIGHT_FX_
#define _LIGHT_FX_

#include "00. Global.fx"

////////////
// struct //
///////////

struct LightDesc
{
    float4 ambient;
    float4 diffuse;
    float4 specular;
    float4 emissive;
    float3 direction;
    float padding;      //16바이트 정렬
};

struct MaterialDesc
{
    float4 ambient;
    float4 diffuse;
    float4 specular;
    float4 emissive;
};

/////////////////
// ConstBuffer //
////////////////

cbuffer LightBuffer
{
    LightDesc GlobalLight;
};

cbuffer MaterialBuffer
{
    MaterialDesc Material;
};

//////////
// SRV //
/////////

Texture2D DiffuseMap;
Texture2D SpecularMap;
Texture2D NormalMap;

///////////////
// Function //
//////////////

float4 ComputeLight(float3 normal,float2 uv,float3 worldPosition)
{
    float4 ambientColor = 0;
    float4 diffuseColor = 0;
    float4 specularColor = 0;
    float4 emissiveColor = 0;
    
    //Ambient - 주변광
    {
        float4 color = GlobalLight.ambient * Material.ambient;
        ambientColor = DiffuseMap.Sample(LinearSampler, uv) * color;
    }
    //Diffuse - 반사광
    {
        float4 color = DiffuseMap.Sample(LinearSampler, uv);
        //람베르트
        float value = dot(-GlobalLight.direction, normalize(normal));
        diffuseColor = color * value * GlobalLight.diffuse * Material.diffuse;
    }
    //Specular  - 광원효과
    {
        //float3 R = reflect(LightDir, normal);
        float3 R = GlobalLight.direction - (2 * normal * dot(GlobalLight.direction, normal));
        R = normalize(R);
    
        float3 cameraPosition = CameraPosition(); //마지막행
        float3 E = normalize(cameraPosition - worldPosition);       //물체-카메라
    
        float value = saturate(dot(R, E)); //clmap 0~1
        float specular = pow(value, 10);
    
        specularColor = GlobalLight.specular * Material.specular * specular;
    }
    //Emissive  - 외곽선
    {
        float3 cameraPosition = CameraPosition();
        //카메라로 가는 방향
        float3 E = normalize(cameraPosition - worldPosition);
    
        float value = saturate(dot(E, normal)); //0~1
        float emissive = 1.0f - value;          //1~0
    
        //점차 증가
        emissive = smoothstep(0.0f, 1.0f, emissive);
        emissive = pow(emissive, 2);
    
        emissiveColor = GlobalLight.emissive * Material.emissive * emissive;
    }
    
    return ambientColor + diffuseColor + specularColor + emissiveColor;
}

#endif

 

이에 맞게 새로운 쉐이더파일을 만들어주자 이때 우리가 만들어준 빛구하는 함수를 사용해주자

Lighting.fx

#include "00. Global.fx"
#include "00. Light.fx"


MeshOutput  VS(VertexTextureNormal input)
{
    MeshOutput output;
    output.position = mul(input.position,W);
    output.worldPosition = output.position.xyz;
    output.position = mul(output.position, VP);
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}




float4 PS(MeshOutput input) : SV_TARGET
{
    float color = ComputeLight(input.normal, input.uv, input.worldPosition);
    
    return color;
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

그리고 우리가 만들어준 구조체를 통한 버퍼를 채워줄 수 있도록 RenderManager에서 값을 밀어넣어줄 수 있는 각 구조체로 선언된 constant buffer와 Effect를 추가해주고 이를 채워주는 함수도 추가해주자

RenderManager.h

#pragma once
#include "ConstantBuffer.h"

class Shader;

struct GlobalDesc
{
	Matrix V = Matrix::Identity;
	Matrix P = Matrix::Identity;
	Matrix VP = Matrix::Identity;
};

struct TransformDesc
{
	Matrix W = Matrix::Identity;
};

//Light
struct LightDesc
{
	Color ambient = Color(1.f, 1.f, 1.f, 1.f);
	Color diffuse = Color(1.f, 1.f, 1.f, 1.f);
	Color specular = Color(1.f, 1.f, 1.f, 1.f);
	Color emissive = Color(1.f, 1.f, 1.f, 1.f);

	Vec3 direction;
	float padding0;
};

struct MaterialDesc
{
	Color ambient = Color(0.f, 0.f, 0.f, 1.f);
	Color diffuse = Color(1.f, 1.f, 1.f, 1.f);
	Color specular = Color(0.f, 0.f, 0.f, 1.f);
	Color emissive = Color(0.f, 0.f, 0.f, 1.f);
};

class RenderManager
{
	DECLARE_SINGLE(RenderManager);

public:
	void Init(shared_ptr<Shader> shader);
	void Update();

	void PushGlobalData(const Matrix& view, const Matrix& projection);
	void PushTransformData(const TransformDesc& desc);
	void PushLightData(const LightDesc& desc);
	void PushMaterialData(const MaterialDesc& desc);


private:
	shared_ptr<Shader> _shader;

	//프레임마다 한번만 세팅
	GlobalDesc _globalDesc;
	shared_ptr<ConstantBuffer<GlobalDesc>> _globalBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _globalEffectBuffer;


	TransformDesc _transformDesc;
	shared_ptr<ConstantBuffer<TransformDesc>> _transformBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _transformEffectBuffer;

	LightDesc _lightDesc;
	shared_ptr<ConstantBuffer<LightDesc>> _lightBuffer;
	ComPtr<ID3DX11EffectConstantBuffer> _lightEffectBuffer;

	MaterialDesc _materialDesc;
	shared_ptr<ConstantBuffer<MaterialDesc>> _materialBuffer;
	ComPtr<ID3DX11EffectConstantBuffer> _materialEffectBuffer;
};

RenderManager.cpp

#include "pch.h"
#include "RenderManager.h"
#include "Camera.h"

void RenderManager::Init(shared_ptr<Shader> shader)
{
	_shader = shader;

	_globalBuffer = make_shared<ConstantBuffer<GlobalDesc>>();
	_globalBuffer->Create();
	_globalEffectBuffer = _shader->GetConstantBuffer("GlobalBuffer");

	_transformBuffer = make_shared<ConstantBuffer<TransformDesc>>();
	_transformBuffer->Create();
	_transformEffectBuffer = _shader->GetConstantBuffer("TransformBuffer");

	_lightBuffer = make_shared<ConstantBuffer<LightDesc>>();
	_lightBuffer->Create();
	_lightEffectBuffer = _shader->GetConstantBuffer("LightBuffer");

	_materialBuffer = make_shared<ConstantBuffer<MaterialDesc>>();
	_materialBuffer->Create();
	_materialEffectBuffer = _shader->GetConstantBuffer("MaterialBuffer");
}

void RenderManager::Update()
{
	PushGlobalData(Camera::S_MatView, Camera::S_MatProjection);
}

void RenderManager::PushGlobalData(const Matrix& view, const Matrix& projection)
{
	_globalDesc.V = view;
	_globalDesc.P = projection;
	_globalDesc.VP = view * projection;
	_globalBuffer->CopyData(_globalDesc);
	//쉐이더로 밀어넣기
	_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}

void RenderManager::PushTransformData(const TransformDesc& desc)
{
	_transformDesc = desc;
	_transformBuffer->CopyData(_transformDesc);
	_transformEffectBuffer->SetConstantBuffer(_transformBuffer->GetComPtr().Get());
}

void RenderManager::PushLightData(const LightDesc& desc)
{
	_lightDesc = desc;
	_lightBuffer->CopyData(_lightDesc);
	_lightEffectBuffer->SetConstantBuffer(_lightBuffer->GetComPtr().Get());
}

void RenderManager::PushMaterialData(const MaterialDesc& desc)
{
	_materialDesc = desc;
	_materialBuffer->CopyData(_materialDesc);
	_materialEffectBuffer->SetConstantBuffer(_materialBuffer->GetComPtr().Get());
}

 

이렇게 해준 뒤 메인코드에서 구조체를 선언해주고 값을 설정하여 넘겨주면 된다. 

LightingDemo.cpp

#include "pch.h"
#include "16. LightingDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void LightingDemo::Init()
{
	RESOURCES->Init();
	_shader = make_shared<Shader>(L"13. Lighting.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f,0.f,-10.f });
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	//Object2
	_obj2 = make_shared<GameObject>();
	_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f,0.f,2.f });
	_obj2->AddComponent(make_shared<MeshRenderer>());
	{
		_obj2->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh2 = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh2);
	}
	{
		//Texture
		auto texture2 = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj2->GetMeshRenderer()->SetTexture(texture2);
	}

	RENDER->Init(_shader);
}

void LightingDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	{
		LightDesc lightDesc;
		lightDesc.ambient = Vec4(0.5f);
		lightDesc.diffuse = Vec4(1.f);
		lightDesc.specular = Vec4(1.f, 1.f, 1.f, 1.f);
		lightDesc.direction = Vec3(0.f, -1.f, 0.f);
		RENDER->PushLightData(lightDesc);
	}

	{
		MaterialDesc desc;
		desc.ambient = Vec4(0.2f);
		desc.diffuse = Vec4(1.f);
		desc.specular = Vec4(1.f);
		//desc.emissive = Color(0.3f, 0.f, 0.f, 0.5f);

		RENDER->PushMaterialData(desc);
		_obj->Update();
	}

	{
		MaterialDesc desc;
		desc.ambient = Vec4(0.5f);
		desc.diffuse = Vec4(1.f);
		//desc.specular = Color(0.5f, 0.5f, 0.5f, 1.f);
		//desc.emissive = Color(1.f, 0.f, 0.f, 1.f);

		RENDER->PushMaterialData(desc);
		_obj2->Update();
	}
}

void LightingDemo::Render()
{
	
}

 

이렇게 해주면 모든 빛이 적용된 모습을 볼 수 있다.

 

 

2.Material

Material은 재질로 물체의 빛에 대한 정보와 texture 정보를 가지고 있다. Material은 shader에 넘기는 인자의 모음 클래스라고 보면 된다. 

이떄, Clone을 함수를 통해 하나의 물체에서 Material을 다른값으로 설정하더라도 그 물체만 영향을 받게 한다.

Material.h

#pragma once
#include "ResourceBase.h"

class Material : public ResourceBase
{
	using Super = ResourceBase;
public:
	Material();
	virtual ~Material();

	shared_ptr<Shader> GetShader() { return _shader; }

	MaterialDesc& GetMaterialDesc() { return _desc; }
	shared_ptr<Texture> GetDiffuseMap() { return _diffuseMap; }
	shared_ptr<Texture> GetNormalMap() { return _normalMap; }
	shared_ptr<Texture> GetSpecularMap() { return _specularMap; }

	void SetShader(shared_ptr<Shader> shader);
	void SetDiffuseMap(shared_ptr<Texture> diffuseMap) { _diffuseMap = diffuseMap; }
	void SetNormalMap(shared_ptr<Texture> normalMap) { _normalMap = normalMap; }
	void SetSpecularMap(shared_ptr<Texture> specularMap) { _specularMap = specularMap; }

	void Update();

	//복사본
	shared_ptr<Material> Clone();
private:
	friend class MeshRenderer;

	MaterialDesc _desc;

	shared_ptr<Shader> _shader;
	shared_ptr<Texture> _diffuseMap;
	shared_ptr<Texture> _normalMap;
	shared_ptr<Texture> _specularMap;

	//캐싱용
	ComPtr<ID3DX11EffectShaderResourceVariable> _diffuseEffectBuffer;
	ComPtr<ID3DX11EffectShaderResourceVariable> _normalEffectBuffer;
	ComPtr<ID3DX11EffectShaderResourceVariable> _specularEffectBuffer;

};

Material.cpp

#include "pch.h"
#include "Material.h"

Material::Material() : Super(ResourceType::Material)
{

}

Material::~Material()
{

}

void Material::SetShader(shared_ptr<Shader> shader)
{
	_shader = shader;

	_diffuseEffectBuffer = shader->GetSRV("DiffuseMap");
	_normalEffectBuffer = shader->GetSRV("NormalMap");
	_specularEffectBuffer = shader->GetSRV("SpecularMap");
}

void Material::Update()
{
	if (_shader == nullptr)
		return;

	RENDER->PushMaterialData(_desc);

	if (_diffuseMap)
		_diffuseEffectBuffer->SetResource(_diffuseMap->GetComPtr().Get());

	if (_normalMap)
		_normalEffectBuffer->SetResource(_normalMap->GetComPtr().Get());

	if (_specularMap)
		_specularEffectBuffer->SetResource(_specularMap->GetComPtr().Get());
}

std::shared_ptr<Material> Material::Clone()
{
	shared_ptr<Material> material = make_shared<Material>();

	material->_desc = _desc;
	material->_shader = _shader;
	material->_diffuseMap = _diffuseMap;
	material->_normalMap = _normalMap;
	material->_specularMap = _specularMap;
	material->_diffuseEffectBuffer = _diffuseEffectBuffer;
	material->_normalEffectBuffer = _normalEffectBuffer;
	material->_specularEffectBuffer = _specularEffectBuffer;

	return material;
}

 

이제 Texture와 shader를 가지고 있던 MeshRenderer 클래스를 수정해주자 shader와 texture대신 material 변수를 추가하고 Update부분에서 shader를 material에서 가져오고 material의 update를 호출하도록하자.

MeshRenderer.h

#pragma once
#include "Component.h"

class Mesh;
class Shader;
class Material;

class MeshRenderer : public Component
{
	using Super = Component;
public:
	MeshRenderer();
	virtual ~MeshRenderer();

	virtual void Update() override;

	void SetMesh(shared_ptr<Mesh> mesh) { _mesh = mesh; }
	void SetMaterial(shared_ptr<Material> material) { _material = material; }

	//Legacy
	void SetTexture(shared_ptr<Texture> texture) { }
	void SetShader(shared_ptr<Shader> shader) { }

private:
	shared_ptr<Mesh> _mesh;
	shared_ptr<Material> _material;
	//shared_ptr<Texture> _texture;
	//shared_ptr<Shader> _shader;
};

MeshRenderer.cpp

void MeshRenderer::Update()
{
	if (_mesh == nullptr || _material == nullptr)
		return;

	auto shader = _material->GetShader();
	if (shader == nullptr) return;

	_material->Update();

	auto world = GetTransform()->GetWorldMatrix();
	RENDER->PushTransformData(TransformDesc{ world });

	uint32 stride = _mesh->GetVertexBuffer()->GetStride();
	uint32 offset = _mesh->GetVertexBuffer()->GetOffset();

	DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);

	shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}

 

 

마지막으로 메인이 되는 코드를 수정해주자. 시작부분에서 Material을 생성 및 초기화해주고 이 Material을 리소스매니저를 통해 불러올 수 있는 리소스로 추가해주자. 그리고 Material에서 texture를 가져와서 각 오브젝트의 texture로 설정해준다. 그리고 만약 material부분을 수정하려면 Clone 함수를 통해 복사본을 만들고 MaterialDesc를 수정해주고 다시 Material

을 설정해주면 된다.

MaterialDemo.cpp

#include "pch.h"
#include "17. MaterialDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "GameObject.h"
#include "CameraScript.h"
#include "MeshRenderer.h"
#include "Mesh.h"
#include "Material.h"

void MaterialDemo::Init()
{
	RESOURCES->Init();
	_shader = make_shared<Shader>(L"13. Lighting.fx");

	// Material
	{
		shared_ptr<Material> material = make_shared<Material>();
		{
			material->SetShader(_shader);
		}
		{
			auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
			material->SetDiffuseMap(texture);
		}

		MaterialDesc& desc = material->GetMaterialDesc();
		desc.ambient = Vec4(1.f);
		desc.diffuse = Vec4(1.f);

		RESOURCES->Add(L"Veigar", material);
	}

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -10.f });
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	// Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		auto material = RESOURCES->Get<Material>(L"Veigar");
		_obj->GetMeshRenderer()->SetMaterial(material);
	}

	// Object2
	_obj2 = make_shared<GameObject>();
	_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f, 0.f, 2.f });
	_obj2->AddComponent(make_shared<MeshRenderer>());
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		auto material = RESOURCES->Get<Material>(L"Veigar")->Clone();
		MaterialDesc& desc = material->GetMaterialDesc();
		//desc.ambient = Vec4(0.f);
		//desc.diffuse = Vec4(0.f);

		_obj2->GetMeshRenderer()->SetMaterial(material);
	}

	RENDER->Init(_shader);
}

void MaterialDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	{
		LightDesc lightDesc;
		lightDesc.ambient = Vec4(0.5f);
		lightDesc.diffuse = Vec4(1.f);
		lightDesc.specular = Vec4(1.f, 1.f, 1.f, 1.f);
		lightDesc.direction = Vec3(0.f, -1.f, 0.f);
		RENDER->PushLightData(lightDesc);
	}

	{
		_obj->Update();
	}

	{
		_obj2->Update();
	}
}

void MaterialDemo::Render()
{

}

 

https://www.acmicpc.net/problem/3009

 

세점을 가지고 축에 평행한 직사각형을 만들기 위한 네번째 점을 찾아야한다. 2번나온 x,y좌표를 제외하고 한번만 나온 x,y값을 사용하여 좌표를 완성해주자

 

나는 모든값을 받고  2번이상나왔는지 if문을 통해 확인했지만 더 효율적으로 푸는 방법은 모든 값을 받고 XOR 연산을 통해 X,Y값을 얻어내는 것이 가장 효율적이다 .

 

정답코드1

#include <iostream>
#include <vector>
#include <utility>

using namespace std;

int main() {
    vector<pair<int, int>> vp(3); // 세 점을 받으므로 크기 3으로 설정

    for (int i = 0; i < 3; i++) {
        cin >> vp[i].first >> vp[i].second; // 세 점의 좌표 입력
    }

    int x, y;

    // X 좌표 구하기
    if (vp[0].first == vp[1].first) {
        x = vp[2].first;
    }
    else if (vp[0].first == vp[2].first) {
        x = vp[1].first;
    }
    else {
        x = vp[0].first;
    }

    // Y 좌표 구하기
    if (vp[0].second == vp[1].second) {
        y = vp[2].second;
    }
    else if (vp[0].second == vp[2].second) {
        y = vp[1].second;
    }
    else {
        y = vp[0].second;
    }

    cout << x << " " << y << endl; // 네 번째 점 출력

    return 0;
}

정답코드2(XOR연산사용)

#include <iostream>

using namespace std;

int main() {
    int x1, y1, x2, y2, x3, y3;
    cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;

    // XOR 연산을 통해 x 좌표와 y 좌표를 구한다
    int x4 = x1 ^ x2 ^ x3;
    int y4 = y1 ^ y2 ^ y3;

    cout << x4 << " " << y4 << endl;

    return 0;
}

 

https://www.acmicpc.net/problem/1085

 

 

직사각형의 경계셔선까지 최소거리를 구하면 되는 문제이다. 이떄, 원점에서 시작되는 직선이 가까운지 아니라면 (w,h)

에서 시작되는 직선이 가까운지 구하면 된다. 

 

정답코드

#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    int x, y, w, h;

    cin >> x >> y >> w >> h;

    // 각각의 경계선까지의 거리
    int distance_to_left = x;         // (x, y)에서 왼쪽 경계선 (0, y)까지의 거리
    int distance_to_right = w - x;    // (x, y)에서 오른쪽 경계선 (w, y)까지의 거리
    int distance_to_bottom = y;       // (x, y)에서 아래쪽 경계선 (x, 0)까지의 거리
    int distance_to_top = h - y;      // (x, y)에서 위쪽 경계선 (x, h)까지의 거리

    // 최솟값 구하기
    int min_value = min({ distance_to_left, distance_to_right, distance_to_bottom, distance_to_top });

    cout << min_value << endl;

    return 0;
}

 

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]9063번. 대지  (0) 2024.09.09
[백준][C++]3009번. 네 번째 점  (1) 2024.09.06
[백준][C++]11653번. 소인수분해  (2) 2024.09.05
[백준][C++]2581번. 소수  (0) 2024.09.04
[백준][C++]1978번. 소수 찾기  (0) 2024.09.03

https://www.acmicpc.net/problem/11653

 

 

소인수분해를 하는 문제이다. 문제자체는 쉽기때문에 일단 풀고 어떻게 해야 시간복잡도를 줄일지 고민해보았다. 

일단 먼저 풀어본 방식은 순회를 하면서 나누어떨어지는게 있으면 벡터에 저장하고 전체를 순회했다면 벡터를

순회하면서 출력해주게 했다.

 

정답코드1

#include "iostream"
#include "vector"
#include "cma"

using namespace std;

int main()
{
	int n;
	vector<int> v;
	cin >> n;

	if (n == 1) cout << "" << endl;

	for (int i = 2; i <= n;)
	{
		if (n % i == 0)
		{
			n /= i;
			v.push_back(i);
		}
		else
		{
			i++;
		}
	}

	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << endl;
	}


	return 0;
}

 

2번째 방식은 어떤 수 n이 a * b로 표현될 때, 적어도 하나의 인수는 sqrt(n) 이하인 것을 사용하여 주어진 수의 제곱근까지만 순회를 하는 방식으로 구현하였다.

 

정답코드2

#include "iostream"
#include "vector"
#include "cmath"

using namespace std;

int main()
{
	int n;
	vector<int> v;
	cin >> n;

	if (n == 1) cout << "" << endl;

	for (int i = 2; i <= sqrt(n); i++) {
		while (n % i == 0) {
			n /= i;
			v.push_back(i);
		}
	}

	// 남아있는 소수 `n`이 있다면 그것도 출력
	if (n > 1) {
		v.push_back(n);
	}

	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << endl;
	}


	return 0;
}

 

3번째 방식은 에타토스테네스의 체를 이용하는 방식이다. 일단 범위내의 소수를 모두 구해두고 구한 소수를 통해 소인수분해를 하는 방식이다. 

 

정답코드3

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

// 에라토스테네스의 체를 이용하여 범위 내의 모든 소수를 구함
vector<int> sieve(int max) {
    vector<bool> is_prime(max + 1, true);
    vector<int> primes;
    is_prime[0] = is_prime[1] = false;

    for (int i = 2; i <= max; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
            for (int j = i * 2; j <= max; j += i) {
                is_prime[j] = false;
            }
        }
    }
    return primes;
}

int main() {
    int n;
    cin >> n;

    if (n == 1) {
        cout << "" << endl;
        return 0;
    }

    // 소수 리스트를 미리 구함
    int max_prime = sqrt(n);  // 소수를 구할 최대 범위 설정
    vector<int> primes = sieve(max_prime);

    // 소인수 분해 수행
    vector<int> factors;
    for (int prime : primes) {
        while (n % prime == 0) {
            factors.push_back(prime);
            n /= prime;
        }
    }

    // 남아있는 소수 n이 있으면 그것도 출력
    if (n > 1) {
        factors.push_back(n);
    }

    // 결과 출력
    for (int factor : factors) {
        cout << factor << endl;
    }

    return 0;
}

https://www.inflearn.com/course/lecture?courseSlug=directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98&unitId=161129&tab=curriculum

 

학습 페이지

 

www.inflearn.com

 

 

오늘부터는 Direct x 에서 사용되는 조명에 대해 배워보자

모든 물체가 보이기 위해서는 조명이 필요하다. 물체에서 반사된 빛이 우리 눈에 보이면서 물체의 색이 인식된다. 하지만 이 빛은 정반사만 이루어지는 것이 아니라 난반사도 이루어지는데 이러한 빛까지 실시간으로 모두 계산하기에는 불가능

하다. 그렇기 때문에 중요한 점은 최소한의 비용으로 효율을 내는 조명 연산식을 통해 조명효과를 만드는 것이다.

 

대표적으로 사용되는 조명에는 Ambient, Diffuse, Specular, Emissive가 있다 

조명에는 색상, 음양효과등이 필요하다.

1.Ambient

일단 처음으로 Ambient Light를 만들어보자. 이 조명은 주변광 또는 간접광으로 불리며 광원에 의해 직접적으로 빛을 받는 것이 아니라 주변 물체에 의해 받는 빛이다. 여러 곳에서 반사되면서 광원이 불분명하지만 최종적으로 물체에 도달한 빛으로 일정한 밝기와 색을 가지고 있다. 일정한 방향으로 나타나는 빛이다. 광원이 희미해도 물체가 보일 수 있도록 해주기

도 한다.

일단 shader 코드를 하나 새로 만들어서 광원의 Ambient 값과 물체의 Ambient값을 받아서 PS 단계에서 곱해준 값을 

내보내도록 하자

Lighting_Ambient.fx

#include "00. Global.fx"

//광원자체의 색상
float4 LightAmbient;
//물체 재질 색상- 빛을 얼마나 받아들이는지   
float4 MaterialAmbient;

VertexOutput VS(VertexTextureNormal input)
{
	VertexOutput output;
    output.position = mul(input.position,W);
    output.position = mul(output.position, VP);
	
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}

Texture2D Texture0;

// Ambient (주변광 / 환경광)
// 일정한 밝기와 색
// 수많은 반사 - 불분명한 광원

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = LightAmbient * MaterialAmbient;
	//텍스처 -> 샘플링된 좌표 
    return color;
    //return Texture0.Sample(LinearSampler, input.uv);
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

이에 맞게 메인코드에서 Update부분에 광원의 주변광과 물체의 주변광 값을 임의로 설정해주자 

AmbientDemo.cpp

#include "pch.h"
#include "12. AmbientDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void AmbientDemo::Init()
{
	RESOURCES->Init();
	_shader = make_shared<Shader>(L"09. Lighting_Ambient.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f,0.f,-10.f });
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	//Object2
	_obj2 = make_shared<GameObject>();
	_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f,0.f,2.f });
	_obj2->AddComponent(make_shared<MeshRenderer>());
	{
		_obj2->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh2 = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh2);
	}
	{
		//Texture
		auto texture2 = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj2->GetMeshRenderer()->SetTexture(texture2);
	}

	RENDER->Init(_shader);
}

void AmbientDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	//조명
	Vec4 lightAmbient{ 0.2f, 0.f, 0.f, 1.f };
	_shader->GetVector("LightAmbient")->SetFloatVector((float*)&lightAmbient);

	{
		//모든 색을 다 받아주는 재질
		Vec4 materialAmbient(1.f);
		_shader->GetVector("MaterialAmbient")->SetFloatVector((float*)&materialAmbient);
		_obj->Update();
	}

	{
		//모든 색을 다 받아주는 재질
		Vec4 materialAmbient(1.f);
		_shader->GetVector("MaterialAmbient")->SetFloatVector((float*)&materialAmbient);
		_obj2->Update();
	}
	
}

void AmbientDemo::Render()
{
	
}

2.Diffuse 

 

Diffuse Light는 분산광으로 물체의 표면에서 바로 반사돼서 눈에 들어오는 빛으로 우리가 지난 시간에 노멀벡터를 통해 빛과 연산해준 것과 같은 빛인것이다. 이 빛에 대한 연산은 람베르트 공식을 통해 이루어지는데 핵심은 각도에 따라 빛의 세기가 달라진다는 것이다.

일단 shader 파일을 하나 새로 만들어주자 이때 LightDir,(빛의 방향) LightDiffuse(빛의 색상), MaterialDiffuse(물체가 받아드릴 색상)과 같은 변수를 두고 이를 통해 빛의 연산이 이루어지도록 하자. 일단 물체의 normal벡터와 빛의 반대방향의 내적을 계산하고 빛의 색과 빛을 얼마나 받아들이는지에 관한 값을 곱해주어서 최종적인 Color 데이터를 반환해주면된다.

Lighting_Diffuse.fx

#include "00. Global.fx"

float3 LightDir;
//광원색
float4 LightDiffuse;
//물체 재질 색상- 빛을 얼마나 받아들이는지 
float4 MaterialDiffuse;
Texture2D DiffuseMap;

VertexOutput VS(VertexTextureNormal input)
{
	VertexOutput output;
    output.position = mul(input.position,W);
    output.position = mul(output.position, VP);
	
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}



//Diffuse - 분산광

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
    
    //람베르트
    float value = dot(-LightDir, normalize(input.normal));
    color = color * value * LightDiffuse * MaterialDiffuse;
    
    return color;
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

이렇게 바꿔주고 이제 광원에 대한 정보와 각 물체의 재질에 대한 정보를 지정해주면 이제 Diffuse Light가 적용된 물체의 모습을 볼 수 있다. 지금은 오른쪽 대각선 아래방향으로 빛이 들어오기 때문에 왼쪽 위방향이 잘 보이게 된다.

DiffuseDemo.cpp

#include "pch.h"
#include "13. DiffuseDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void DiffuseDemo::Init()
{
	RESOURCES->Init();
	_shader = make_shared<Shader>(L"10. Lighting_Diffuse.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f,0.f,-10.f });
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	//Object2
	_obj2 = make_shared<GameObject>();
	_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f,0.f,2.f });
	_obj2->AddComponent(make_shared<MeshRenderer>());
	{
		_obj2->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh2 = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh2);
	}
	{
		//Texture
		auto texture2 = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj2->GetMeshRenderer()->SetTexture(texture2);
	}

	RENDER->Init(_shader);
}

void DiffuseDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	//조명
	Vec4 lightDiffuse{ 1.f,1.f,1.f,1.f };
	_shader->GetVector("LightDiffuse")->SetFloatVector((float*)&lightDiffuse);

	Vec3 lightDir{ 1.f,-1.f,1.f };
	lightDir.Normalize();
	_shader->GetVector("LightDir")->SetFloatVector((float*)&lightDir);

	{
		//모든 색을 다 받아주는 재질
		Vec4 material(1.f);
		_shader->GetVector("MaterialDiffuse")->SetFloatVector((float*)&material);
		_obj->Update();
	}

	{
		//모든 색을 다 받아주는 재질
		Vec4 material(1.f);
		_shader->GetVector("MaterialDiffuse")->SetFloatVector((float*)&material);
		_obj2->Update();
	}
	
}

void DiffuseDemo::Render()
{
	
}

 

이때 큐브모양은 빛이 균일한 모습인데 이렇게 되는 이유는 큐브는 면상의 normal vector가 균일하기 때문이다.

3.Specular

Specular Light는 철과 같은 물체가 빛에 반사될 떄 반짝반짝하는 효과를 내주는 조명이다. 이 조명은 Diffuse Light와

비슷하지만 조명이 눈에 들어올 때의 효과에 관한 것으로 빛의 반사각과 카메라와의 각도가 중요하다. 결국 한 방향으로 완전히 반사되는 빛이며 퐁 반사모델을 따른다.

 

일단 쉐이더코드를 만들어주자 핵심은 반사되는 빛과 카메라사이의 각을 구해야하며 이를 바탕으로 specular 값을 구해주면 된다.

 

Lighting_Specular.fx

#include "00. Global.fx"

float3 LightDir;
float4 LightSpecular;
float4 MaterialSpecular;

Texture2D DiffuseMap;

MeshOutput  VS(VertexTextureNormal input)
{
    MeshOutput output;
    output.position = mul(input.position,W);
    output.worldPosition = output.position;
    output.position = mul(output.position, VP);
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}



//Specular 반사광
// 한방향으로 완전히 반사되는 빛- 퐁

float4 PS(MeshOutput input) : SV_TARGET
{
    //내장함수 방식
    //float3 R = reflect(LightDir, input.normal);
    //수학식 방식
    float3 R = LightDir - (2 * input.normal * dot(LightDir, input.normal));
    R = normalize(R);
    
    float3 cameraPosition = -V._41_42_43;
    float3 E = normalize(cameraPosition - input.worldPosition);
    
    float value = saturate(dot(R, E));      //clmap 0~1
    float specular = pow(value, 10);
    
  
    float4 color = LightSpecular * MaterialSpecular * specular;
    
    return color;
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

이렇게 해주고 빛의 세기와 빛을 받아주는 재질을 설정해주면 된다.

SpecularDemo.cpp

#include "pch.h"
#include "14. SpecularDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void SpecularDemo::Init()
{
	RESOURCES->Init();
	_shader = make_shared<Shader>(L"11. Lighting_Specular.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f,0.f,-10.f });
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	//Object2
	_obj2 = make_shared<GameObject>();
	_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f,0.f,2.f });
	_obj2->AddComponent(make_shared<MeshRenderer>());
	{
		_obj2->GetMeshRenderer()->SetShader(_shader);
	}
	{
		auto mesh2 = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh2);
	}
	{
		//Texture
		auto texture2 = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj2->GetMeshRenderer()->SetTexture(texture2);
	}

	RENDER->Init(_shader);
}

void SpecularDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	//조명
	Vec4 light{ 1.f };
	_shader->GetVector("LightSpecular")->SetFloatVector((float*)&light);

	Vec3 lightDir{ 1.f,0.f,1.f };
	lightDir.Normalize();
	_shader->GetVector("LightDir")->SetFloatVector((float*)&lightDir);

	{
		//빨간색만 받아주기
		Vec4 material(1.f);
		_shader->GetVector("MaterialSpecular")->SetFloatVector((float*)&material);
		_obj->Update();
	}

	{
		//모든 색을 다 받아주는 재질
		Vec4 material(1.f);
		_shader->GetVector("MaterialSpecular")->SetFloatVector((float*)&material);
		_obj2->Update();
	}
	
}

void SpecularDemo::Render()
{
	
}


이렇게 해주면 지금은 오른쪽으로 빛을 비추고 있기 때문에 왼쪽 면이 빛나고 있는 것을 볼 수 있다.

또한 쉐이더 코드 상에서 PS단계에 value의 제곱값을 줄이게되면 빛나는 부분의 영역이 커지는 것을 볼 수 있다. 우리가 saturate 함수를 사용했기 때문에 0~1 사이의 값으로 나오게 되고 이를 제곱값으로 계산하게 되면 제곱값이 클수록 부분적으로 빛이 작용하는 것이다.

4. Emissive

물체의 outline 즉 외곽선을 나타내주는 조명이다. 외곽선을 구할 때 사용한다. 

 

쉐이더 파일을 새로 생성해준 뒤 PS 부분을 고쳐주자. 이 Emissive Light는 빛의 방향과 색상은 필요없고 물체가 어떤 색을 받아들이는 지가 중요하다. PS방향에서 카메라 위치를 통해 카메라(눈)로 가는 방향벡터를 구해주고 이 벡터와 normal

벡터를 내적해주고 이 값을 수식에 맞게 계산해주면 된다. 이떄 emissive 값을 smoothstep을 통해 점차 증가하도록 해주고 이 값을 물체재질 값과 곱해줘서 반환해주면 된다.

 

Lighting_Emissive.fx

#include "00. Global.fx"


float4 MaterialEmissive;


MeshOutput  VS(VertexTextureNormal input)
{
    MeshOutput output;
    output.position = mul(input.position,W);
    output.worldPosition = output.position;
    output.position = mul(output.position, VP);
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}



//Emissive - 림라이트
// 외곽선 구할 때 사용

float4 PS(MeshOutput input) : SV_TARGET
{
    float3 cameraPosition = -V._41_42_43;
    //카메라로 가는 방향
    float3 E = normalize(cameraPosition - input.worldPosition);
    
    float value = saturate(dot(E, input.normal));
    float emissive = 1.0f - value;
    
    //점차 증가
    emissive = smoothstep(0.0f, 1.0f, emissive);
    
    float4 color = MaterialEmissive * emissive;
    
    return color;
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

이때 emissive 값이 커질수록 외곽선이 작아진다.  즉 E방향과 normal 방향이 멀어질수록 커진다

 

https://www.acmicpc.net/problem/2581

 

 

이것또한 에라토스테네스의 체 방식을 사용하여 최대값까지의 소수를 구한 뒤에 m~n 까지 순회하면서 소수라면 벡터에 저장하고 sum도 같이 추가하면서 최종적인 최솟값과 합을 출력해주면된다. 만약 벡터의 크기가 0이라면 -1을 출력해준다.

 

정답코드

#include "iostream"
#include "vector"
#include "cmath"

using namespace std;

// 에라토스테네스의 체로 소수 목록을 생성
vector<bool> sieve(int max) {
    vector<bool> is_prime(max + 1, true);
    is_prime[0] = is_prime[1] = false; // 0과 1은 소수가 아님

    for (int i = 2; i * i <= max; i++) {
        if (is_prime[i]) {
            for (int j = i * i; j <= max; j += i) {
                is_prime[j] = false;
            }
        }
    }

    return is_prime;
}

int main()
{
    int m, n, sum = 0;
    vector<int> sumv;
    cin >> m >> n;

    vector<bool> is_prime = sieve(n);

    for (int i = m; i <= n; i++)
    {
        if (is_prime[i])
        {
            sumv.push_back(i);
            sum += i;
        }
    }

    if (sumv.size() > 0) cout << sum << "\n" << sumv[0] << endl;
    else cout << -1 << endl;

    return 0;

}

https://www.acmicpc.net/problem/1978

 

소수를 찾는 문제이다. 나는 n이 100이하이고 n의 수도 1000이하라서 반복문을 순회하면서 소수인지 판별했으나

에라토스테네스의 체를 통해 소수를 미리 구해두고 확인하는 방식이 훨씬 효율적이다. 

밑은 에라토스테네스의 체를 설명한 그림이다. 소수를 찾고 그 배수는 모두 소수에서 제외시키는 방식으로 소수가 아닌것을 제외하면 된다.

https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4

 

정답코드 

#include "iostream"
#include "vector"
#include "cmath"

using namespace std;

bool isValue(int n)
{
	if (n == 1) return false;
	
	for (int i = 2; i < n; i++)
	{
		if (n % i == 0) return false;
	}

	return true;
}

vector<int> v;
int main()
{
	int n, cnt = 0;

	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int j;
		cin >> j;
		v.push_back(j);
	}

	for (int i = 0; i < v.size(); i++)
	{
		if (isValue(v[i])) cnt++;
	}

	cout << cnt << endl;
	return 0;
}

 

시간복잡도를 줄인 버전

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

// 에라토스테네스의 체로 소수 목록을 생성
vector<bool> sieve(int max) {
    vector<bool> is_prime(max + 1, true);
    is_prime[0] = is_prime[1] = false; // 0과 1은 소수가 아님

    for (int i = 2; i * i <= max; i++) {
        if (is_prime[i]) {
            for (int j = i * i; j <= max; j += i) {
                is_prime[j] = false;
            }
        }
    }

    return is_prime;
}

int main() {
    int n, count = 0;
    cin >> n;
    
    vector<int> numbers(n);
    for (int i = 0; i < n; i++) {
        cin >> numbers[i];
    }

    // 1000까지의 소수를 구하기 위해 에라토스테네스의 체 실행
    vector<bool> is_prime = sieve(1000);

    // 입력된 숫자들에 대해 소수인지 확인
    for (int i = 0; i < n; i++) {
        if (is_prime[numbers[i]]) {
            count++;
        }
    }

    cout << count << endl;
    return 0;
}

https://www.inflearn.com/course/lecture?courseSlug=directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98&unitId=161127&tab=curriculum

 

학습 페이지

 

www.inflearn.com

 



1.Global Shader 생성

오늘은 Shader 코드를 수정해보자

코드의 최상단을 보자면 Matrix와 Texture 등 변수가 선언되어있는데 내부적으로는 이게 한꺼번에 초기화가 되고있다.

하지만 이렇게 선언된 변수 중에 물체에 종속적인 부분과 그렇지 않은 부분도 있으며 코드상에 중복되는 부분도 많다.

오늘은 그런 부분을 고쳐줄 것이다.

shader에서도 헤더파일처럼 기존 헤더파일을 가져와서 쓸 수 있다. 

이를 통해 자주쓰는 쉐이더 코드를 모아둘 GlobalShader를 만들어주자

이때 그냥 카메라의 좌표를 사용하기에는  회전이 적용될 경우 회전에 적용되는 값도 들어가기 때문에 카메라의 월드 좌표는 카메라의 역행렬을 통해 구해준.  

Global.fx

#ifndef _GLOBAL_FX_
#define _GLOBAL_FX_

/////////////////
// ConstBuffer //
/////////////////

cbuffer GlobalBuffer
{
    matrix V;
    matrix P;
    matrix VP;
};

cbuffer TransformBuffer
{
    matrix W;
};

/////////////////
// VertexBuffer //
/////////////////

struct Vertex
{
    float4 position : POSITION;
};

struct VertexTexture
{
    float4 position : POSITION;
    float2 uv : TEXCOORD;
};

struct VertexColor
{
    float4 Position : POSITION;
    float4 Color : COLOR;
};

struct VertexTextureNormal
{
    float4 position : POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

/////////////////
// Vertexoutput //
/////////////////

struct VertexOutput
{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

/////////////////
// SamplerState //
/////////////////

SamplerState LinearSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState PointSampler
{
    Filter = MIN_MAG_MIP_POINT;
    AddressU = Wrap;
    AddressV = Wrap;
};

/////////////////////
// RasterizerState //
/////////////////////

//와이어프레임모드
RasterizerState FillModeWireFrame
{
    FillMode = Wireframe;
};

///////////
// Macro //
////////////

#define PASS_VP(name, vs, ps)                       \
pass name                                           \
{                                                   \
	SetVertexShader(CompileShader(vs_5_0, vs()));   \
	SetPixelShader(CompileShader(ps_5_0, ps()));    \
}                  


//////////////
// Function //
//////////////

#endif

 

이를 활용하는 쉐이더 코드도 만들어주자

GlobalTest.fx

#include "00. Global.fx"


VertexOutput VS(VertexTextureNormal input)
{
	VertexOutput output;
    output.position = mul(input.position,W);
    output.position = mul(output.position, VP);
	
    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) W);

	return output;
}

Texture2D Texture0;

float4 PS(VertexOutput input) : SV_TARGET
{
    //내적
    //return float4(1, 1, 1, 1) * dot(light, normal);
	//텍스처 -> 샘플링된 좌표 
    return Texture0.Sample(LinearSampler, input.uv);
}

technique11 T0
{
	//하나의 통로
    PASS_VP(P0, VS, PS)
};

 

그리고 만들어준 글로벌 쉐이더에 맞게 데이터를 버퍼에 저장하고 넘겨주는 RenderManager라는 클래스를 만들어주자

이때 ID3DX11EffectConstantBuffer를 활용하여 정보를 밀어넣어줄 것이다.

RenderManager.h

#pragma once
#include "ConstantBuffer.h"

class Shader;

struct GlobalDesc
{
	Matrix V = Matrix::Identity;
	Matrix P = Matrix::Identity;
	Matrix VP = Matrix::Identity;
};

struct TransformDesc
{
	Matrix W = Matrix::Identity;
};

class RenderManager
{
	DECLARE_SINGLE(RenderManager);

public:
	void Init(shared_ptr<Shader> shader);

	void PushGlobalData(const Matrix& view, const Matrix& projection);
	void PushTransformData(const TransformDesc& desc);

private:
	shared_ptr<Shader> _shader;

	//프레임마다 한번만 세팅
	GlobalDesc _globalDesc;
	shared_ptr<ConstantBuffer<GlobalDesc>> _globalBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _globalEffectBuffer;


	TransformDesc _transformDesc;
	shared_ptr<ConstantBuffer<TransformDesc>> _transformBuffer;
	//정보넘겨주기
	ComPtr<ID3DX11EffectConstantBuffer> _transformEffectBuffer;
};

 

RenderManager.cpp

#include "pch.h"
#include "RenderManager.h"

void RenderManager::Init(shared_ptr<Shader> shader)
{
	_shader = shader;

	_globalBuffer = make_shared<ConstantBuffer<GlobalDesc>>();
	_globalBuffer->Create();
	_globalEffectBuffer = _shader->GetConstantBuffer("GlobalBuffer");

	_transformBuffer = make_shared<ConstantBuffer<TransformDesc>>();
	_transformBuffer->Create();
	_transformEffectBuffer = _shader->GetConstantBuffer("TransformBuffer");
}

void RenderManager::PushGlobalData(const Matrix& view, const Matrix& projection)
{
	_globalDesc.V = view;
	_globalDesc.P = projection;
	_globalDesc.VP = view * projection;
	_globalBuffer->CopyData(_globalDesc);
	//쉐이더로 밀어넣기
	_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}

void RenderManager::PushTransformData(const TransformDesc& desc)
{
	_transformDesc = desc;
	_transformBuffer->CopyData(_transformDesc);
	_transformEffectBuffer->SetConstantBuffer(_transformBuffer->GetComPtr().Get());
}

 

새로 만들어준 매니저에 맞게 MeshRenderer 클래스 수정해주자

MeshRenderer.cpp

#include "pch.h"
#include "MeshRenderer.h"
#include "Camera.h"
#include "Game.h"
#include "Mesh.h"
#include "Shader.h"

MeshRenderer::MeshRenderer() : Super(ComponentType::MeshRenderer)
{

}

MeshRenderer::~MeshRenderer()
{

}

void MeshRenderer::Update()
{
	if (_mesh == nullptr || _texture == nullptr || _shader == nullptr)
		return;

	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());

	auto world = GetTransform()->GetWorldMatrix();
	RENDER->PushTransformData(TransformDesc{ world });
	
	uint32 stride = _mesh->GetVertexBuffer()->GetStride();
	uint32 offset = _mesh->GetVertexBuffer()->GetOffset();

	DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);

	_shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}

 

이제 실제 메인코드인 GlobalTestDemo의 코드를 수정해주자

GlobalTestDemo.h

#pragma once

#include "IExecute.h"
#include "Geometry.h"

class GlobalTestDemo : public IExecute
{
public:
	void Init() override;
	void Update() override;
	void Render() override;


	shared_ptr<Shader> _shader;
	
	// Object
	shared_ptr<GameObject> _obj;
	//Camera
	shared_ptr<GameObject> _camera;
};

GlobalTestDemo.cpp

#include "pch.h"
#include "10. GlobalTestDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void GlobalTestDemo::Init()
{
	_shader = make_shared<Shader>(L"08. GlobalTest.fx");

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		_obj->GetMeshRenderer()->SetShader(_shader);
	}
	{
		RESOURCES->Init();
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}

	RENDER->Init(_shader);
}

void GlobalTestDemo::Update()
{
	_camera->Update();

	RENDER->Update();
	_obj->Update();
}

void GlobalTestDemo::Render()
{
	
}

 

이렇게 해주면 이제 베이가 구가 나타난다.

 

 

2.Depth Stencil 생성

오늘은 3D에서 중요한 Depth Stencil을 추가해보자 

지금 만약 깊이가 다르게 물체를 2가지 생성해보면 현재 카메라의 위치가 달라질 수록 물체가 다르게 보이며 뒤에 

있는 오브젝트 큐브가 오히려 앞에 있는 것처럼 보이기도 한다.

 

이때 지금의 쉐이더에서는 깊이 정보를 제외하고 각 물체들의 정보만을 가지고 물체를 생성해주고 있는데 이때 깊이 정보 값을 추가 해주어야한다. 이렇게 해주기 위해 우리가 전체적인 그래픽이 필요한 것을 정의해줬던 Graphics 클래스에서

우리의 화면 정보와 깊이정보를 가지고 있을 변수를 선언하고 초기화해주자

이때 desc 부분에서 주석이 옆에 달린 부분을 주의하여 초기화해주자

Graphics.h

#pragma once


//그려지는 것에 필요한 것들
class Graphics
{
	DECLARE_SINGLE(Graphics);

public:
	void Init(HWND hwnd);

	void RenderBegin();
	void RenderEnd();

	ComPtr<ID3D11Device> GetDevice() { return _device; }
	ComPtr<ID3D11DeviceContext> GetDeviceContext() { return _deviceContext; }

private:
	void CreateDeviceAndSwapChain();
	void CreateRenderTargetView();
	void CreateDepthStencilView();
	void SetViewport();

private:
	HWND _hwnd = {};

	// Device & SwapChain
	//물체를 생성 및 바인딩
	ComPtr<ID3D11Device> _device = nullptr;
	ComPtr<ID3D11DeviceContext> _deviceContext = nullptr;
	ComPtr<IDXGISwapChain> _swapChain = nullptr;

	// RTV - 쉐이더에서 계산한거 그려주기
	ComPtr<ID3D11RenderTargetView> _renderTargetView;

	// DSC- 깊이 
	ComPtr<ID3D11Texture2D> _depthStencilTexture;
	ComPtr<ID3D11DepthStencilView> _depthStencilView;


	// Misc
	D3D11_VIEWPORT _viewport = { 0 };
};

Graphics.cpp

#include "pch.h"
#include "Graphics.h"

void Graphics::Init(HWND hwnd)
{
	_hwnd = hwnd;

	CreateDeviceAndSwapChain();
	CreateRenderTargetView();
	CreateDepthStencilView();
	SetViewport();
}

void Graphics::RenderBegin()
{
	_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), _depthStencilView.Get());
	_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), (float*)(&GAME->GetGameDesc().clearColor));
	_deviceContext->RSSetViewports(1, &_viewport);
}

void Graphics::RenderEnd()
{
	HRESULT hr = _swapChain->Present(1, 0);
	CHECK(hr);
}
void Graphics::CreateDeviceAndSwapChain()
{
	DXGI_SWAP_CHAIN_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	{
		desc.BufferDesc.Width = GAME->GetGameDesc().width;
		desc.BufferDesc.Height = GAME->GetGameDesc().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 hr = ::D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE,
		nullptr,
		0,
		nullptr,
		0,
		D3D11_SDK_VERSION,
		&desc,
		_swapChain.GetAddressOf(),
		_device.GetAddressOf(),
		nullptr,
		_deviceContext.GetAddressOf()
	);

	CHECK(hr);
}

void Graphics::CreateRenderTargetView()
{
	HRESULT hr;

	ComPtr<ID3D11Texture2D> backBuffer = nullptr;
	hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());
	CHECK(hr);

	hr = _device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());
	CHECK(hr);
}

void Graphics::CreateDepthStencilView()
{
	{
		//화면크기와 동일한 텍스처 생성
		D3D11_TEXTURE2D_DESC desc = { 0 };
		ZeroMemory(&desc, sizeof(desc));
		desc.Width = static_cast<uint32>(GAME->GetGameDesc().width);
		desc.Height = static_cast<uint32>(GAME->GetGameDesc().height);
		desc.MipLevels = 1;
		desc.ArraySize = 1;
		desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;		//
		desc.SampleDesc.Count = 1;
		desc.SampleDesc.Quality = 0;
		desc.Usage = D3D11_USAGE_DEFAULT;
		desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;		//2개만 신경써주면된다.
		desc.CPUAccessFlags = 0;
		desc.MiscFlags = 0;

		HRESULT hr = DEVICE->CreateTexture2D(&desc, nullptr, _depthStencilTexture.GetAddressOf());
		CHECK(hr);
	}
	
	{
		D3D11_DEPTH_STENCIL_VIEW_DESC desc;
		ZeroMemory(&desc, sizeof(desc));
		desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;		//
		desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
		desc.Texture2D.MipSlice = 0;

		HRESULT hr = DEVICE->CreateDepthStencilView(_depthStencilTexture.Get(), &desc, _depthStencilView.GetAddressOf());
		CHECK(hr);
	}
}

void Graphics::SetViewport()
{
	_viewport.TopLeftX = 0.0f;
	_viewport.TopLeftY = 0.0f;
	_viewport.Width = static_cast<float>(GAME->GetGameDesc().width);
	_viewport.Height = static_cast<float>(GAME->GetGameDesc().height);
	_viewport.MinDepth = 0.0f;
	_viewport.MaxDepth = 1.0f;
}

 

이렇게 만들어준 텍스처와 View는 OM, Output Merge에서 어떻게 사용할지 세팅해줄 수 있다. 

이렇게 해주면 우리가 만들어준 화면을 담고 있는 텍스처에 깊이값을 기록하게 된다. 이렇게 기록된 깊이 값을 통해 물체를

그려주게 된다. 

하지만 이렇게 실행하면 아무것도 나오지 않는데 이렇게 나오는 이유는 매프레임마다 깊이값을 다시 밀어줘야하는데 그것을 아직 안하고 있기 때문이다. 

이를 해결하기 위해 RenderBegin 함수에 스텐실뷰를 초기화해주는 함수를 추가해주자

_deviceContext->ClearDepthStencilView(_depthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);

 

이 초기화 함수는 같은 깊이 값에 다른 물체가 생성된다고 하면 중복되기 때문에 생성할 수 없기 때문에 이것을 방지하기 위하여 제일 끝인 1로 초기화를 해주는 것이다.

 

이렇게 해주면 깊이에 따른 물체의 순서와 크기가 제대로 그려진다.

 

 

 

https://www.acmicpc.net/problem/9506

 

약수를 구한 다음 약수의 총합이 본래의 수와 같으면 기존의 수를 저장한 벡터를 순서대로 출력해주면 된다.

시간복잡도를 줄이기 위해 약수가 대칭인 점을 이용하여 제곱근까지만 약수를 구해주자

 

 

정답코드

#include "iostream"
#include "vector"
#include "cmath"
#include "algorithm"

using namespace std;

int main()
{
	int n;
	while (true)
	{
		cin >> n;
		if (n == -1) return 0;

		vector<int> v;
		int sum = 0;
		for (int i = 1; i <= sqrt(n); i++)
		{
			if (n % i == 0)
			{
				v.push_back(i);
				if (i != 1 && i != n / i)  //n / i과 다를 경우
				{
					v.push_back(n / i); // 약수 n/i 추가
				}
				sum += i;  // 약수의 합에 추가
				if (i != 1 && i != n / i)
				{
					sum += n / i; // n / i가 약수일 경우 합에 추가
				}
			}
		}
		sort(v.begin(), v.end());
		// 완전수 판별
		if (sum == n)
		{
			cout << n << " = ";
			for (int i = 0; i < v.size(); i++)
			{
				cout << v[i];
				if (i != v.size() - 1)
				{
					cout << " + ";
				}
			}
			cout << endl;
		}
		else
		{
			cout << n << " is NOT perfect." << endl;
		}
	}
}

 



1.Height Map

오늘은 이제 높이가 있는 맵을 제작해 볼 것이다. 

이때 리소스에서 높이정보를 가져올 것이다.

리소스를 가져올 때, 비트 수준을 보고 이 비트에 맞게 파싱을 해와야한다.

 

이 리소스를 가져온 다음 높이를 지정해주면 되는 것이다. 이때 계산은 CPU로 해주도록 하자

이때 heightMap이라는 텍스처 변수를 통해 Heightmap 리소스를 받아온뒤 이 픽셀정보를 가지고 이 픽셀정보를 통해 

가져온 정보를 통해 geometry의 정점의 높이정보를 바꿔주고 이를 볼 수 있게 하자.

HeightDemo.cpp

#include "pch.h"
#include "07. HeightMapDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"

void HeightMapDemo::Init()
{
	_shader = make_shared<Shader>(L"06. Terrain.fx");

	//Texture
	_heightMap = RESOURCES->Load<Texture>(L"Height", L"..\\Resources\\Textures\\Terrain\\height.png");
	_texture = RESOURCES->Load<Texture>(L"Grass", L"..\\Resources\\Textures\\Terrain\\grass.jpg");

	const int32 width = _heightMap->GetSize().x;
	const int32 height = _heightMap->GetSize().y;

	const DirectX::ScratchImage& info = _heightMap->GetInfo();
	uint8* pixelBuffer = info.GetPixels();

	//Object
	_geometry = make_shared<Geometry<VertexTextureData>>();
	GeometryHelper::CreateGrid(_geometry, width, height);

	//CPU - 좌표설정
	{
		vector<VertexTextureData>& v = const_cast<vector<VertexTextureData>&>(_geometry->GetVertices());

		for (int32 z = 0; z < height; z++)
		{
			for (int32 x = 0; x < width; x++)
			{
				//1치원배열일때의 순서
				int32 idx = width * z + x;

				//정보에 해당하는 높이구하기
				uint8 height = pixelBuffer[idx] / 255.f * 25.f;
				v[idx].position.y = height;		//높이 보정
			}
		}
	}

	//고속복사
	_vertexBuffer = make_shared<VertexBuffer>();
	_vertexBuffer->Create(_geometry->GetVertices());
	_indexBuffer = make_shared<IndexBuffer>();
	_indexBuffer->Create(_geometry->GetIndices());

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());
	_camera->GetTransform()->SetPosition(Vec3(0.f, 5.f, 0.f));
	_camera->GetTransform()->SetRotation(Vec3(25.f, 0.f, 0.f));
	
}

void HeightMapDemo::Update()
{
	_camera->Update();
}

void HeightMapDemo::Render()
{
	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());


	uint32 stride = _vertexBuffer->GetStride();
	uint32 offset = _vertexBuffer->GetOffset();

	DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
	
	//_buffer->GetCount()
	_shader->DrawIndexed(0, 0, _indexBuffer->GetCount(), 0, 0);
}

 

이렇게 한 뒤 메인코드를 바꿔주면 높이 맵 텍스처에 따른 높이가 적용된 버전의 Grid가 출력된다.

 

 

2.Normal Vector

 

이제 도형들의 Normal Vector를 구해볼 것이다. 일단 이 Normal Vector는 광원과의 각도를 이용하여 빛에 대한 연산을 

할 때 필요하다. 

빛의 반사를 생각해보자면 이때 Normal Vector와 광원에서 면까지의 방향의 반대를 내적해주게 되면 빛의 세기를 계산

할 수 있는데 이 값은 Cos 값에 따라 달라진다. 만약 빛과 normal vector가 이루는 각 90도가 된다면 최소 값이 될 것이고 수직일 때 최대가 된다.

 

큐브와 같은 물체들은 각 면에 따른 노멀 벡터를 가지고 있다.

구와 같은 물체는 각 위치가 노멀벡터가 되는데 중심에서 자기 위치까지의 벡터가 노멀 벡터가 되기 때문이다.

 

shader 단계에서 normal을 Vertex Shader 단계에서 입력받고 회전을 고려하여 출력을 해주어야하는 데 이때 cpu 단계

에서 계산할 때 TransformNormal을 사용했었는데 이 함수는 x y z 와 마지막 값에서 0을 사용하도록 해주는 함수인다.

이 마지막 값이 중요한 이유는 노멀 벡터는 평행이동과는 상관이 없으며 스케일링과 회전에만 영향을 받기 때문이다.

여러 도형을 만들어 볼 것인데 이때, Normal 벡터를 가지고 있는 VertexTextureNormalData 구조체 버전을 사용하자

VertexData.h

struct VertexTextureNormalData
{
	Vec3 position = { 0, 0, 0 };
	Vec2 uv = { 0, 0 };
	Vec3 normal = { 0, 0, 0 };
};

GeometryHelper.cpp

void GeometryHelper::CreateQuad(shared_ptr<Geometry<VertexTextureNormalData>> geometry)
{
	vector<VertexTextureNormalData> vtx;
	vtx.resize(4);

	vtx[0].position = Vec3(-0.5f, -0.5f, 0.f);
	vtx[0].uv = Vec2(0.f, 1.f);
	//Look 벡터의 반대방향
	vtx[0].normal = Vec3(0.f, 0.f, -1.f);
	vtx[1].position = Vec3(-0.5f, 0.5f, 0.f);
	vtx[1].uv = Vec2(0.f, 0.f);
	vtx[1].normal = Vec3(0.f, 0.f, -1.f);
	vtx[2].position = Vec3(0.5f, -0.5f, 0.f);
	vtx[2].uv = Vec2(1.f, 1.f);
	vtx[2].normal = Vec3(0.f, 0.f, -1.f);
	vtx[3].position = Vec3(0.5f, 0.5f, 0.f);
	vtx[3].uv = Vec2(1.f, 0.f);
	vtx[2].normal = Vec3(0.f, 0.f, -1.f);
	geometry->SetVertices(vtx);

	vector<uint32> idx = { 0, 1, 2, 2, 1, 3 };
	geometry->SetIndices(idx);
}

void GeometryHelper::CreateCube(shared_ptr<Geometry<VertexTextureNormalData>> geometry)
{
	float w2 = 0.5f;
	float h2 = 0.5f;
	float d2 = 0.5f;

	vector<VertexTextureNormalData> vtx(24);

	// 앞면
	vtx[0] = VertexTextureNormalData(Vec3(-w2, -h2, -d2), Vec2(0.0f, 1.0f), Vec3(0.0f, 0.0f, -1.0f));
	vtx[1] = VertexTextureNormalData(Vec3(-w2, +h2, -d2), Vec2(0.0f, 0.0f), Vec3(0.0f, 0.0f, -1.0f));
	vtx[2] = VertexTextureNormalData(Vec3(+w2, +h2, -d2), Vec2(1.0f, 0.0f), Vec3(0.0f, 0.0f, -1.0f));
	vtx[3] = VertexTextureNormalData(Vec3(+w2, -h2, -d2), Vec2(1.0f, 1.0f), Vec3(0.0f, 0.0f, -1.0f));
	// 뒷면
	vtx[4] = VertexTextureNormalData(Vec3(-w2, -h2, +d2), Vec2(1.0f, 1.0f), Vec3(0.0f, 0.0f, 1.0f));
	vtx[5] = VertexTextureNormalData(Vec3(+w2, -h2, +d2), Vec2(0.0f, 1.0f), Vec3(0.0f, 0.0f, 1.0f));
	vtx[6] = VertexTextureNormalData(Vec3(+w2, +h2, +d2), Vec2(0.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f));
	vtx[7] = VertexTextureNormalData(Vec3(-w2, +h2, +d2), Vec2(1.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f));
	// 윗면
	vtx[8] = VertexTextureNormalData(Vec3(-w2, +h2, -d2), Vec2(0.0f, 1.0f), Vec3(0.0f, 1.0f, 0.0f));
	vtx[9] = VertexTextureNormalData(Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
	vtx[10] = VertexTextureNormalData(Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
	vtx[11] = VertexTextureNormalData(Vec3(+w2, +h2, -d2), Vec2(1.0f, 1.0f), Vec3(0.0f, 1.0f, 0.0f));
	// 아랫면
	vtx[12] = VertexTextureNormalData(Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f), Vec3(0.0f, -1.0f, 0.0f));
	vtx[13] = VertexTextureNormalData(Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f), Vec3(0.0f, -1.0f, 0.0f));
	vtx[14] = VertexTextureNormalData(Vec3(+w2, -h2, +d2), Vec2(0.0f, 0.0f), Vec3(0.0f, -1.0f, 0.0f));
	vtx[15] = VertexTextureNormalData(Vec3(-w2, -h2, +d2), Vec2(1.0f, 0.0f), Vec3(0.0f, -1.0f, 0.0f));
	// 왼쪽면
	vtx[16] = VertexTextureNormalData(Vec3(-w2, -h2, +d2), Vec2(0.0f, 1.0f), Vec3(-1.0f, 0.0f, 0.0f));
	vtx[17] = VertexTextureNormalData(Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f), Vec3(-1.0f, 0.0f, 0.0f));
	vtx[18] = VertexTextureNormalData(Vec3(-w2, +h2, -d2), Vec2(1.0f, 0.0f), Vec3(-1.0f, 0.0f, 0.0f));
	vtx[19] = VertexTextureNormalData(Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f), Vec3(-1.0f, 0.0f, 0.0f));
	// 오른쪽면
	vtx[20] = VertexTextureNormalData(Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f));
	vtx[21] = VertexTextureNormalData(Vec3(+w2, +h2, -d2), Vec2(0.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f));
	vtx[22] = VertexTextureNormalData(Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f), Vec3(1.0f, 0.0f, 0.0f));
	vtx[23] = VertexTextureNormalData(Vec3(+w2, -h2, +d2), Vec2(1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f));

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 앞면
	idx[0] = 0; idx[1] = 1; idx[2] = 2;
	idx[3] = 0; idx[4] = 2; idx[5] = 3;
	// 뒷면
	idx[6] = 4; idx[7] = 5; idx[8] = 6;
	idx[9] = 4; idx[10] = 6; idx[11] = 7;
	// 윗면
	idx[12] = 8; idx[13] = 9; idx[14] = 10;
	idx[15] = 8; idx[16] = 10; idx[17] = 11;
	// 아랫면
	idx[18] = 12; idx[19] = 13; idx[20] = 14;
	idx[21] = 12; idx[22] = 14; idx[23] = 15;
	// 왼쪽면
	idx[24] = 16; idx[25] = 17; idx[26] = 18;
	idx[27] = 16; idx[28] = 18; idx[29] = 19;
	// 오른쪽면
	idx[30] = 20; idx[31] = 21; idx[32] = 22;
	idx[33] = 20; idx[34] = 22; idx[35] = 23;

	geometry->SetIndices(idx);
}

void GeometryHelper::CreateGrid(shared_ptr<Geometry<VertexTextureNormalData>> geometry, int32 sizeX, int32 sizeZ)
{
	vector<VertexTextureNormalData> vtx;

	for (int32 z = 0; z < sizeZ + 1; z++)
	{
		for (int32 x = 0; x < sizeX + 1; x++)
		{
			VertexTextureNormalData v;
			v.position = Vec3(static_cast<float>(x), 0, static_cast<float>(z));
			v.uv = Vec2(static_cast<float>(x), static_cast<float>(sizeZ - z));
			v.normal = Vec3(0.f, 1.f, 0.f);

			vtx.push_back(v);
		}
	}

	geometry->SetVertices(vtx);

	vector<uint32> idx;

	for (int32 z = 0; z < sizeZ; z++)
	{
		for (int32 x = 0; x < sizeX; x++)
		{
			//  [0]
			//   |	\
			//  [2] - [1]
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z)+(x));
			//  [1] - [2]
			//   	\  |
			//		  [0]
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z + 1) + (x + 1));
		}
	}

	geometry->SetIndices(idx);
}

void GeometryHelper::CreateSphere(shared_ptr<Geometry<VertexTextureNormalData>> geometry)
{
	float radius = 0.5f; // 구의 반지름
	uint32 stackCount = 20; // 가로 분할
	uint32 sliceCount = 20; // 세로 분할

	vector<VertexTextureNormalData> vtx;

	VertexTextureNormalData v;

	// 북극
	v.position = Vec3(0.0f, radius, 0.0f);
	v.uv = Vec2(0.5f, 0.0f);
	v.normal = v.position;
	v.normal.Normalize();
	vtx.push_back(v);

	float stackAngle = XM_PI / stackCount;
	float sliceAngle = XM_2PI / sliceCount;

	float deltaU = 1.f / static_cast<float>(sliceCount);
	float deltaV = 1.f / static_cast<float>(stackCount);

	// 고리마다 돌면서 정점을 계산한다 (북극/남극 단일점은 고리가 X)
	for (uint32 y = 1; y <= stackCount - 1; ++y)
	{
		float phi = y * stackAngle;

		// 고리에 위치한 정점
		for (uint32 x = 0; x <= sliceCount; ++x)
		{
			float theta = x * sliceAngle;

			v.position.x = radius * sinf(phi) * cosf(theta);
			v.position.y = radius * cosf(phi);
			v.position.z = radius * sinf(phi) * sinf(theta);

			v.uv = Vec2(deltaU * x, deltaV * y);

			//각 위치마다 자신이 노말벡터
			v.normal = v.position;
			v.normal.Normalize();

			vtx.push_back(v);
		}
	}

	// 남극
	v.position = Vec3(0.0f, -radius, 0.0f);
	v.uv = Vec2(0.5f, 1.0f);
	v.normal = v.position;
	v.normal.Normalize();
	vtx.push_back(v);

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 북극 인덱스
	for (uint32 i = 0; i <= sliceCount; ++i)
	{
		//  [0]
		//   |  \
		//  [i+1]-[i+2]
		idx.push_back(0);
		idx.push_back(i + 2);
		idx.push_back(i + 1);
	}

	// 몸통 인덱스
	uint32 ringVertexCount = sliceCount + 1;
	for (uint32 y = 0; y < stackCount - 2; ++y)
	{
		for (uint32 x = 0; x < sliceCount; ++x)
		{
			//  [y, x]-[y, x+1]
			//  |		/
			//  [y+1, x]
			idx.push_back(1 + (y)*ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			//		 [y, x+1]
			//		 /	  |
			//  [y+1, x]-[y+1, x+1]
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x + 1));
		}
	}

	// 남극 인덱스
	uint32 bottomIndex = static_cast<uint32>(vtx.size()) - 1;
	uint32 lastRingStartIndex = bottomIndex - ringVertexCount;
	for (uint32 i = 0; i < sliceCount; ++i)
	{
		//  [last+i]-[last+i+1]
		//  |      /
		//  [bottom]
		idx.push_back(bottomIndex);
		idx.push_back(lastRingStartIndex + i);
		idx.push_back(lastRingStartIndex + i + 1);
	}

	geometry->SetIndices(idx);
}

 

Normal.fx

matrix World;
matrix View;
matrix Projection;
Texture2D Texture0;
float3 LightDir;

struct VertexInput
{
	float4 position : POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

struct VertexOutput
{
	float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};

VertexOutput VS(VertexInput input)
{
	VertexOutput output;
    output.position = mul(input.position,World);
    output.position = mul(output.position, View);
    output.position = mul(output.position, Projection);
	

    output.uv = input.uv;
    //회전고려
    output.normal = mul(input.normal, (float3x3) World);

	return output;
}

SamplerState Sampler0;


float4 PS(VertexOutput input) : SV_TARGET
{
    float3 normal = normalize(input.normal);
    float3 light = -LightDir;
    
    //내적
    //return float4(1, 1, 1, 1) * dot(light, normal);
    
	//텍스처 -> 샘플링된 좌표 
    return Texture0.Sample(Sampler0, input.uv) * dot(light, normal);
}

//와이어프레임모드
RasterizerState FillModeWireFrame
{
    FillMode = Wireframe;
};


technique11 T0
{
	//하나의 통로
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0, VS()));
		SetPixelShader(CompileShader(ps_5_0, PS()));
	}

    pass P1
    {
        SetRasterizerState(FillModeWireFrame);
        SetVertexShader(CompileShader(vs_5_0, VS()));
        SetPixelShader(CompileShader(ps_5_0, PS()));
    }
};

 

이렇게하고 큐브로 먼저 테스트를 해보자 이때 빛은 임의로 설정해주자 지금은 오른쪽에 광원이 있다고 가정하자

 

NormalDemo.h

#pragma once

#include "IExecute.h"
#include "Geometry.h"

class NormalDemo : public IExecute
{
public:
	void Init() override;
	void Update() override;
	void Render() override;


	shared_ptr<Shader> _shader;
	
	// Object
	shared_ptr<Geometry<VertexTextureNormalData>> _geometry;
	shared_ptr<VertexBuffer> _vertexBuffer;
	shared_ptr<IndexBuffer> _indexBuffer;
	Matrix _world=Matrix::Identity;

	//Camera
	shared_ptr<GameObject> _camera;

	shared_ptr<Texture> _texture;

	//빛
	Vec3 _lightDir = Vec3(-1.f, 0.f, 0.f);
};

NormalDemo.cpp

#include "pch.h"
#include "08. NormalDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"

void NormalDemo::Init()
{
	_shader = make_shared<Shader>(L"07. Normal.fx");


	//Object
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateCube(_geometry);
	//GeometryHelper::CreateSphere(_geometry);

	//고속복사
	_vertexBuffer = make_shared<VertexBuffer>();
	_vertexBuffer->Create(_geometry->GetVertices());
	_indexBuffer = make_shared<IndexBuffer>();
	_indexBuffer->Create(_geometry->GetIndices());

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());
	//_camera->GetTransform()->SetPosition(Vec3(0.f, 5.f, 0.f));
	//_camera->GetTransform()->SetRotation(Vec3(25.f, 0.f, 0.f));

	//Texture
	_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
	
}

void NormalDemo::Update()
{
	_camera->Update();
}

void NormalDemo::Render()
{
	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
	_shader->GetVector("LightDir")->SetFloatVector((float*)&_lightDir);


	uint32 stride = _vertexBuffer->GetStride();
	uint32 offset = _vertexBuffer->GetOffset();

	DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
	
	//_buffer->GetCount()
	_shader->DrawIndexed(0, 0, _indexBuffer->GetCount(), 0, 0);
}

 

이렇게 해주면 오른쪽에만 텍스처가 보이게 된다.

 

이것을 구에 적용하면 좀 더 빛이 잘 적용되는 모습을 볼 수 있다.

 

3.Mesh

 

2D에서 사용하던 Mesh 부분을 이제 3D에서도 적용해보자 

 

geometry, vertexbuffer, indexbuffer, world matrix 부분을 하나로 모아서 Mesh라는 리소스로 만들어서 관리할 수 있도록 하자.

코드를 보자면 이 Mesh 클래스에서 geometry, vertexbuffer, indexbuffer 이 세개의 변수를 관리하고 이 클래스의 함수를 통해 도형을 만들어주게 되는데 이때 CreateBuffers 라는 함수를 만들어서 도형을 만든 다음 이 정보를 GPU쪽으로 넘겨줄 수 있게 버퍼를 초기화해주고 정보를 넘겨줄 수 있도록 하자.

Mesh.h

#pragma once
#include "ResourceBase.h"
#include "Geometry.h"

class Mesh : public ResourceBase
{
    using Super = ResourceBase;

public:
    Mesh();
    virtual ~Mesh();

	void CreateQuad();
	void CreateCube();
	void CreateGrid(int32 sizeX, int32 sizeZ);
	void CreateSphere();

	shared_ptr<VertexBuffer> GetVertexBuffer() { return _vertexBuffer; }
	shared_ptr<IndexBuffer> GetIndexBuffer() { return _indexBuffer; }

private:
	void CreateBuffers();

private:
	// Mesh
	shared_ptr<Geometry<VertexTextureNormalData>> _geometry;
	shared_ptr<VertexBuffer> _vertexBuffer;
	shared_ptr<IndexBuffer> _indexBuffer;
};

Mesh.cpp

#include "pch.h"
#include "Mesh.h"
#include "GeometryHelper.h"

Mesh::Mesh()  : Super(ResourceType::Mesh)
{

}

Mesh::~Mesh()
{

}

void Mesh::CreateQuad()
{
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateQuad(_geometry);
	CreateBuffers();
}

void Mesh::CreateCube()
{
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateCube(_geometry);
	CreateBuffers();
}

void Mesh::CreateGrid(int32 sizeX, int32 sizeZ)
{
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateGrid(_geometry, sizeX, sizeZ);
	CreateBuffers();
}

void Mesh::CreateSphere()
{
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateSphere(_geometry);
	CreateBuffers();
}

void Mesh::CreateBuffers()
{
	_vertexBuffer = make_shared<VertexBuffer>();
	_vertexBuffer->Create(_geometry->GetVertices());
	_indexBuffer = make_shared<IndexBuffer>();
	_indexBuffer->Create(_geometry->GetIndices());
}

 

그리고 이 매쉬가 오브젝트의 컴포넌트로 사용될 수 있게 MeshRenderer라는 클래스를 만들어주자. 이때 기존의 Demo

에서 사용하던 Render부분을 전부 가지고 와서 MeshRenderer에서 사용하게 만들어주면 된다.

MeshRenderer.h

#pragma once
#include "Component.h"

class Mesh;
class Shader;

class MeshRenderer : public Component
{
	using Super = Component;
public:
	MeshRenderer();
	virtual ~MeshRenderer();

	virtual void Update() override;

	void SetMesh(shared_ptr<Mesh> mesh) { _mesh = mesh; }
	void SetTexture(shared_ptr<Texture> texture) { _texture = texture; }
	void SetShader(shared_ptr<Shader> shader) { _shader = shader; }

private:
	shared_ptr<Mesh> _mesh;
	shared_ptr<Texture> _texture;
	shared_ptr<Shader> _shader;
};

MeshRenderer.cpp

#include "pch.h"
#include "MeshRenderer.h"
#include "Camera.h"
#include "Game.h"
#include "Mesh.h"
#include "Shader.h"

MeshRenderer::MeshRenderer() : Super(ComponentType::MeshRenderer)
{

}

MeshRenderer::~MeshRenderer()
{

}

void MeshRenderer::Update()
{
	if (_mesh == nullptr || _texture == nullptr || _shader == nullptr)
		return;

	auto world = GetTransform()->GetWorldMatrix();
	_shader->GetMatrix("World")->SetMatrix((float*)&world);
	
	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
	
	// TEMP
	Vec3 lightDir = {0.f, 0.f, 1.f};
	_shader->GetVector("LightDir")->SetFloatVector((float*)&lightDir);

	uint32 stride = _mesh->GetVertexBuffer()->GetStride();
	uint32 offset = _mesh->GetVertexBuffer()->GetOffset();

	DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);

	_shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}

 

이제 메인이 되는 코드에서 오브젝트를 생성한 뒤 MeshRenderer를 컴포넌트로 붙여주고 shader,mesh,texture를 붙여주자

MeshDemo.h

#pragma once

#include "IExecute.h"
#include "Geometry.h"

class MeshDemo : public IExecute
{
public:
	void Init() override;
	void Update() override;
	void Render() override;


	shared_ptr<Shader> _shader;
	
	// Object
	shared_ptr<GameObject> _obj;
	//Camera
	shared_ptr<GameObject> _camera;
};

MeshDemo.cpp

#include "pch.h"
#include "09. MeshDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"
#include "MeshRenderer.h"

void MeshDemo::Init()
{


	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());

	
	

	//Object
	_obj = make_shared<GameObject>();
	_obj->GetOrAddTransform();
	_obj->AddComponent(make_shared<MeshRenderer>());
	{
		auto shader = make_shared<Shader>(L"07. Normal.fx");
		_obj->GetMeshRenderer()->SetShader(shader);
	}
	{
		RESOURCES->Init();
		auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
		_obj->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		//Texture
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj->GetMeshRenderer()->SetTexture(texture);
	}
}

void MeshDemo::Update()
{
	_camera->Update();
	_obj->Update();
}

void MeshDemo::Render()
{
	
}

 

이렇게 해주면 이제 meshrenderer에 해당하는 부분이 하나의 클래스로 작동하여 훨씬 관리하기 편해졌다.

+ Recent posts