• 티스토리 홈
  • 프로필사진
    유니얼
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
유니얼
  • 프로필사진
    유니얼
    • 분류 전체보기 (295)
      • Unity (17)
        • 게임 개발 (5)
      • Unreal (24)
        • 게임 개발 (20)
      • DirectX (36)
      • 코딩테스트 (91)
        • 프로그래머스 (25)
        • 백준 (66)
      • Google Workspace (1)
      • Programing (102)
        • C# (68)
        • C++ (24)
        • JavaScript (10)
      • 게임 서버 프로그래밍 (17)
      • Web (6)
        • 슈퍼코딩 (6)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
    등록된 댓글이 없습니다.
  • 최근 공지
    등록된 공지가 없습니다.
# Home
# 공지사항
#
# 태그
# 검색결과
# 방명록
  • [DirectX11] HeightMap을 활용한 지형 생성
    2024년 03월 05일
    • 유니얼
    • 작성자
    • 2024.03.05.:46
    728x90

    DirectX 11로 게임 엔진 아키텍처 만들기

    3D 게임 개발 및 시각화 프로젝트에서 사실적인 지형을 생성하는 것은 몰입감을 높이는 중요한 요소입니다. DirectX11과 HeightMap을 활용하면, 효율적으로 다양한 지형을 구현할 수 있습니다. 이번 글에서는 HeightMap 기반 지형 생성 프로세스를 간단한 예제 코드를 통해 설명합니다.

     

    Result

    참고강의 링크:

    https://www.inflearn.com/course/directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98/dashboard

     

    [게임 프로그래머 도약반] DirectX11 입문 강의 - 인프런

    게임 프로그래머 공부에 있어서 필수적인 DirectX 11 지식을 초보자들의 눈높이에 맞춰 설명하는 강의입니다., [사진][사진] [사진] 게임 개발자는 Unreal, Unity만 사용할 줄 알면 되는 거 아닌가요? 엔

    www.inflearn.com

    HeightMap이란?

    HeightMap은 3차원 풍경을 모델링할 때 자주 사용되는 기술로, 각 지점의 높이 정보를 담고 있는 2차원 이미지입니다. 흑백 이미지에서 각 픽셀의 밝기가 해당 지점의 높이를 나타냅니다. 밝은 부분이 높은 지역을, 어두운 부분이 낮은 지역을 나타내며, 이를 통해 복잡한 지형을 상대적으로 적은 데이터로 표현할 수 있습니다.

    HeightMap의 활용

    HeightMap은 주로 지형 생성에 사용됩니다. 간단한 2차원 그리드(mesh) 위에 HeightMap을 적용하여, 각 정점(vertex)의 높이를 조절함으로써 3차원 지형을 만들어냅니다. 이 기법은 자연스러운 지형 모델링뿐만 아니라 도시의 건물 배치와 같은 다양한 3D 모델링 작업에도 활용됩니다.

    DirectX11에서의 HeightMap 사용

    DirectX11에서 HeightMap을 사용하기 위해 먼저 HeightMap 이미지를 로드하고, 이를 기반으로 각 정점의 높이 정보를 설정해야 합니다. 다음은 HeightMap을 적용하여 지형을 생성하는 과정을 간략하게 설명한 것입니다:

    1, HeightMap 로드

    HeightMap 이미지는 주로 흑백 이미지로 저장되며, DirectX11에서는 DirectXTK 라이브러리를 통해 쉽게 로드할 수 있습니다. 이미지의 각 픽셀 값을 읽어 높이 정보로 변환합니다.

    2, 지형 메쉬 생성

    2차원 그리드 형태의 지형 메쉬를 생성합니다. 이때, 메쉬의 각 정점에 HeightMap에서 얻은 높이 정보를 적용하여 3차원 지형을 형성합니다.

    3, 정점 셰이더(Vertex Shader) 적용

    정점 셰이더에서는 각 정점의 위치를 HeightMap에 기반한 높이로 조정합니다. 이 과정을 통해 실제 3D 지형이 구성됩니다.

    4, 텍스처와 조명 적용

    생성된 지형에 텍스처를 매핑하고 조명 효과를 추가하여, 더욱 사실적인 지형을 완성합니다. HeightMap은 지형의 형태만 결정하기 때문에, 외관을 개선하기 위해서는 추가적인 텍스처 매핑이 필요합니다.

    예제: HeightMap을 이용한 지형 생성

    다음은 DirectX11에서 HeightMap을 이용해 지형을 생성하는 과정을 단계별로 설명한 코드입니다.

    Terrain.fx

    // 3D 오브젝트의 위치, 방향, 크기 등을 결정하는 변환 행렬들을 정의합니다.
    matrix World;
    matrix View;
    matrix Projection;
    
    // 쉐이더에서 사용할 텍스처를 정의합니다.
    Texture2D Texture0;
    
    // 버텍스 쉐이더로 넘어오는 입력 데이터 구조체입니다. 각 버텍스의 위치와 텍스처 좌표를 포함합니다.
    struct VertexInput
    {
    	float4 position : POSITION; // 3D 공간에서의 버텍스 위치
    	float2 uv : TEXCOORD;       // 텍스처 좌표
    };
    
    // 버텍스 쉐이더의 출력 데이터 구조체입니다. 처리된 버텍스 위치와 텍스처 좌표를 포함합니다.
    struct VertexOutput
    {
    	float4 position : SV_POSITION; // 스크린 공간에서의 버텍스 위치
    	float2 uv : TEXCOORD;          // 텍스처 좌표
    };
    
    // 버텍스 쉐이더 함수입니다. 3D 오브젝트의 각 버텍스 위치를 처리합니다.
    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;
    
    	return output;
    }
    
    // 텍스처 샘플링을 위한 샘플러 상태를 정의합니다. 여기서는 UV 좌표가 범위를 벗어날 경우 텍스처를 반복합니다.
    SamplerState Sampler0
    {
    	AddressU = Wrap; // U 좌표가 1을 초과할 경우 텍스처를 반복
    	AddressV = Wrap; // V 좌표가 1을 초과할 경우 텍스처를 반복
    };
    
    // 픽셀 쉐이더 함수입니다. 최종적으로 화면에 렌더링될 픽셀의 색상을 결정합니다.
    float4 PS(VertexOutput input) : SV_TARGET
    {
    	// 정의된 샘플러를 사용하여 텍스처에서 색상 값을 샘플링하고 반환합니다.
    	return Texture0.Sample(Sampler0, input.uv);
    }
    
    // 래스터라이저 상태를 정의합니다. 이 예제에서는 와이어프레임 모드를 설정합니다.
    RasterizerState FillModeWireFrame
    {
    	FillMode = Wireframe; // 렌더링 모드를 와이어프레임으로 설정
    };
    
    // 렌더링 기법을 정의합니다. 이 기법은 두 개의 패스를 사용합니다.
    technique11 T0
    {
    	pass P0
    	{
    		// 첫 번째 패스에서는 버텍스 쉐이더와 픽셀 쉐이더를 설정합니다.
    		SetVertexShader(CompileShader(vs_5_0, VS()));
    		SetPixelShader(CompileShader(ps_5_0, PS()));
    	}
    
    	pass P1
    	{
    		// 두 번째 패스에서는 래스터라이저 상태를 와이어프레임 모드로 설정하고, 
    		// 동일한 버텍스 쉐이더와 픽셀 쉐이더를 재사용합니다.
    		SetRasterizerState(FillModeWireFrame);
    
    		SetVertexShader(CompileShader(vs_5_0, VS()));
    		SetPixelShader(CompileShader(ps_5_0, PS()));
    	}
    };

    프로젝트 호출

    생성한 Sampler는 픽셀 셰이더에서 사용됩니다. ID3D11DeviceContext::PSSetSamplers() 함수를 호출하여 픽셀 셰이더의 Sampler 슬롯에 바인딩합니다. 이렇게 하면 픽셀 셰이더에서 텍스처를 샘플링할 때 해당 Sampler의 설정을 사용하게 됩니다.

    HeightMapDemo.h

    #pragma once
    #include "IExecute.h"
    #include "Geometry.h"
    #include "GameObject.h"
    
    class HeightMapDemo : public IExecute
    {
    public:
    	void Init() override;
    	void Update() override;
    	void Render() override;
    
    	shared_ptr<Shader> _shader;
    
    	//Object
    	shared_ptr<Geometry<VertexTextureData>> _geometry;
    	shared_ptr<VertexBuffer> _vertexBuffer;
    	shared_ptr<IndexBuffer> _indexBuffer;
    
    	Vec3 _translation = Vec3(0.f, 0.f, 0.f);
    
    	Matrix _world = Matrix::Identity;
    
    	//Camera
    	shared_ptr<GameObject> _camera;
    
    	shared_ptr<Texture> _heightMap;
    	shared_ptr<Texture> _texture;
    };

    HeightMapDemo.cpp

    #include "pch.h" // 프리컴파일 헤더
    #include "07. HeightMapDemo.h" // 이 클래스의 헤더 파일
    #include "GeometryHelper.h" // 지오메트리 생성을 돕는 함수들이 있는 헤더 파일
    #include "Camera.h" // 카메라 구현 헤더 파일
    #include "CameraScript.h" // 카메라 스크립트(조작 등) 헤더 파일
    #include "Transform.h" // 변환(위치, 회전, 크기 조정) 헤더 파일
    #include "Texture.h" // 텍스처 관련 헤더 파일
    #include "ResourceBase.h" // 리소스 관리 기본 클래스 헤더 파일
    
    void HeightMapDemo::Init()
    {
    	// 쉐이더와 텍스처 로드
    	_shader = make_shared<Shader>(L"06. Terrain.fx");
    	_heightMap = RESOURCES->Load<Texture>(L"Height", L"..\\Resources\\Texture\\Terrain\\height.png");
    	_texture = RESOURCES->Load<Texture>(L"Grass", L"..\\Resources\\Texture\\Terrain\\grass.jpg");
    
    	// 높이 맵의 크기 정보를 가져옴
    	const int32 width = _heightMap->GetSize().x;
    	const int32 height = _heightMap->GetSize().y;
    
    	// 픽셀 버퍼에서 높이 데이터를 가져옴
    	const DirectX::ScratchImage& info = _heightMap->GetInfo();
    	uint8* pixelBuffer = info.GetPixels();
    
    	// 지형 메시 생성
    	_geometry = make_shared<Geometry<VertexTextureData>>();
    	GeometryHelper::CreateGrid(_geometry, width, height);
    
    	// CPU에서 지형의 높이를 설정
    	{
    		vector<VertexTextureData>& v = const_cast<vector<VertexTextureData>&>(_geometry->GetVertices());
    
    		for (int32 z = 0; z < height; z++)
    		{
    			for (int32 x = 0; x < width; x++)
    			{
    				int32 idx = width * z + x;
    				uint8 height = pixelBuffer[idx] / 255.f * 25.f; // 픽셀 값에 따라 높이 결정
    				v[idx].position.y = height; // 높이 값 설정
    			}
    		}
    	}
    
    	// 버텍스 버퍼와 인덱스 버퍼 생성 및 데이터 설정
    	_vertexBuffer = make_shared<VertexBuffer>();
    	_vertexBuffer->Create(_geometry->GetVertices());
    	_indexBuffer = make_shared<IndexBuffer>();
    	_indexBuffer->Create(_geometry->GetIndices());
    
    	// 카메라 설정
    	_camera = make_shared<GameObject>();
    	_camera->GetOrAddTransform();
    	_camera->AddComponent(make_shared<Camera>());
    	_camera->AddComponent(make_shared<CameraScript>());
    
    	// 카메라 초기 위치 및 회전 설정
    	_camera->GetTransform()->SetWorldPosition(Vec3(0.f, 5.f, 0.f));
    	_camera->GetTransform()->SetWorldRotation(Vec3(25.f, 0.f, 0.f));
    }
    
    void HeightMapDemo::Update()
    {
    	_camera->Update(); // 카메라 업데이트
    }
    
    void HeightMapDemo::Render()
    {
    	// 쉐이더에 변환 행렬과 텍스처 리소스 바인딩
    	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
    	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
    	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
    	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
    
    	// 버텍스 버퍼와 인덱스 버퍼를 입력 어셈블러에 바인딩
    	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, _indexBuffer->GetCount(), 0, 0);
    }

    결론

    HeightMap을 활용한 지형 생성은 복잡한 자연 지형을 실시간으로 렌더링하는 데 매우 유용합니다. DirectX11을 사용하면, 이러한 프로세스를 통해 생성된 지형에 고급 렌더링 기술을 적용하여 보다 사실적인 3D 환경을 구현할 수 있습니다.

    반응형
    다음글
    다음 글이 없습니다.
    이전글
    이전 글이 없습니다.
    댓글
조회된 결과가 없습니다.
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
목차
표시할 목차가 없습니다.
    • 안녕하세요
    • 감사해요
    • 잘있어요

    티스토리툴바