1일1알

DirectX11 학습 - Normal Vector 본문

DirectX11

DirectX11 학습 - Normal Vector

영춘권의달인 2024. 3. 28. 18:20

 

물체의 어떤 지점에서 표면과 수직인 방향벡터를 노말 벡터라고 한다.

이 노말 벡터는 빛 연산을 할때 사용된다.

노말 벡터와 빛이 들어오는 방향을 이용해 내적 연산을 해서 해당 픽셀에 들어오는 빛의 세기를 구할 수 있다.

 

shared_ptr<Geometry<VertexTextureNormalData>> _geometry;

struct VertexTextureNormalData
{
	Vec3 position = { 0, 0, 0 };
	Vec2 uv = { 0, 0 };
	Vec3 normal = { 0, 0, 0 };
};

void P_3Normal::Init()
{
	_shader = make_shared<Shader>(L"_3_Normal.fx");
	
	// Texture
	_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");

	// Object
	_geometry = make_shared<Geometry<VertexTextureNormalData>>();
	GeometryHelper::CreateSphere(_geometry);

	_vertexBuffer = make_shared<VertexBuffer>();
	_vertexBuffer->Create(_geometry->GetVertices());

	_indexBuffer = make_shared<IndexBuffer>();
	_indexBuffer->Create(_geometry->GetIndices());

	// Camera
	{
		_camera = make_shared<GameObject>();
		_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
		_camera->AddComponent(make_shared<Camera>());
		_camera->AddComponent(make_shared<CameraScript>());
		//CUR_SCENE->Add(camera);
	}

	
}

 

이번 예제에서 정점이 포함하는 정보는 position, uv에 normal값을 추가했다.

GeometryHelper를 이용해 구를 만들어줬고 저기서 normal 값을 넣어주는데, 구는 각 정점의 위치가 노말 벡터의 값과 같다. 정점의 좌표가 구의 중심을 기준으로 하기 때문이다.

 

void P_3Normal::Render()
{
	Vec3 _lightDir = Vec3(1.f, -1.f, 1.f);
    
	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
	_shader->GetMatrix("View")->SetMatrix((float*)&_camera->GetCamera()->_matView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&_camera->GetCamera()->_matProjection);

	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
	_shader->GetVector("LightDir")->SetFloatVector((float*)&_lightDir);

	uint32 stride = _vertexBuffer->GetStride();
	uint32 offset = _vertexBuffer->GetOffset();
	DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);

	_shader->DrawIndexed(0, 0, _geometry->GetIndexCount());
}

 

빛의 효과를 추가하여 노말 벡터의 효과를 보여주기 위해서 임시로 빛의 방향을 나타내는 lightDir을 쉐이더에 함께 넘겨주었다.

 

VertexOutput VS(VertexInput input)
{
	VertexOutput output;
	output.position = mul(input.position, World);
	output.position = mul(output.position, View);
	output.position = mul(output.position, Projection);

	output.uv = input.uv;
	output.normal = mul(input.normal, (float3x3)World);

	return output;
}

 

버텍스 쉐이더에서는 position, uv는 저번과 같이 다음 단계로 넘겨주고 normal값을 이용한 연산은 world좌표에서 하기 위해서 world space로 변환하는 연산을 했다.

 

SamplerState Sampler0;

float4 PS(VertexOutput input) : SV_TARGET
{
	float3 normal = normalize(input.normal);
	float3 light = -LightDir;

	float4 color = Texture0.Sample(Sampler0, input.uv);
	return color * dot(light, normal);
}

 

픽셀 쉐이더 단계에서 normal값을 이용해 연산을 해준다.

빛의 방향과 normal 을 내적 연산을 해서 빛의 세기를 계산하고 uv매핑으로 구한 픽셀의 색상값을 빛의 세기와 곱하였다.

 

normal벡터와 light의 원점이 일치하기 때문에 light에 -를 붙여준다.

-light와 normal를 내적해주면 사이각이 작을수록, 즉 두 벡터가 일치할수록 값이 1에 가까워지고 이는 곧 빛이 해당 픽셀에 많이 들어온다는 것을 뜻한다.

 

 

실제 결과 화면이다. 빛의 방향이 왼쪽 위에서 오른쪽 아래 방향이기 때문에 왼쪽 위는 밝고 오른쪽 아래는 어두운 것을 확인할 수 있다.