-
[DirectX11] Normal Mapping2024년 03월 06일
- 유니얼
-
작성자
-
2024.03.06.:56
728x90DirectX 11로 게임 엔진 아키텍처 만들기
게임 개발과 3D 그래픽스를 공부하는 과정에서, 우리는 다양한 시각적 기법을 통해 장면의 실감나는 표현을 추구합니다. 이 중 Normal Mapping은 3D 모델의 디테일을 향상시키면서도 성능을 최적화하는 효과적인 방법 중 하나입니다.
참고강의 링크:
Normal Mapping이란?
Normal Mapping은 3D 모델의 표면 디테일을 높이기 위해 사용되는 기법입니다. 모델의 각 픽셀에 대한 노멀(표면의 방향) 정보를 텍스처로 저장하고, 이를 렌더링 시에 활용하여 빛의 반사를 계산합니다. 이 방법은 모델에 추가적인 다각형을 추가하지 않고도, 높은 디테일을 가진 표면을 시뮬레이션할 수 있게 해줍니다.
기본 원리
- 노멀 맵: RGB 이미지로, 각 채널은 각각의 방향(X, Y, Z)에 대한 노멀 벡터의 값을 가집니다. 이 값들은 표면의 방향 변화를 나타냅니다.
- 렌더링 과정: 렌더링 시, 노멀 맵에서 추출한 노멀 벡터를 사용하여 빛의 반사를 계산합니다. 이는 표면의 미세한 높낮이 차이를 고려하여, 더욱 리얼리틱한 조명 효과를 생성합니다.
Normal Mapping의 장점
- 디테일 향상: 고해상도의 노멀 맵을 사용함으로써, 모델의 디테일을 크게 향상시킬 수 있습니다.
- 성능 최적화: 추가적인 지오메트리를 사용하지 않으므로, 렌더링 성능에 미치는 영향이 적습니다.
- 다양한 사용 사례: 거친 표면, 직물의 질감, 벽돌 벽과 같은 다양한 재질의 표현에 사용될 수 있습니다.
NormalMapping
#include "00. Global.fx" #include "00. Light.fx" // 정점 쉐이더: 입력 정점을 처리하여 출력 정점을 생성합니다. MeshOutput VS(VertexTextureNormalTangent input) { MeshOutput output; // 출력 정점 구조체 output.position = mul(input.position, W); // 정점 위치를 월드 좌표로 변환 output.worldPosition = input.position.xyz; // 월드 좌표로의 변환을 저장 (추후 조명 계산에 사용) output.position = mul(output.position, VP); // 뷰-프로젝션 행렬을 적용하여 최종적인 스크린 좌표를 계산 output.uv = input.uv; // 텍스처 좌표는 직접 전달 output.normal = mul(input.normal, (float3x3)W); // 정점의 법선 벡터를 월드 좌표로 변환 output.tangent = mul(input.tangent, (float3x3)W); // 정점의 탄젠트 벡터를 월드 좌표로 변환 return output; // 변환된 정점 데이터를 반환 } // 픽셀 쉐이더: 변환된 정점 데이터를 기반으로 최종 픽셀 색상을 계산합니다. float4 PS(MeshOutput input) : SV_TARGET { ComputeNormalMapping(input.normal, input.tangent, input.uv); // 정규 매핑을 계산하여 법선 벡터를 수정 float4 color = ComputeLight(input.normal, input.uv, input.worldPosition); // 수정된 법선과 다른 입력을 기반으로 조명을 계산 return color; // 최종 색상 반환 } // 렌더링 기법 정의: 하나의 패스를 사용하여 정점 쉐이더와 픽셀 쉐이더를 지정 technique11 T0 { PASS_VP(P0, VS, PS) };
프로젝트 호출
NormalMappingDemo.h
#pragma once #include "IExecute.h" #include "Geometry.h" class NormalMappingDemo : public IExecute { public: void Init() override; void Update() override; void Render() override; shared_ptr<Shader> _shader; shared_ptr<GameObject> _obj; shared_ptr<GameObject> _obj2; shared_ptr<GameObject> _camera; };
NormalMappingDemo.cpp
#include "pch.h" #include "18. NormalMappingDemo.h" #include "GeometryHelper.h" #include "Camera.h" #include "CameraScript.h" #include "Transform.h" #include "Texture.h" #include "ResourceBase.h" #include "MeshRenderer.h" #include "GameObject.h" //#include "RenderManager.h" #include "Material.h" void NormalMappingDemo::Init() { RESOURCES->Init(); _shader = make_shared<Shader>(L"14. NormalMapping.fx"); //Material { shared_ptr<Material> material = make_shared<Material>(); { material->SetShader(_shader); } { auto texture = RESOURCES->Load<Texture>(L"Leather", L"..\\Resources\\Texture\\Leather.jpg"); material->SetDiffuseMap(texture); } { auto texture = RESOURCES->Load<Texture>(L"LeatherNormal", L"..\\Resources\\Texture\\Leather_Normal.jpg"); material->SetNormalMap(texture); } MaterialDesc& desc = material->GetMaterialDesc(); desc.ambient = Vec4(1.0f); desc.diffuse = Vec4(1.0f); desc.specular = Vec4(1.0f); desc.emissive = Vec4(1.0f); RESOURCES->Add(L"Leather", material); } // Camera _camera = make_shared<GameObject>(); _camera->GetOrAddTransform()->SetWorldPosition(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>()); { auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); _obj->GetMeshRenderer()->SetMesh(mesh); } { auto material = RESOURCES->Get<Material>(L"Leather"); _obj->GetMeshRenderer()->SetMaterial(material); } // Object2 _obj2 = make_shared<GameObject>(); _obj2->GetOrAddTransform()->SetWorldPosition(Vec3{ 0.5f, 0.f, 2.f }); _obj2->AddComponent(make_shared<MeshRenderer>()); { auto mesh = RESOURCES->Get<Mesh>(L"Cube"); _obj2->GetMeshRenderer()->SetMesh(mesh); } { auto material = RESOURCES->Get<Material>(L"Leather"); MaterialDesc& desc = material->GetMaterialDesc(); _obj2->GetMeshRenderer()->SetMaterial(material); } // Object //RENDER->Init(_shader); } void NormalMappingDemo::Update() { _camera->Update(); //RENDER->Update(); { LightDesc lightDesc; lightDesc.ambient = Vec4(0.f); lightDesc.diffuse = Vec4(0.f); lightDesc.specular = Vec4(0.f); lightDesc.emissive = Vec4(1.f,0.0f,0.0f,1.0f); lightDesc.direction = Vec3(1.f,0.0f,1.0f); //RENDER->PushLightData(lightDesc); } { MaterialDesc desc; desc.ambient = Vec4(0.2f); desc.diffuse = Vec4(1.0f); desc.specular = Vec4(1.0f); desc.emissive = Color(0.3f,0.f,0.f,0.5f); //RENDER->PushMaterialData(desc); _obj->Update(); } { MaterialDesc desc; desc.ambient = Vec4(0.1f); desc.diffuse = Vec4(1.0f); //desc.specular = Vec4(1.0f); //RENDER->PushMaterialData(desc); _obj2->Update(); } } void NormalMappingDemo::Render() { }
결론
Normal Mapping은 DirectX11 게임 개발과 3D 그래픽스에서 중요한 기법 중 하나입니다. 이를 통해, 개발자와 아티스트는 성능 부담을 크게 늘리지 않으면서도, 높은 수준의 시각적 디테일을 달성할 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드
받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이
감지되어도 모달 창이 표시되지 않습니다.)