DirectX

44. Light, Material_Ambient

devRiripong 2024. 2. 13.
반응형

조명 4 총사에 대해 얘기할 건데

나중에는 모든 조명들을 합해가지고 한 번에 들어가긴 할 것이다.

하지만 이해를 위해 나눠서 Ambient, Diffuse, Specular, Emissive를 각각 해볼 것이다.

특히 클라이언트 프로그래머 면접에서 자주 나온다.

 

이것들에 대해 얘기하려면 일단 원론적으로 조명이 뭔지 알아야 한다.

조명이란 빛이 우리 눈에 어떻게 토스되어 보이느냐가 핵심이다.

모든 것을 완벽하게 실시간으로 연산하는 건 불가능하다.

그래픽스에서 주된 연구과제는 최소한의 비용으로 그럴싸한 퀄리티를 보여주는 조명 연산식을 개발해서 매 프레임마다 연산을 해서 게임에서 표현을 하는 것이다.

 

보통 애니에서 사용되는 기술에 5년 정도 뒤쳐진다고 보면 된다.

게임은 실시간으로 렌더링을 해야 한다는 게 차이다.

애니는 한번만 만들면 된다.

모바일에선 PBR (Physically Based Rendering, PBR) 같은 기술이 나와도 사용을 못한다.

 

가장 기본이 되는 4 총사가 있다.

Ambient

Diffuse

Specular

Emissive

가장 기본이 되는 아이들인데 이걸 알아볼 것이다.

 

이 4가지를 다 같이 조립을 해서 한 번에 사용하게 된다.

 

그럴싸하게 보이게 하려면 뭐가 있어야 할까?

바라보는 물체가 축구공이라면 색상을 나타낼 수 있는 뭔가가 있고, 깊이에 따라 살짝 그림자가 달라지는 거랑, 빛을 받는 부위에 따라 명암이 생기는 것도 있어야 할 것이다.

당연하게 생각했던 걸 공식으로 만들면 생각보다 아주 단순하지는 않다.

 

Ambient - 환경광, 빛들이 나아가다 이리저리 반사가 되어서 모든 면이 최소한의 일정한 빛은 받고 있다는 거

Diffuse - normal 실습할 때 해본 거. 색상을 무조건 다 그려주는 건 아니고 빛이 얼마나 강타하냐에 따라서 강하게 보여줄지 설정하는 거

Specular - 철이나, 금속으로 된 물체를 보먄 반짝이는 눈부신 걸 묘사하는 거.

이 3개가  가장 핵심이고 Emissive 은 약간 옵션이라고 볼 수 있다.

Emissive - 어떤 물체의 아웃라인 같은 거 만들어 주고 싶은 경우가 있다. 클릭했을 때 외곽선을 빨갛게 그려준다거나 하는 거.

 

일반적으로는 3 총사가 기본이 된다고 볼 수 있다.

 

한 아이의 조명만 대입을 하는 게 아니라 이 세 아이를 적절하게 섞여줘야지만 자연스럽게 효과가 나온다.

4개를 하나씩 만들어보고 하나로 합치는 작업을 할 것이다. 이게 워낙 중요한 건데 한 번에 만들면 뭐가 뭔지 머리에 안 남는다.

 

클라 프로그래머를 지원할 때 OpenGL이나 DX를 해봤다고 적게 되는데 항상 나오는 질문 중 하나다.

 

Amibient부터 해보자.

 

1. 파일 생성 후 세팅하기

1) AmbientDemo를 생성하고, Main에 세팅하기, 배경색을 검게 하기

탐색기에서 10. DepthStencilDemo를 복제해서 12. AmbientDemo로 이름을 바꾼다.

솔루션 탐색기의 Client/Game/Week2 필터에 넣는다.

Main.cpp에 가서

#include "12. AmbientDemo.h"

를 추가하고

desc.clearColor = Color(0.f, 0.f, 0.f, 0.f);
desc.app = make_shared<AmbientDemo>(); // 실행 단위

이렇게 색도 검은색으로 밀고, 세팅한다.

 

2) 09. Lighting_Ambient.fx를 생성하고 AmbientDemo::Init에서 세팅하기 

일단 쉐이더 파주고, 실습을 하고, 나중에 합치는 방식으로 한다.

탐색기에서 08. GlobalTest.fx를 복제해서 09. Lighting_Ambient.fx로 해서 Client/Shaders/Week2네 넣어준다.

AmbientDemo::Init()에서

_shader = make_shared<Shader>(L"09. Lighting_Ambient.fx");

이렇게 세팅한다.

 

그리고 Client를 빌드해 보자

실행해 보면 그냥 어둠 속에서 두 개가 뜨는 것을 알 수 있다.

 

2. 09. Lighting_Ambient.fx 작성하기

Ambient부터 적용을 하겠다고 하면,

수많은 반사를 거쳐서 광원이 불분명한 빛을 말한다.

그래서 일정한 밝기와 색으로 표현한다는 특징이 있다.

일정한 방향, 일정한 색으로 표현한다가 핵심이 된다.

 

나중엔 ConstantBuffer로 하는 걸로 바꾸겠지만

지금은 간단하게 개념부터 익히기 위해 전역으로 만들어 본다.

 

1) LightAmbient, MaterialAmbient 변수 선언하기

09. Lighting_Ambient.fx로 가서

float4 LightAmbient; 
float4 MaterialAmbient;

 

LightAmbient는 조명이 될 텐데 Embient 효과가 얼마나 되는지. 태양이라면 노란색빛을 내뿜겠지만 빨간색이라 하면 빨간색을 내뿜을 것이다. 광원 자체의 색상을 얘기한다.

 

MaterialAmbient는 물체마다 흡수하는 색상이 다를 것이고, 빨간색 물체였다고 하면 빛이 하얀색이어도 물체가 빨간색으로 보인다.

MaterialAmbient 은 물체의 색상이라기보다는 재질의 색상, 빛을 얼마만큼을 받아줄 수 있는지가 더 정확한 거 같다.

 

빛이 어떤 색인지와 물체가 어떤 색인지가 각각이 이렇게 있다고 보면 된다.

 

2) LightAmbient, MaterialAmbient를 이용해 PS에서 연산하기

Ambient(주변광/환경광)

수많은 반사를 거쳐서 광원이 불분명한 빛

일정한 밝기와 색으로 표현

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = LightAmbient * MaterialAmbient;
    return color; 
    //return Texture0.Sample(LinearSampler, input.uv);
}

이렇게 color를 만들어 주면 된다.

 

기본적으로 조명, 라이팅을 공부할 때는 기본적 컨셉은 있지만 정확히 어떤 공식이 있는 게 아니다.

Diffuse, Specular는 정확한 공식이 보장된 게 아니라서 조금씩 고쳐보면서 만들면 된다.

 

3. AmbientDemo::Update에서 09. Lighting_Ambient.fx 쉐이더의 LightAmbient, MaterialAmbient에 값을 넣기

나중엔 AmbientDemo에서 하는 게 아니라 Light라는 컴포넌트를 들고 있는 애들이 빛을 내뿜는 형태로 바꿔야 할 테지만 나중에 챙기도록 하고

AmbientDemo::Update에서

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

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

	{
	// 각각의 object가 들고 있는 Material이 있을 것이다. 
        // 보통은 MeshRender에다 Material을 끼워 넣는 경우가 많다. 
	// 지금은 각각 물체마다 따로 설정을 해보도록 한다.
		Vec4 materialAmbient(1.f); // 하나의 값을 넣어주면 나머지 값은 동일한 값 넣어주는 생성자 실행된다. 
	// 모든 빛을 다 받아주는 재질
		_shader->GetVector("MaterialAmbient")->SetFloatVector((float*)&materialAmbient);
		_obj->Update();

	}

	{
		Vec4 materialAmbient(1.f);
		_shader->GetVector("MaterialAmbient")->SetFloatVector((float*)&materialAmbient);
		_obj2->Update();

	}
}

지금은 따로따로 Material을 넣어준다.

 

같은 타입의 물체라면 Material을 공유해서 사용하긴 할 것이다.

 

4. LightAmbient와 MaterialAmbient의 값을 바꿔가며 테스트하기

실행하면 어떻게 뜰지를 예상을 해보자.

1) Vec4 lightAmbient{ 0.5f, 0.f, 0.f, 1.f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 경우 

여기까지 만들면 그냥 빨간색으로 칠해지게 될 것이다.

 

은은하게 일정한 빨간색을 여기다 뿌려준 것이다.

2) Vec4 lightAmbient{ 0.2f, 0.f, 0.f, 1.f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 경우 

더 은은하게 하려면

void AmbientDemo::Update()에서

Vec4 lightAmbient{ 0.2f, 0.f, 0.f, 1.f };

이렇게 해주면

더 어둡게 될 것이고,

 

3) Vec4 lightAmbient{ 0.2f, 0.f, 0.f, 1.f };로 하고 Sphere의 materialAmbient를 Vec4 materialAmbient(0.1f);로 한 경우 

아무리 빨간색으로 뿌린다고 해도

Vec4 materialAmbient(0.1f);

이렇게 구를 세팅을 하면

구는 더 어둡게 표현이 된다.

 

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = LightAmbient * MaterialAmbient;
    return color; 
    //return Texture0.Sample(LinearSampler, input.uv);
}

결국 이 빛을 보여주려고 하고 있지만

MaterialAmbient라는 게 물체마다 받아주는 게 다르기 때문에 MaterialAmbient에 따라서 역할이 달라질 수 있다.

빛도 자신만의 색상이 있고, Material도 자신이 받아주는 색상이 있어서 곱해지게 되어서 최종 결과가 나오게 된다고 보면 된다.

RGBA의 R은 R, B는 B끼리 각각 연산이 되어 가지고, float4로 나타나는 거이지 딱히 행렬의 의미는 아니다.

 

광원이 불분명하고 모든 애들이 색상을 입는다고 했으니까 그걸 따라가지고 실질적으로 이렇게 빨간색을 다이렉트로 하는 경우도 있지만

나중에 Diffuse를 배우면 Texture의 색상과 연관이 있다.

 

4) Vec4 lightAmbient{ 0.5f, 0.f, 0.f, 1.f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 다음 Texture0을 Sampling 한 수치에 color를 곱해준 경우

원래 방식대로

    //return Texture0.Sample(LinearSampler, input.uv);

Texture를 샘플링해서 uv좌표를 꺼내서 만드는 방식이었는데, 환경광을 엮어서 한다면, 이 Sampling 한 수치와 color와 연관을 지어 주는 것도 고려해 줄 수 있다.

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = LightAmbient * MaterialAmbient;
    //return color; 
    return Texture0.Sample(LinearSampler, input.uv) * color;
}

공식이 하나만 있는 게 아니다.

 

다시 AmbientDemo::Update에서

Vec4 lightAmbient{ 0.5f, 0.f, 0.f, 1.f };
Vec4 materialAmbient(1.f);

이렇게 세팅 후 실행을 해보면

원래 완전히 빨간색으로 보였지만 지금은 Sampe이 된 최종 색상에서 color를 곱할 때 빨강만 남기고 나머지는 약하게 만들었기 때문에 폭주 베이가처럼 빨간색으로 보이고 있다.

 

환경광이라는 건 기본적으로

    float4 color = LightAmbient * MaterialAmbient;

이렇게 곱해서 일정한 밝기와 식으로 표현이 되는 것인데 그거를 그대로 보여줄지, 샘플링된 결과물에 색상을 입힐지는 우리의 공식을 만들면 된다.

 

게임에서 어두운 곳에 어둡더라도 물체가 조금은 보인다. 그런 효과를 나타내고 싶을 때 전형적으로 사용할 수 있는 게 Ambient라고 볼 수 있다.

 

5) Vec4 lightAmbient{ 0.2f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 다음 Texture0을 Sampling 한 수치에 color를 곱해준 경우

만약 AmbientDemo::Update에서

Vec4 lightAmbient{ 0.2f };

이렇게 하면 빨강이 아닌 하얀빛이 들어가니

 

일반적인 하얀색 빛인데 굉장히 어두워서 조금만 보인다.

6) Vec4 lightAmbient{1.f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 다음 Texture0을 Sampling 한 수치에 color를 곱해준 경우

Vec4 lightAmbient{ 1.f };

이렇게 하면

lightAmbient가 1,1,1, 1이 되었으니 선명하게 보인다. 비율이라고 볼 수도 있다.

적용하기 전 100% 보이는 상태가 재현이 된다.

조금씩 줄여서 색을 갉을 수 있다.

어떨 때는 곱하고, 어떨 때는 더할 것이다.

7) Vec4 lightAmbient{ 1.f, 0.f, 0.f, 0.f };로 하고 Sphere와 Cube의 materialAmbient를 Vec4 materialAmbient(1.f);로 한 다음 Texture0을 Sampling 한 수치에 color를 곱해준 경우

어떠한 Ambient 대상으로 원래 있던 색상에서 더 어둡게 하거나 밝게 하거나 할 때는 지금처럼 곱하기를 해서 구한 결과물을 곱하게 되면

float4 PS(VertexOutput input) : SV_TARGET
{
    float4 color = LightAmbient * MaterialAmbient;
    //return color; 
    return Texture0.Sample(LinearSampler, input.uv) * color;
}

이 수치에 따라서,

만약

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

이런 빛을 쏜다면

빨간색만 남게 될 것이다.

 

다른 색 조명도 얼마든지 할 수 있다.

앞으로 조명과 관련된 연산을 할 때는 이런 식으로 뭔가 본에 있는 색상에서 뭔가를 고치거나,

아니면 막 바로 입히는 것도 방법이 될 수 있다.

쉐이더는 정답이 없다.

 

정답은 평가자들의 눈이 정답이다.

 

지난 시간에는 Light를 받아서 방향에 따라 Normal 연산을 해줬는데 이거는 그런 게 전혀 없다.

 

반응형

'DirectX' 카테고리의 다른 글

46. Light, Material_Specular  (0) 2024.02.13
45. Light, Material_Diffuse  (0) 2024.02.13
43. Light, Material_Depth Stencil View  (0) 2024.02.11
42. Light, Material_Global Shader  (0) 2024.02.11
41. DirectX11 3D 입문_Mesh  (0) 2024.02.10

댓글