본문 바로가기

DirectX11 3D/기본 문법

<DirectX11 3D> 75 - Lighting(Ambient, Diffuse, Specular, Emissive)

 

필요한 개념


조명 종류는 4가지가 있다.
Ambient
Diffuse
Specular
Emissive

Ambient : 주 빛(ex) 태양, 주 빛, 어느 방향이든 어느 쪽에서든 동일한 강도로 동일한 색이 들어온다 간주, 예를 들어 노을이 지면 붉은 색 계열이 비침
색 하나를 넣어주는게 Ambient, 낮에는 흰색을 넣어줬다가 노을이 지면 붉은 색을 넣어줌 밤이 되면 어두운 색으로 바꿔줌

 

그림 1-1

 

그림 1-2



Diffuse : 자기가 가진 텍스쳐 색, 자기 색(텍스쳐를 의미), 음영도 자기가 표현하는 색, 음영도 diffuse에 들어감
Diffuse Color * Texture * N dot L(Diffuse 식)

Specular : 정반사광(우리말로), 유리, 스테인리스재질(우리가 바라보는 쪽으로 색이 반사되는것), 형광등이라면 하이글롯이라는 맨들한 재질에 가면
반사가 됨 -> 색이 쭉 출력됨
이전에 우리가 기존 빛을 계산할때(Lambert) 우리가 N dot L 할때 법선 벡터가 있고, 벡터가 들어오면 뒤집어서 내각을 구해서 음영값으로 사용했다.(그림 1-2 참고)
Specular는 이거와 비슷하지만 조금 더 복잡
Diffuse -> Lambert(사람이름, 식이름)(그림 1-2 참조)
Specular -> Phong(사람이름, 식이름)(그림 1-3 참조)

Specluar Color * Texture * Phong(Specluar 식)

그림 1-3



phong 설명(그림 1 - 3)
Light를 Normal에 의해 반사시켜서 R(리플랙션, 즉 반사백터)로 만듬
반사각이 나오고
여기서 정점으로 부터 카메라 방향을 또하나 만든다(E(Eye)), 내각을 만듬(시야 백터)
참고(여기선 light를 뒤집지 않는다. 반사 벡터로 만들거여서)
dot(R, E) 결과를 가지고 pow(RdotE, specluar 강도)
ex(pow(0.5, 10) 하면 0.5^10승이다.)
왜 쓰냐? 현실에서는 길게 비춰지지만, 연산량이 많기 때문에 동그라미(구모양)으로 비춰지게한다.(사각형이든, 뭐든간에 모두 구모양으로 비춰짐)
수학시간의 원의 넓이는 파이알제곱이다.
여기서 파이를 빼면 알제곱만 쓴다면 구 모양이 나옴
파이는 360도를 표현하기 위한 값
알제곱만해도 어느쪽으로 가든 동일한 값을 가짐(피타고라스의 정리에 나옴)
반지름에 제곱이상이면 구가 나온다.

pow 제곱의 특징
값이 커지면 원의 넓이가 커지며 빛은 약해지고, 반대의 경우 넓이가 좁아지며 빛의 강도가 강해진다.


요약(빨간색 구가 있다고 가정한다면)
Diffuse : 빨간색과 음영 부분
Specular: 하얀색으로 하일라이팅 된 부분
Emissive : 외곽선의 색을 표시해주는 부분
Ambient: 전체의 색을 조정


Specular는 사람의 시야에 따라 바뀌게 된다. ex) 주 카메라가 움직이면 그에 따라 빛나는 부분이 바뀐다. -> 시야까지 계산해야함

정리
Ambient: 모든 방향에서 동일한 강도의 색이 들어온다고 가정
GlobalLight.Ambient * Material.Ambient
Diffuse : 자신이 가진 색(음영 포함)
Material.Diffuse * DiffuseMap * NdotL


Specular도 마찬가지로 빛이 안닿는 부분은 색이 안들어온다.(안닿는 부분은 계산할 의미가 없다.) -> Diffuse도 그랬음

반사식은 면접문제에 잘 나옴
반사식 찾아서 정리해야함

 

수학 반사 벡터 정리 :

https://cpplab.tistory.com/74

 

반사 벡터 ( Reflection Vector )

정반사는 입사벡터와 반사벡터의 크기가 같고, 입사각과 반사각의 크기가 같은것을 말한다. Fig.1 을 보면 입사벡터 P 와 법선벡터 n 이 주어졌을때, 반사벡터 R 은 벡터 P 와 크기가 같고, 입

cpplab.tistory.com



가산혼합을 쓴다.
색은 곱하면 곱할수록 어두워지지만
색상들을 더한다면(혼합)하면 색이 밝아짐
return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb;

fx와 fxo 성능비교
fx 기준 2332
fxo 기준 2

그림 2-1을 보면 빛이 반사되는데(Specular), 텍스쳐의 검은색 선 부분이 안보이고 전부 반사된다.
저 검은색 부분이 보이고 반사되어야하는게 맞다.
그래서 저 부분을 보일 수 있도록 가능한것이 SpecularMap이다.
SpecularMap : 어두운 부분은 빛이 약하게 들어가며 밝은 부분은 빛이 강하게 들어가게 될 부분
(모델의 Specular 텍스쳐를 말한다.)
텍스쳐에 보면 하얀 부분은 빛이 강하게 들어가고 검은 부분은 어둡게 들어간다.(밝은 색은 값이 크니까 곱해지면 크게 나오고, 어두운색은 0에 가까우니까 곱하면 값이 작아짐)
SpecularMap을 만들고 싶다면 NormalMap Online을 치면 된다.(모델의 Specular가 없다면 수치를 조정해서 만들 수 있다.) -> 유용한 사이트

SpecularColor * Texture를 우리는 Texture()함수에서 했다.
이거와 동일하게 SpecularMap도 해주면 된다.
Texture(Material.Specular, SpecularMap, input.Uv);

Specluar 결과를 보면
상당히 선명하게 검은선이 보이는 것을 알 수 있다.

 

그림 3-1

 

 

Emissive는 외곽선을 어떻게 찾아내느냐에 관건이 있다.
외곽선을 찾아내는 방법은 여러가지
가장 쉬운 방법이 눈하고 Normal을 쓰는것
외곽선은 바라보는 시점에서 좌우된다.(우리가 정면에서 본다면 양옆이 외곽선이 될 것이다.
옆에서 바라본다면 앞뒤가 외곽선이 된다.)

바라보는 시점에서 외곽은 NormalVector와 내적을 했을 때의 값이다. 그것을 뒤집어서 쓰겠다는 것이다.(그림 3-1 참고)

Emissive는 텍스쳐가 없으나, 실무에서는 사용하는 경우도 있다.
외곽선이 얼마나 들어갈지 텍스쳐로 하는 경우도 있지만, 거의 없다.

모델은 왜 외곽선이 안그려지는지?
.Material에 Emissive값이 없어서 그렇다. 디자이너가 안 넣은것
임의로 값을 수정해서 넣어준다.

OutlineShader도 같은 느낌
Emissive를 Rim Lighting이라고 부른다.

 

 

 

 

LightingDemo.h


더보기
#pragma once
#include "Systems/IExecute.h"

class LightingDemo : public IExecute
{
public:
	virtual void Initialize() override;
	virtual void Ready() override {}
	virtual void Destroy() override {}
	virtual void Update() override;
	virtual void PreRender() override {}
	virtual void Render() override;
	virtual void PostRender() override {}
	virtual void ResizeScreen() override {}

private:
	void Mesh();
	void Airplane();
	void Kachujin();
	void KachujinCollider();
	void KachujinWeapon();

	void Pass(UINT mesh, UINT model, UINT anim);

private:
	Shader* shader;

	CubeSky* sky;

	Material* floor;
	Material* stone;
	Material* brick;
	Material* wall;

	MeshRender* cube;
	MeshRender* cylinder;
	MeshRender* sphere;
	MeshRender* grid;

	ModelRender* airplane = NULL;


	ModelAnimator* kachujin = NULL;
	Transform* colliderInitTransforms;
	ColliderObject** colliders;

	ModelRender* weapon = NULL;
	Transform* weaponInitTransform;

	vector<MeshRender*> meshes;
	vector<ModelRender*> models;
	vector<ModelAnimator*> animators;
};

 

 

 

 

 

 

 

LightingDemo.cpp


더보기
#include "stdafx.h"
#include "LightingDemo.h"

void LightingDemo::Initialize()
{
	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
	Context::Get()->GetCamera()->Position(1, 36, -85);


	//Performance perfomence;
	//perfomence.Start();
	//{
	shader = new Shader(L"75_Lighting.fxo");
	//}
	//float t = perfomence.End();
	//MessageBox(D3D::GetDesc().Handle, to_wstring(t).c_str(), L"", MB_OK);


	sky = new CubeSky(L"Environment/GrassCube1024.dds");

	Mesh();
	Airplane();

	Kachujin();
	KachujinCollider();
	KachujinWeapon();
}

void LightingDemo::Update()
{
	//Weapon
	{
		Vector3 position;
		weaponInitTransform->Position(&position);
		ImGui::SliderFloat3("Weapon Position", position, -20, 20);

		Vector3 scale;
		weaponInitTransform->Scale(&scale);
		ImGui::SliderFloat3("Weapon Scale", scale, 0.1f, 3.0f);

		Vector3 rotation;
		weaponInitTransform->Rotation(&rotation);
		ImGui::SliderFloat3("Weapon Rotation", rotation, -1.0f, 1.0f);

		weaponInitTransform->Position(position);
		weaponInitTransform->Scale(scale);
		weaponInitTransform->Rotation(rotation);
	}

	//Collider
	{
		Vector3 position;
		colliderInitTransforms->Position(&position);
		ImGui::SliderFloat3("Collider Position", position, -20, 20);

		Vector3 scale;
		colliderInitTransforms->Scale(&scale);
		ImGui::SliderFloat3("Collider Scale", scale, 10.0f, 100.0f);

		Vector3 rotation;
		colliderInitTransforms->Rotation(&rotation);
		ImGui::SliderFloat3("Collider Rotation", rotation, -1.0f, 1.0f);

		colliderInitTransforms->Position(position);
		colliderInitTransforms->Scale(scale);
		colliderInitTransforms->Rotation(rotation);
	}


	sky->Update();

	cube->Update();
	grid->Update();
	cylinder->Update();
	sphere->Update();

	airplane->Update();
	kachujin->Update();

	Matrix worlds[MAX_MODEL_TRANSFORMS];
	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
	{
		kachujin->GetAttachTransform(i, worlds);
		//colliders[i]->Collider->GetTransform()->World(worlds[40]);
		//colliders[i]->Collider->Update();

		weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]);
	}

	weapon->UpdateTransforms();
	weapon->Update();
}

void LightingDemo::Render()
{
	sky->Render();


	Pass(0, 1, 2);

	wall->Render();
	sphere->Render();

	brick->Render();
	cylinder->Render();

	stone->Render();
	cube->Render();

	floor->Render();
	grid->Render();

	airplane->Render();

	kachujin->Render();
	weapon->Render();
	//for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
		//colliders[i]->Collider->Render();
}

void LightingDemo::Mesh()
{
	//Create Material
	{
		floor = new Material(shader);
		floor->DiffuseMap("Floor.png");
		floor->Specular(1, 1, 1, 20);
		floor->SpecularMap("Floor_Specular.png");
		//floor->NormalMap("Floor_Normal.png");

		stone = new Material(shader);
		stone->DiffuseMap("Stones.png");
		stone->Specular(1, 1, 1, 20);
		stone->SpecularMap("Stones_Specular.png");
		stone->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		//stone->NormalMap("Stones_Normal.png");


		brick = new Material(shader);
		brick->DiffuseMap("Bricks.png");
		brick->Specular(1, 0.3f, 0.3f, 20);
		brick->SpecularMap("Bricks_Specular.png");
		brick->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		//brick->NormalMap("Bricks_Normal.png");

		wall = new Material(shader);
		wall->DiffuseMap("Wall.png");
		wall->Specular(1, 1, 1, 20);
		wall->SpecularMap("Wall_Specular.png");
		wall->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		//wall->NormalMap("Wall_Normal.png");
	}

	//Create Mesh
	{
		Transform* transform = NULL;

		cube = new MeshRender(shader, new MeshCube());
		transform = cube->AddTransform();
		transform->Position(0, 5, 0);
		transform->Scale(20, 10, 20);

		grid = new MeshRender(shader, new MeshGrid(5, 5));
		transform = grid->AddTransform();
		transform->Position(0, 0, 0);
		transform->Scale(12, 1, 12);

		cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
		sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
		for (UINT i = 0; i < 5; i++)
		{
			transform = cylinder->AddTransform();
			transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = cylinder->AddTransform();
			transform->Position(30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);


			transform = sphere->AddTransform();
			transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = sphere->AddTransform();
			transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);
		}
	}

	sphere->UpdateTransforms();
	cylinder->UpdateTransforms();
	cube->UpdateTransforms();
	grid->UpdateTransforms();

	meshes.push_back(sphere);
	meshes.push_back(cylinder);
	meshes.push_back(cube);
	meshes.push_back(grid);
}

void LightingDemo::Airplane()
{
	airplane = new ModelRender(shader);
	airplane->ReadMesh(L"B787/Airplane");
	airplane->ReadMaterial(L"B787/Airplane");

	Transform* transform = airplane->AddTransform();
	transform->Position(2.0f, 9.91f, 2.0f);
	transform->Scale(0.004f, 0.004f, 0.004f);
	airplane->UpdateTransforms();

	models.push_back(airplane);
}

void LightingDemo::Kachujin()
{
	kachujin = new ModelAnimator(shader);
	kachujin->ReadMesh(L"Kachujin/Mesh");
	kachujin->ReadMaterial(L"Kachujin/Mesh");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
	kachujin->ReadClip(L"Kachujin/Salsa Dancing");


	Transform* transform = NULL;

	transform = kachujin->AddTransform();
	transform->Position(0, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(0, 0, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(1, 1, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-30, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(2, 2, 0.75f);

	transform = kachujin->AddTransform();
	transform->Position(15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayBlendMode(3, 0, 1, 2);
	kachujin->SetBlendAlpha(3, 1.5f);

	transform = kachujin->AddTransform();
	transform->Position(30, 0, -32.5f);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(4, 4, 0.75f);

	kachujin->UpdateTransforms();

	animators.push_back(kachujin);
}

void LightingDemo::KachujinCollider()
{
	UINT count = kachujin->GetTransformCount();
	colliders = new  ColliderObject * [count];

	colliderInitTransforms = new Transform();
	colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f);
	colliderInitTransforms->Scale(5, 5, 75);

	for (UINT i = 0; i < count; i++)
	{
		colliders[i] = new ColliderObject();

		//colliders[i]->Init = new Transform();
		//colliders[i]->Init->Position(0, 0, 0);
		//colliders[i]->Init->Scale(10, 30, 10);

		colliders[i]->Transform = new Transform();
		//colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
		colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms);
	}
}

void LightingDemo::KachujinWeapon()
{
	weapon = new ModelRender(shader);
	weapon->ReadMesh(L"Weapon/Sword");
	weapon->ReadMaterial(L"Weapon/Sword");

	UINT count = kachujin->GetTransformCount();
	for (UINT i = 0; i < count; i++)
		weapon->AddTransform();

	weapon->UpdateTransforms();
	models.push_back(weapon);


	weaponInitTransform = new Transform();
	weaponInitTransform->Position(-2.9f, 1.45f, -6.45f);
	weaponInitTransform->Scale(0.5f, 0.5f, 0.75f);
	weaponInitTransform->Rotation(0, 0, 1);
}

void LightingDemo::Pass(UINT mesh, UINT model, UINT anim)
{
	for (MeshRender* temp : meshes)
		temp->Pass(mesh);

	for (ModelRender* temp : models)
		temp->Pass(model);

	for (ModelAnimator* temp : animators)
		temp->Pass(anim);
}

 

 

 

 

 

 

71_Lighting.fx


더보기
#include "00_Global.fx"
#include "00_Light.fx"
#include "00_Render.fx"


float4 PS(MeshOutput input) : SV_Target
{
    // 즉 DiffuseColor * Texture 여기서 계산되고
    Texture(Material.Diffuse, DiffuseMap, input.Uv);
    Texture(Material.Specular, SpecularMap, input.Uv);
    
    // 나머지 위에 계산된거랑 * NdotL이랑은 여기서 연산된다.
    // 여기까지 해야 Diffuse식 연산이 완성됨
    MaterialDesc output;
    ComputeLight(output, input.Normal, input.wPosition);
    
    return float4(MaterialToColor(output), 1.0f);
}

technique11 T0
{
    P_VP(P0, VS_Mesh, PS)
    P_VP(P1, VS_Model, PS)
    P_VP(P2, VS_Animation, PS)
}

 

 

 

 

 

 

 

 

 

00_Light.fx 추가된 부분


더보기
// 조명, material 색상에 관한걸 모아놈


// Light 정보
struct LightDesc
{
    float4 Ambient;
    float4 Specular;
    float3 Direction;
    float Padding;
    float3 Position;
};

cbuffer CB_Light
{
    LightDesc GlobalLight;
};


// Material 정보
Texture2D DiffuseMap;
Texture2D SpecularMap;
Texture2D NormalMap;
TextureCube SkyCubeMap;

struct MaterialDesc
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float4 Emissive;
};

cbuffer CB_Material
{
    MaterialDesc Material;
};

MaterialDesc MakeMaterial()
{
    MaterialDesc output;
    output.Ambient = float4(0, 0, 0, 0);
    output.Diffuse = float4(0, 0, 0, 0);
    output.Specular = float4(0, 0, 0, 0);
    output.Emissive = float4(0, 0, 0, 0);

    return output;
}

// 가산혼합을 쓴다.
// 색은 곱하면
// 곱할수록 어두워지지만
// 색상들을 더한다면(혼합) 하면
// 색이 밝아짐
float3 MaterialToColor(MaterialDesc result)
{
    // alpha는 반투명(나중에 배움, 식이 복잡)으로 별도로 처리할꺼라 float3로 리턴
    return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb;
}

////////////////////////////////////////////////////////////////////////////

// Diffuse와 specular의 앞에 식이 같기 때문에 그 부분만 먼저 구해주는 함수를 만든다.(Color * Texture)
// 1. diffuse나 specluar들어옴 2. 자기 텍스쳐 들어옴
void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition)
{
    // MakeMaterial : MaterialDesc값을 초기화 시켜주는 함수
    output = MakeMaterial();
    // 밑에와 동일한 결과(깔끔하게 쓸라고 함수 만든것임)
    // output = (MaterialDesc)0;
    
    // Diffuse 먼저 계산하려 뒤집음
    float3 direction = -GlobalLight.Direction;
    // diffuse를 위해 계산
    float NdotL = dot(direction, normalize(normal));
    
    // Ambient 계산은 2가지가 있다.
    // (자기가 가진 Ambient색, 주빛)
    // 태양 빛(주 빛) -> GlobalLight.Ambient
    // 카추진은 어둡게, 다른 애는 밝게 표현 등 그룹에 대한 색(Material.Ambient)
    output.Ambient = GlobalLight.Ambient * Material.Ambient;
    
    // 방향벡터니까 normalize을함
    // 카메라의 위치에서 월드 위치를 뺸다.
    // wPosition에서 ViewPosition(카메라 위치)를 바라보는 방향벡터가 된다.
    float3 E = normalize(ViewPosition() - wPosition);

    
    // NdotL이 0보다 크지 않으면 검은색(할필요 x)
    // 그래서 0보다 클때만 Diffuse 연산을 함
    // Specular도 빛이 안닿는 부분은 연산할 필요가 없다.(색이 필요 없는 부분)
    [flatten]
    if (NdotL > 0.0f)
    {
        output.Diffuse = Material.Diffuse * NdotL;
        
        
        // Specular.a(알파)에 강도를 넣어놓았다.(강도가 0이라면 연산할 필요 x)
        // 우리 모델할때도 Specular.a에 shineess(샤인니리스)를 넣어놨다.
        [flatten]
        if (Material.Specular.a > 0.0f)
        {
            // 반사식은 면접문제에 잘 나옴
            // 반사식 찾아서 정리해야함
            // 반사(reflect)는 이 함수로 구함
            // 위에서 light를 뒤집어 놔서 다시 뒤집어 놔야한다.(specular는 라이트를 정방향으로 reflect를 구하기 때문)
            // 빛과 노멀로 반사벡터를 만듬
            // 방향벡터니까 normalilze함(연산을 편하게하기 위해 크기가 1인 단위벡터로 만듬(방향은 유지한채로))
            float3 R = normalize(reflect(-direction, normal));
            // saturate(0 ~ 1 범위로 만든다(값제한)) : 0이하의 값은 0으로, 0 ~ 1 사이의 값은 그대로 나오고 , 1이상의 값은 1로 나온다.
            float RdotE = saturate(dot(R, E));

            // 1. RdotE, 2. 강도 -> Material.Specular.a
            float specular = pow(RdotE, Material.Specular.a);
            // Material.Specular -> specular color * Texture
            // GlobalLight.Specular -> 흰색 반사색뿐만아니라, 다른 반사색도 넣을 수 있도록함(ex) 붉은 노을이라면 붉은 색 반사색)
            output.Specular = Material.Specular * specular * GlobalLight.Specular;
        }
    }
}

 

 

 

 

 

 

Specular 결과


결과 전(Specular만 적용시)

검은색 라인 부분이 안보이는 것을 확인할 수 있다.

 

결과 후(Specular + SpecularMap(Texture))

SpecularMap을 사용시에 텍스쳐 색에 따라 빛의 세기가 달라진다. (어두운 부분일수록 빛이 약해짐)

 

00_Light.fx 추가된 부분


더보기
// 조명, material 색상에 관한걸 모아놈


// Light 정보
struct LightDesc
{
    float4 Ambient;
    float4 Specular;
    float3 Direction;
    float Padding;
    float3 Position;
};

cbuffer CB_Light
{
    LightDesc GlobalLight;
};


// Material 정보
Texture2D DiffuseMap;
Texture2D SpecularMap;
Texture2D NormalMap;
TextureCube SkyCubeMap;

struct MaterialDesc
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float4 Emissive;
};

cbuffer CB_Material
{
    MaterialDesc Material;
};

MaterialDesc MakeMaterial()
{
    MaterialDesc output;
    output.Ambient = float4(0, 0, 0, 0);
    output.Diffuse = float4(0, 0, 0, 0);
    output.Specular = float4(0, 0, 0, 0);
    output.Emissive = float4(0, 0, 0, 0);

    return output;
}

// 가산혼합을 쓴다.
// 색은 곱하면
// 곱할수록 어두워지지만
// 색상들을 더한다면(혼합) 하면
// 색이 밝아짐
float3 MaterialToColor(MaterialDesc result)
{
    // alpha는 반투명(나중에 배움, 식이 복잡)으로 별도로 처리할꺼라 float3로 리턴
    return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb;
}

////////////////////////////////////////////////////////////////////////////

// Diffuse와 specular의 앞에 식이 같기 때문에 그 부분만 먼저 구해주는 함수를 만든다.(Color * Texture)
// 1. diffuse나 specluar들어옴 2. 자기 텍스쳐 들어옴
void Texture(inout float4 color, Texture2D t, float2 uv, SamplerState samp)
{
    // 음영에 따라 감색을 주기위해 *연산을 해줌
    float4 sampling = t.Sample(samp, uv);
    
    color = color * sampling;
}

// Linear 샘플러만 사용하는 걸로
void Texture(inout float4 color, Texture2D t, float2 uv)
{
    Texture(color, t, uv, LinearSampler);
}
////////////////////////////////////////////////////////////////////////////

// wPosition : 월드가 계산된 위치(월드가 이동한 포지션)
// eye를 계산하기 위해서는 로컬이 아닌 월드로 된 중심점으로 해야한다.
void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition)
{
	...
    // 이미시브 구현 부분
    // 강도가 0보다 클 경우만 한다.
    [flatten]
    if (Material.Emissive.a > 0.0f)
    {
        // 가장 정면은 1이 나오고, 외곽은 0이 나온다.
        float NdotE = dot(E, normalize(normal));
         // 값을 뒤집는다.
        // 외곽선은 1.0f - Material.Emissive에 가깝게 나옴
        // lerp를 해도 smoothstep을 해도 상관은 없지만 둘다 0 ~ 1까지여서, lerp는 선형보간이다.
        // SmoothStep이란? Lerp와는 다르게 시작일 수록 값이 커졌다가 작아지므로 더 부드러운 표현가능
        // float emissive = lerp(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
        float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
        
        // Emissive는 텍스쳐가 없으나, 실무에서는 사용하는 경우도 있다.
        // Color * emissive
        output.Emissive = Material.Emissive * emissive;
    }
}

 

75_Lighting.fx 추가된 부분


더보기
#include "00_Global.fx"
#include "00_Light.fx"
#include "00_Render.fx"


float4 PS(MeshOutput input) : SV_Target
{
    // 즉 DiffuseColor * Texture 여기서 계산되고
    Texture(Material.Diffuse, DiffuseMap, input.Uv);
    Texture(Material.Specular, SpecularMap, input.Uv);
    
    // 검은색으로 해서 Emissive가 더 잘보이도록 한다.
    Material.Diffuse = float4(0, 0, 0, 1);
    
    // 나머지 위에 계산된거랑 * NdotL이랑은 여기서 연산된다.
    // 여기까지 해야 Diffuse식 연산이 완성됨
    MaterialDesc output;
    ComputeLight(output, input.Normal, input.wPosition);
    
    return float4(MaterialToColor(output), 1.0f);
}

technique11 T0
{
    P_VP(P0, VS_Mesh, PS)
    P_VP(P1, VS_Model, PS)
    P_VP(P2, VS_Animation, PS)
}

 

 

 

 

 

Emissive 결과


 

결과 전

 

결과 후