PART 3 - 강의 6/7

랜드스케이프 통합

World Partition과 대규모 랜드스케이프의 통합 관리

01

랜드스케이프 아키텍처

UE5 랜드스케이프 시스템의 구조적 이해

구성요소 역할 성능 영향
Landscape Component 렌더링, 가시성, 충돌 기본 단위 컴포넌트당 CPU 비용
Section LOD 계산 기본 단위 섹션당 드로우콜
Landscape Proxy World Partition 통합 시 스트리밍 단위 자동 로드/언로드
메모리 효율성

랜드스케이프는 정점당 4바이트(하이트맵)만 사용하여 Static Mesh(24-28바이트/정점)보다 6-7배 메모리 효율적입니다.

02

World Partition 통합

랜드스케이프와 World Partition의 자동 통합

랜드스케이프는 World Partition과 네이티브 통합됩니다. 에디터에서는 단일 통합 오브젝트로 편집하면서, 내부적으로는 별도 Landscape Proxy 액터로 분할되어 스트리밍됩니다.

Editor Settings // World Settings에서 랜드스케이프 스트리밍 설정 World Settings > World Partition > Landscape Settings // 주요 설정 Enable Landscape Streaming: true Landscape Grid Size: (World Partition Cell Size와 일치 권장) Generate Landscape Proxy Per Component: true

권장 크기 설정

Landscape Configuration // 권장 랜드스케이프 설정 Section Size: 127 x 127 (정점) Sections Per Component: 2 x 2 Component Count: 적절한 월드 크기에 맞춰 조정 // World Partition Cell Size 계산 // Cell Size = Component Size = Section Size * Sections Per Component * Scale // 예: 127 * 2 * 100 = 25,400 cm (약 254m) // 권장: Cell Size를 Component Size의 배수로 설정 Cell Size: 25600 (256m) 또는 51200 (512m)
03

LOD 및 텍스처 스트리밍

거리 기반 세부도 조절

랜드스케이프 LOD는 텍스처 밉맵에 저장되어 부드러운 LOD 전환이 가능합니다.

LOD Settings // Landscape Actor Details에서 설정 Details > LOD // LOD Distribution Setting LOD 0 Screen Size: 1.0 // 가장 상세 LOD 1 Screen Size: 0.5 LOD 2 Screen Size: 0.25 LOD 3 Screen Size: 0.125 // 가장 단순 // 텍스처 스트리밍 // - 필요한 밉맵만 로드 // - 원거리 LOD는 낮은 밉맵 사용 // - 표준 UE 스트리밍 시스템 활용
C++ LOD 제어 #include "Landscape/LandscapeProxy.h" // 런타임 LOD 바이어스 조정 void AMyGameMode::AdjustLandscapeLOD(float LODBias) { // 모든 Landscape Proxy 찾기 TArray<AActor*> LandscapeActors; UGameplayStatics::GetAllActorsOfClass( GetWorld(), ALandscapeProxy::StaticClass(), LandscapeActors ); for (AActor* Actor : LandscapeActors) { if (ALandscapeProxy* Landscape = Cast<ALandscapeProxy>(Actor)) { // LOD 바이어스 설정 (-1.0 ~ 1.0) // 음수: 더 상세한 LOD 사용 // 양수: 더 단순한 LOD 사용 Landscape->LODBias = LODBias; } } }
04

Runtime Virtual Texturing

GPU 기반 실시간 텍스처 생성

Runtime Virtual Texture(RVT)는 복잡한 랜드스케이프 머티리얼을 GPU에서 실시간으로 캐싱하여 VRAM 사용량을 크게 줄입니다.

RVT 설정 단계 // 1. 프로젝트 설정 활성화 Project Settings > Engine > Rendering > Virtual Textures Enable virtual texture support = true // 2. RVT Asset 생성 Content Browser > Add > Miscellaneous > Runtime Virtual Texture - Size: 8192 x 8192 (월드 크기에 따라) - Virtual Texture Content: Base Color, Normal, etc. // 3. RVT Volume 배치 Place Actors > Runtime Virtual Texture Volume - 랜드스케이프를 완전히 포함하도록 크기 조정 - Virtual Texture 할당 // 4. 랜드스케이프 머티리얼에 RVT 노드 추가 Material Editor: - Runtime Virtual Texture Sample 노드 추가 - Write to Runtime Virtual Texture Output 연결
RVT 성능 이점
  • VRAM 절약 - 테스트 사례: 8GB에서 3.5GB로 감소
  • 레이어 수 제한 해제 - 기존 16레이어 제한 우회
  • 데칼 최적화 - RVT에 데칼 렌더링으로 드로우콜 감소
  • 머티리얼 복잡도 - 복잡한 블렌딩도 한 번만 계산
05

C++ 랜드스케이프 API

프로그래밍을 통한 랜드스케이프 조작

LandscapeHelper.h #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "LandscapeHelper.generated.h" UCLASS() class MYGAME_API ULandscapeHelper : public UBlueprintFunctionLibrary { GENERATED_BODY() public: // 특정 위치의 랜드스케이프 높이 가져오기 UFUNCTION(BlueprintCallable, Category = "Landscape") static float GetLandscapeHeightAtLocation( UObject* WorldContext, FVector Location); // 특정 위치의 랜드스케이프 노말 가져오기 UFUNCTION(BlueprintCallable, Category = "Landscape") static FVector GetLandscapeNormalAtLocation( UObject* WorldContext, FVector Location); // 특정 위치의 레이어 웨이트 가져오기 UFUNCTION(BlueprintCallable, Category = "Landscape") static TMap<FName, float> GetLandscapeLayerWeightsAtLocation( UObject* WorldContext, FVector Location); };
LandscapeHelper.cpp #include "LandscapeHelper.h" #include "Landscape/LandscapeProxy.h" #include "LandscapeComponent.h" #include "Kismet/GameplayStatics.h" float ULandscapeHelper::GetLandscapeHeightAtLocation( UObject* WorldContext, FVector Location) { if (!WorldContext) return 0.0f; UWorld* World = GEngine->GetWorldFromContextObject( WorldContext, EGetWorldErrorMode::LogAndReturnNull); if (!World) return 0.0f; // 라인트레이스로 높이 확인 FHitResult HitResult; FVector Start = FVector(Location.X, Location.Y, 100000.0f); FVector End = FVector(Location.X, Location.Y, -100000.0f); FCollisionQueryParams QueryParams; QueryParams.bTraceComplex = true; if (World->LineTraceSingleByChannel( HitResult, Start, End, ECC_WorldStatic, QueryParams)) { // 랜드스케이프 컴포넌트인지 확인 if (HitResult.Component.IsValid()) { if (Cast<ULandscapeComponent>(HitResult.Component.Get())) { return HitResult.Location.Z; } } } return 0.0f; } FVector ULandscapeHelper::GetLandscapeNormalAtLocation( UObject* WorldContext, FVector Location) { if (!WorldContext) return FVector::UpVector; UWorld* World = GEngine->GetWorldFromContextObject( WorldContext, EGetWorldErrorMode::LogAndReturnNull); if (!World) return FVector::UpVector; FHitResult HitResult; FVector Start = FVector(Location.X, Location.Y, 100000.0f); FVector End = FVector(Location.X, Location.Y, -100000.0f); FCollisionQueryParams QueryParams; QueryParams.bTraceComplex = true; if (World->LineTraceSingleByChannel( HitResult, Start, End, ECC_WorldStatic, QueryParams)) { if (Cast<ULandscapeComponent>(HitResult.Component.Get())) { return HitResult.Normal; } } return FVector::UpVector; } TMap<FName, float> ULandscapeHelper::GetLandscapeLayerWeightsAtLocation( UObject* WorldContext, FVector Location) { TMap<FName, float> LayerWeights; if (!WorldContext) return LayerWeights; UWorld* World = GEngine->GetWorldFromContextObject( WorldContext, EGetWorldErrorMode::LogAndReturnNull); if (!World) return LayerWeights; // 모든 랜드스케이프 프록시 검색 TArray<AActor*> LandscapeActors; UGameplayStatics::GetAllActorsOfClass( World, ALandscapeProxy::StaticClass(), LandscapeActors ); for (AActor* Actor : LandscapeActors) { ALandscapeProxy* Landscape = Cast<ALandscapeProxy>(Actor); if (!Landscape) continue; // 해당 위치의 컴포넌트 찾기 ULandscapeComponent* Component = Landscape->GetComponentAt(Location.X, Location.Y); if (Component) { // 레이어 웨이트 데이터 추출 // (실제 구현은 WeightmapUsageMap 접근 필요) break; } } return LayerWeights; }
SUMMARY

핵심 요약

  • Landscape Proxy로 World Partition과 자동 통합
  • Cell Size를 Landscape Component Size와 일치시켜 최적화
  • LOD는 밉맵 기반으로 부드러운 전환 제공
  • Runtime Virtual Texture로 VRAM 사용량 대폭 감소
  • C++ API로 런타임 높이, 노말, 레이어 정보 조회 가능
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 랜드스케이프와 World Partition 연동

Landscape를 World Partition 환경에서 생성하고, 랜드스케이프 컴포넌트 크기와 Cell Size를 일치시키세요. 랜드스케이프 스트리밍이 올바르게 작동하는지 확인하세요.

실습 2: 랜드스케이프 레이어 페인팅

RPG 오픈월드에 적합한 Landscape Material을 설정하고(잔디, 흙, 바위, 눈), 각 레이어를 페인팅하세요. Landscape Layer Blend 노드를 활용한 자연스러운 텍스처 전환을 구현하세요.

심화 과제: 대규모 랜드스케이프 최적화

8x8km 이상의 대규모 랜드스케이프에서 Virtual Texture, Nanite Landscape(UE5.4+), LOD 설정을 조합하여 최적화하세요. 랜드스케이프 컴포넌트 수와 해상도에 따른 메모리/성능 트레이드오프를 분석하세요.