DirectX

45. Light, Material_Diffuse

devRiripong 2024. 2. 13.
반응형

Normal에 대해 얘기할 때 해본 기억이 있다.

 

diffuse는 분산광이라고 하는데 일반적인 조명이랑 유사하다고 보면 된다.

물체의 표면에서 분산되어서 눈으로 들어오는 빛

각도에 따라서 밝기가 다르다는 특징이 있다.

각도에 따른 빛의 세기의 공식을 만든 사람이 람베르트라서 람베르트 공식이라고 한다.

normal 방향과 일치할 때 가장 세게 빛을 받고

normal 방향과 90도 방향으로 오면 빛의 영향을 못 받는다.

뒤에서 올 때는 아예 못 받는다.

cos을 이용한다.

-L와 n벡터와 내적을 한다.

L와 n은 단위 벡터이기 때문에 cos사이각이 나온다.

cos 특성이 파동 그래프이기 때문에 0도일 때 1, 90도 일 때 0이 나온다.

구는 각 점의 포지션 자체가 방향벡터였다.

이걸 코드로 옮겨 본다.

 

1. 10. Lighting_Diffuse.fx생성 후 DiffuseDemo::Init에서 세팅하고, 13. Diffse_Demo클래스 생성 후 Main에 세팅하기

 

탐색기에서 09. Lighting_Ambient.fx를 복제하고 이름을 10. Lighting_Diffuse.fx로 바꿔준다. Client/Shader/Week2에 넣는다.

 

탐색기에서 12. AmbientDemo도 복제하고 이름을 13. Diffse_Demo로 바꾸고, Client/Game/Week2에 넣는다. 코드를 DiffuseDemo에 맞게 수정한다.

DiffuseDemo::Init()에서

_shader = make_shared<Shader>(L"10. Lighting_Diffuse.fx");

세팅하고,

 

Main.cpp에서

#include "13. DiffuseDemo.h"
desc.app = make_shared<DiffuseDemo>(); // 실행 단위

이렇게 세팅해 주고 시작을 한다.

Client를 빌드하면 문제가 없다.

 

2. 10. Lighting_Diffuse.fx 쉐이더 작성하기

1) LightDir(방향), LightDiffuse(색), MaterialDiffuse(퍼센티지) 3 총사 선언

10. Lighting_Diffuse.fx에서

float3 LightDir;

이걸 추가해서 빛에 대한 방향을 정해줘야 한다. 태양 빛의 방향이 보통 LightDir이 된다.

float4 LightDiffuse;

또 이걸 추가하는데, 색상이 있을 것이다.

float4 MaterialDiffuse;

물체가 받아들이는 빛의 퍼센티지의 느낌이다.

 

기존에 있던

float4 LightAmbient; 
float4 MaterialAmbient;

는 삭제한다.

 

float3 LightDir; 
float4 LightDiffuse; 
float4 MaterialDiffuse;

방향, 색상, 머테리얼의 세트는 모든 조명에 대해서 등장하게 된다.

 

2) Texture0의 이름을 DiffuseMap으로 변경하고 MeshRenderer::Update()에서 값을 세팅하는 부분을 바뀐 이름에 맞게 수정하기

Texture2D Texture0;

이걸로 받았던 게 사실은

Texture2D DiffuseMap;

DiffuseMap이었던 거다. 이름을 이걸로 바꾼다.

 

이제 PS에서

// Diffuse (분산광)

// 물체의 표면에서 분산되어 눈으로 바로 들어오는 빛

// 각도에 따라 발기가 다르다 (Lambert 공식)

을 적용해 준다.

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = DiffuseMap.Sample(LinearSampler, input.uv); 
    
    return color;
}

이게 지금까지 사용하던 공식이었다.

 

void MeshRenderer::Update()를 복제한 후 하나는 주석처리를 하고,

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

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

	auto world = GetTransform()->GetWorldMatrix();
	RENDER->PushTransformData(TransformDesc{ world }); 
	//_shader->GetMatrix("World")->SetMatrix((float*)&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);
}

_shader->GetSRV에 들어갈 인자를 DiffuseMap으로 바꾼다.

 

Engine을 빌드하면 빌드가 된다.

 

지금까지 물체에다가 발라주는 그 맵이 사실은 DiffuseMap이었다.

DiffuseLighting이라는 게 결국 그 물체의 색상을 우리가 그려주는 가장 직관적인 조명이라고 볼 수 있다.

그 부분을 케어해 주기 위해서 만들어 준 것이다.

 

실행을 하면

기본적인 방식이고, 다만 사실상 조명이 없는 상태에서 물체가 자신의 색상을 100% 발휘하고 있을 때의 색상을 이렇게 보여주고 있는 상황이다.

 

3. PS에서 LightDir, LightDiffuse, MaterialDiffuse를 적용하기

적용시켜야 하는 건 람베르트 공식을 이용해 가지고 빛이 들어오는 방향에 따라서 사실상 뭔가가 달라져야 한다가 되는 건데,

PS에서

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;
}

일단 Lighting을 세팅해야 한다.

 

4. DiffuseDemo::Update에서 10. Lighting_Diffuse.fx 쉐이더에 있는 LightDir, LightDiffuse, MaterialDiffuse의 값을 세팅하기

DiffuseDemo::Update에서

10. Lighting_Diffuse.fx 쉐이더의 LightDir, LightDiffuse, MaterialDiffuse에 값을 세팅한다. 

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();

	}
}

이런 느낌으로 만들어 주면 된다.

 

5. DiffuseDemo::Update에서 LightDiffuse, lightDir, MaterialDiffuse의 값을 수정하며 테스트하기 

1) lightDir{ 1.f, -1.f, 1.f }, lightDiffuse{ 1.f, 1.f, 1.f, 1.f }, material(1.f)로 테스트하기

DiffuseDemo 가지고 여러 가지로 장난을 치면 된다.

실행을 해보면

1차적으로 오른쪽 아래 깊이 방향으로 가고 있으니까 바라보는 방향으로 가기 때문에 이렇게 명암이 진다.

 

큐브는 왜 효과가 미미할까

 

큐브는 normal 벡터가 면에 하나씩으로 균일하기 때문이다.

 

나중에 Normal mapping으로 극복할 수 있긴 하다.

 

2) lightDir{ 1.f, 0.f, 0.f }, lightDiffuse{ 1.f, 1.f, 1.f, 1.f }, material(1.f)로 테스트하기

빛의 방향을 바꿔서 테스트해 보자.

Vec3 lightDir{ 1.f, 0.f, 0.f };

이렇게 바꿔준다면 

 

오른쪽으로 빛을 쏘기 때문에 카메라를 이동해서 보면 왼쪽 면만 보이고 있다는 걸 알 수 있다. 오른쪽 면은 보이지 않는다.

 

3) lightDir{ 1.f, 0.f, 0.f }, lightDiffuse{ 1.f, 0.f, 0.f, 0.f }, material(1.f)로 테스트하기

color = color * value * LightDiffuse * MaterialDiffuse;

여기서 LightDiffuse * MaterialDiffuse가 없어도 되긴 하겠지만,

빛도 자신만의 색상이 있을 거고,

Vec4 lightDiffuse{ 1.f, 0.f, 0.f, 0.f };

이렇게 빨간색으로 되어 있다면

이렇게 효과가 나타난다.

 

4) lightDir{ 1.f, 0.f, 0.f }, lightDiffuse{ 1.f }, material( 1.f, 0.f, 0.f, 0.f )로 테스트하기

다시 lightDiffuse를 하얀색 빛으로 되돌리고, 이번엔 MaterialDiffuse가

Vec4 material(1.f, 0.f, 0.f, 0.f);

빨간색만 받아주고 나머지 값들은 거부하겠다고 하면

하얀색 빛인데도 빨간색으로 나온다.

 

material은 빛을 받아주는 퍼센티지라고 보면 되고,

lightDiffuse는 light 자체의 색이라고 보면 된다.

 

다시 material도 1.f으로 바꾼다.

 

6. 어두운 부분도 반사광을 구현해서 보이게 하려면 어떻게 해야 할까

왼쪽에서 오른쪽으로 빛이 비친다고 아예 절반이 안 보이는 건 어색하다.

태양이 왼쪽에 있어도 물리적으로 봤을 때 어디 벽 같은데 튕겨서 뒤에 돌아오기도 하기 때문에 오른쪽이 은은하게 보이는 게 정상이다.

그렇게 하려면 어떻게 해야 할까?

 

Ambient는 모든 얘들이 빛의 방향과 무관하게 균일한 효과를 나타내는 거라고 했다.

Ambient를 추가해서 합치면 오른쪽도 살짝 보이는 걸 만들 수 있을 것이다. 이런 식으로 빛을 섞는 것이다.

섞는 건 3개를 다 해보고 섞을 것이다.

섞는 건 만든 결과물에 곱셈이 아닌 덧셈을 해줄 것이다.

지금까지는 곱셈으로 비율을 깎고 있었는데,

3 총사 색상을 덧셈으로 합치게 될 것이다.

 

합치다가 값이 커지면 하얀색으로 가게 된다.

값들을 1이 아니라 조절해야 할 필요가 생길 수 있다.

Vec4 lightDiffuse{ 0.5f};

이런 식으로 여러 수치를 조절해서 응용을 하게 될 것이다.

 

다음 시간은 툴 I’m GUI를 이용해서 조절할 것이기 때문에 코드에서 수치를 조절해서 빌드하기보다는 툴로 봐서 조절하는 게 좀 더 편하다.

 

 7. 맺음말 

DiffuseLighting에 대해 알아봤다.

가장 일반적인 조명에 가깝다.

    float value = dot(-LightDir, normalize(input.normal));

이 공식 보자마자 무슨 뜻인지 알 수 있어야 한다.

이런 식이 엔진에도 나온다.

기본기 수학을 이해할 수 있어야 한다.

반응형

'DirectX' 카테고리의 다른 글

47. Light, Material_Emissive  (0) 2024.02.14
46. Light, Material_Specular  (0) 2024.02.13
44. Light, Material_Ambient  (0) 2024.02.13
43. Light, Material_Depth Stencil View  (0) 2024.02.11
42. Light, Material_Global Shader  (0) 2024.02.11

댓글