학습 페이지
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()
{
}
'게임공부 > Directx11' 카테고리의 다른 글
[Directx11][C++][3D]7. Assimp(이론 및 설정) (0) | 2024.09.09 |
---|---|
[Directx11][C++][3D]6. Normal Mapping (4) | 2024.09.07 |
[Directx11][C++][3D]5. Light (0) | 2024.09.04 |
[Directx11][C++][3D]4. Global Shader (0) | 2024.09.02 |
[Directx11][C++][3D]3. 도형 다루기 (0) | 2024.08.30 |