[DirectX11]

Light #1 - Ambient

럭키🍀 2024. 8. 27. 10:29

조명? 빛 4총사에 대해서 알아볼 것이다.

 

 

사물이 사람의 눈에 보이는 원리는 빛이 사물에 반사되어 보이는데, 빛이 사물에 닿았을때 일종의 필터처럼 어떤 색의 빛으로 반사하느냐에 따라 색이 다르게 보인다.

다만 현실에서는 빛이 넓게 비춰지고 반사된 빛이 다른곳에 또 다중으로 반사되는데 이를 게임 엔진에서 똑같이 만드는것은 무리라 최소한의 연산의로 그럴듯하게 만드는게 목표이다.

 

 

게임은 영화나 애니메이션과 다르게 매 프레임마다 카메라의 위치가 바뀌고 조명의 위치가 바뀌는 실시간 렌더링이 필요하니 연산 규모를 축소시키는것이 목표인것이다. 요즘에는 물리기반렌더링(PBR) 기법등이 새로 나왔지만 환경적인 제약이 있어 특히 모바일에서는 사용하지 못한다. 그럼 기본이 되는 4가지 기법 Ambient, Diffuse, Specular, Emessive에 대해서 알아보자. 이 모두를 선택취사하는게 아니고 조립해서 한번에 사용해야한다.

 

Ambient, Diffuse, Specular, Emessive중 Emessive는 옵션같은것이고 각 역할을 요약하자면 아래와 같다.

  • Ambient: 환경광. 빛이 다중 반사되어 모든 사물에 조금은 빛을 받고있다.
  • Diffuse: Normal실습때처럼 빛이 얼마만큼 사물에 강타하느냐에 따라 얼마나 강하게 보여줄지 여부.
  • Specular: 금속물체보면 빛이 반짝이는 연출같은거에 사용.
  • Emissive: 물체의 아웃라인(클릭시 외곽선 효과 등) 표현 위해 사용된다.

각각 세 개를 하나씩 보고 마지막에 다 합쳐서 보자. 이 조명 4총사가 클라렌더링 면접에 나오는 가장 흔한 질문이다.

=> 위 3가지를 적절하게 섞어서 사용해야 자연스러운 조명효과가 나타난다. 조명을 생각해보면 조명에 반사되는 색을 우선 고려해야하고 조명이 비치는 정도에 따른 명암도 고려해야 하므로 여러개를 적절히 섞어야한다.

 

DepthStencilDemo클래스를 복사해서 12.AmbientDemo파일을 만들어준다.

그리고 08.GlobalTest.fx도 복붙해서 09.Lighting_Ambient.fx 쉐이더파일을 만들었다.

// 09.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) * color;
}

technique11 T0
{
	PASS_VP(P0, VS, PS)
};

우선 09.Light_Ambient.fx파일에 float4 타입으로 LightAmbient, MaterialAmbient 두 개를 만들었다. 각각 조명이 가지고있는 앰비언트 효과와 머티리얼이 가지고 있는 앰비언트 효과이다.

LightAmbient 는 광원 자체의 색상이라고 보면된다. MaterialAmbient 는 재질마다 흡수하는 색이 다를것인데 파란색 빛을 흡수해도 빨간색 물체라면 파란색과 빨간색 중간에 가까운 색으로 보일 것이다. 그래서 빛과 물체 각각의 색 특성으로 들어간다.

픽셀쉐이더에서 Ambient를 적용한 값을 구할것인데 기본 공식은 LightAmbient값에 MaterialAmbient를 곱한 값을 color로 갖는다. ✨그러나 참고로 라이팅도 그렇고 쉐이더에서 쓰는 공식이 정확히 정해져 있는게 아니라 필요에 따라 강하게 주고 싶으면 제곱도 하고 하는 등 적합하도록 변형해서 자유롭게 쓸 수 있다는 걸 기억하자.✨

 

그리고 AmbientDemo::Update에서 나중엔 라이트 컴포넌트를 들고있는 오브젝트가 빛을 내야하지만 후일을 약속하고 우선 임의로 LightAmbient 값을 넣어서 쉐이더에 전달해 테스트해보자.

// 12.AmbientDemo.cpp
void AmbientDemo::Update()
{
	_camera->Update();
	RENDER->Update();

	// 
	Vec4 lightAmbient{1.f, 0.f, 0.f, 0.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();
	}	
}

그리고 오브젝트가 두 개 있어서 그려줘야하는데 각각 머티리얼이 있다. 보통 머티리얼은 상용 엔진에서는 MeshRenderer컴포넌트 안에 있지만 여기서도 임의로 따로 지정하는 것이다. Vec4 타입에 초기화시 1.f를 넣으면 기본 생성자에서 모든 값을 1.f로 넣어준다.

 

위 픽셀쉐이더(PS)에서 color값을 return하게 하고 실행하면 모든 오브젝트가 그냥 붉은색으로 나온다.

PS에서 LightAmbient와 MaterialAmbient의 R,G,B,A끼리 각각 곱해져서 float4타입으로 color값이 정해지는데 그래서 Ambient값은 광원이 불분명하게 은은하게 모든 애들이 일정한 밝기와 색으로 표현돼서 일정하게 보인다.

Ambient 값 사용 예시

AmbientDemo::Update에서 lightAmbient값이 r값만 1이엿어서 이에 따라 materialAmbient값은 일정하다고 설정해서 값이 나왔다.앰비언트 환경광은 최종적으로 물체에 맞닥뜨린 빛이므로 일정한 밝기와 색으로 표현한다는 특징이 있다.

 

이제는 색 말고 텍스쳐를 다시한번 사용해보자.

11.DepthStencilDemo 클래스에서 사용한것과 같은 Init함수에서 두 물체에 텍스쳐를 가져와 입히는 걸 그대로 쓴다.

더보기
// AmbientDemo.cpp
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);
	}
	{
		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 mesh = RESOURCES->Get<Mesh>(L"Cube");
		_obj2->GetMeshRenderer()->SetMesh(mesh);
	}
	{
		auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
		_obj2->GetMeshRenderer()->SetTexture(texture);
	}

	RENDER->Init(_shader);
}

 

그리고 픽셀쉐이더에서 텍스쳐 샘플링을 한다. 텍스쳐에서 샘플링을 통해 UV좌표값을 꺼내 텍스쳐를 오브젝트에 입혀주는 식이였다. 그런데 그냥 샘플링한 텍스쳐 값을 반환하지 않고 Ambient환경광을 엮는다면 구한 color값이랑 엮으면

float4 PS(VertexOutput input) : SV_TARGET

{

    float4 color = LightAmbient * MaterialAmbient;

    return color; return Texture0.Sample(LinearSampler, input.uv) * color;

}

Ambient값에 텍스쳐 샘플링한 값을 곱한 예시

빨간 텍스쳐가 입힌것처럼 나온다.

 

정리하면 환경관은 LightAmbient는 광원자체의 색상이고 MaterialAmbient은 머티리얼에서 이 빛을 얼마만큼 받아들일지 여부라 각 RGBA를 곱해서 하나의 일정한 밝기와 색으로 표현하는 것인데 이것을 그대로 보여줄 수도 있지만 활용해 텍스쳐에 샘플링된 결과물에 곱해서 응용할수도 있다.

 

그리고 환경광의 개념이 어떠한 색의 빛이 어디에서 오는지 불분명할 정도로 여기저기 반사되어 비춰지는 것이라 오브젝트에는 일정한 밝기와 색으로 다 보여진다.

게임내에서 조명이 없더라고 은은하게 보이긴 하는 효과를 내고자 할때 쓰는데 이 앰비언트이다.

'[DirectX11]' 카테고리의 다른 글

Light #3 - Specular  (0) 2024.09.02
Light #2 - Diffuse  (0) 2024.08.30
3D - Geometry, Sampling  (0) 2024.08.07
3D - 프로젝트 설정  (0) 2024.08.03
Constant Buffer  (0) 2024.01.15