MetaHuman 엔진 내 통합
UE 5.6부터 가능해진 완전한 엔진 내 MetaHuman 워크플로우
엔진 내 MetaHuman Creator
웹 앱에서 엔진 내부로 이전
UE 5.6부터 MetaHuman Creator가 기존 웹 앱에서 Unreal Engine 내부로 이전되었습니다. 이를 통해 로컬에서 더 빠른 워크플로우로 MetaHuman을 제작하고 커스터마이즈할 수 있습니다.
- 웹 앱 대신 에디터 내에서 직접 실행
- 로컬 처리로 더 빠른 반복 작업
- Python/Blueprint 배치 처리 자동화 지원
- UE 5.7에서 Linux, macOS 플랫폼 지원 추가
- Maya 2026 지원 (MetaHuman for Maya)
지원 플랫폼 (UE 5.7)
Windows
UE 5.6+
Linux
UE 5.7+
macOS
UE 5.7+
C++ MetaHuman 관리 시스템
프로그래매틱 MetaHuman 제어
// MetaHumanManager.h
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MetaHumanManager.generated.h"
/**
* MetaHuman 관리 서브시스템
* NPC 및 플레이어 캐릭터의 MetaHuman 에셋 관리
*/
USTRUCT(BlueprintType)
struct FMetaHumanAppearanceData
{
GENERATED_BODY()
// 얼굴 블렌드쉐이프 값
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TMap<FName, float> FacialBlendShapes;
// 피부 톤
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor SkinTone = FLinearColor(0.8f, 0.65f, 0.55f);
// 눈 색상
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor EyeColor = FLinearColor(0.2f, 0.15f, 0.1f);
// 헤어 스타일 에셋
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UGroomAsset> HairStyle;
// 헤어 색상
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor HairColor = FLinearColor(0.1f, 0.08f, 0.05f);
// 체형
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float BodyWeight = 0.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float BodyMuscle = 0.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Height = 175.0f; // cm
};
UCLASS()
class MYGAME_API UMetaHumanManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
// MetaHuman 에셋 로드
UFUNCTION(BlueprintCallable, Category = "MetaHuman")
void LoadMetaHumanAsset(
const FString& MetaHumanId,
FOnMetaHumanLoaded OnLoaded);
// 외형 적용
UFUNCTION(BlueprintCallable, Category = "MetaHuman")
void ApplyAppearance(
ACharacter* Character,
const FMetaHumanAppearanceData& Appearance);
// 블렌드쉐이프 실시간 업데이트
UFUNCTION(BlueprintCallable, Category = "MetaHuman")
void UpdateFacialBlendShape(
ACharacter* Character,
FName BlendShapeName,
float Value);
// LOD 레벨 설정
UFUNCTION(BlueprintCallable, Category = "MetaHuman")
void SetMetaHumanLOD(
ACharacter* Character,
int32 LODLevel);
// 배치 처리용 다중 MetaHuman 로드
UFUNCTION(BlueprintCallable, Category = "MetaHuman")
void BatchLoadMetaHumans(
const TArray<FString>& MetaHumanIds,
FOnMetaHumanBatchLoaded OnBatchLoaded);
protected:
// 캐시된 MetaHuman 데이터
TMap<FString, FMetaHumanCachedData> MetaHumanCache;
// 비동기 로딩 핸들
TArray<TSharedPtr<FStreamableHandle>> LoadingHandles;
};
// MetaHumanManager.cpp
#include "MetaHumanManager.h"
#include "Engine/StreamableManager.h"
#include "Components/SkeletalMeshComponent.h"
#include "GroomComponent.h"
void UMetaHumanManager::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
UE_LOG(LogMetaHuman, Log, TEXT("MetaHuman Manager 초기화"));
}
void UMetaHumanManager::ApplyAppearance(
ACharacter* Character,
const FMetaHumanAppearanceData& Appearance)
{
if (!Character) return;
USkeletalMeshComponent* Mesh = Character->GetMesh();
if (!Mesh) return;
// 블렌드쉐이프 적용
for (const auto& Pair : Appearance.FacialBlendShapes)
{
Mesh->SetMorphTarget(Pair.Key, Pair.Value);
}
// 머티리얼 파라미터 적용
for (int32 i = 0; i < Mesh->GetNumMaterials(); ++i)
{
UMaterialInstanceDynamic* MID =
Mesh->CreateAndSetMaterialInstanceDynamic(i);
if (MID)
{
// 피부 톤
MID->SetVectorParameterValue(FName("SkinColor"), Appearance.SkinTone);
// 눈 색상 (눈 머티리얼 슬롯에만)
if (Mesh->GetMaterialSlotNames()[i].ToString().Contains("Eye"))
{
MID->SetVectorParameterValue(FName("IrisColor"), Appearance.EyeColor);
}
}
}
// 헤어 Groom 컴포넌트 설정
TArray<UGroomComponent*> GroomComponents;
Character->GetComponents<UGroomComponent>(GroomComponents);
for (UGroomComponent* GroomComp : GroomComponents)
{
if (GroomComp->ComponentHasTag(FName("Hair")))
{
// 헤어 에셋 교체
if (UGroomAsset* HairAsset = Appearance.HairStyle.LoadSynchronous())
{
GroomComp->SetGroomAsset(HairAsset);
}
// 헤어 색상
if (UMaterialInstanceDynamic* HairMID =
GroomComp->CreateDynamicMaterialInstance(0))
{
HairMID->SetVectorParameterValue(FName("HairColor"), Appearance.HairColor);
}
}
}
// 체형 적용 (스켈레탈 모디파이어 또는 블렌드쉐이프)
ApplyBodyShape(Mesh, Appearance.BodyWeight, Appearance.BodyMuscle, Appearance.Height);
}
void UMetaHumanManager::UpdateFacialBlendShape(
ACharacter* Character,
FName BlendShapeName,
float Value)
{
if (!Character) return;
USkeletalMeshComponent* Mesh = Character->GetMesh();
if (Mesh)
{
// 값 클램프 (0-1)
Value = FMath::Clamp(Value, 0.0f, 1.0f);
Mesh->SetMorphTarget(BlendShapeName, Value);
}
}
void UMetaHumanManager::SetMetaHumanLOD(
ACharacter* Character,
int32 LODLevel)
{
if (!Character) return;
// 메인 스켈레탈 메시 LOD
USkeletalMeshComponent* Mesh = Character->GetMesh();
if (Mesh)
{
Mesh->SetForcedLOD(LODLevel);
}
// Groom 컴포넌트 LOD
TArray<UGroomComponent*> GroomComponents;
Character->GetComponents<UGroomComponent>(GroomComponents);
for (UGroomComponent* GroomComp : GroomComponents)
{
// Groom LOD도 함께 조정
GroomComp->SetForcedLOD(LODLevel);
}
}
void UMetaHumanManager::BatchLoadMetaHumans(
const TArray<FString>& MetaHumanIds,
FOnMetaHumanBatchLoaded OnBatchLoaded)
{
TArray<FSoftObjectPath> AssetsToLoad;
for (const FString& Id : MetaHumanIds)
{
// MetaHuman 에셋 경로 구성
FString BasePath = FString::Printf(
TEXT("/Game/MetaHumans/%s/"), *Id);
AssetsToLoad.Add(FSoftObjectPath(BasePath + "BP_" + Id));
AssetsToLoad.Add(FSoftObjectPath(BasePath + "Face/" + Id + "_FaceMesh"));
AssetsToLoad.Add(FSoftObjectPath(BasePath + "Body/" + Id + "_Body"));
}
// 비동기 배치 로드
FStreamableManager& StreamableManager =
UAssetManager::GetStreamableManager();
TSharedPtr<FStreamableHandle> Handle = StreamableManager.RequestAsyncLoad(
AssetsToLoad,
FStreamableDelegate::CreateLambda([OnBatchLoaded, MetaHumanIds]()
{
OnBatchLoaded.ExecuteIfBound(MetaHumanIds, true);
})
);
LoadingHandles.Add(Handle);
}
페이셜 애니메이션 시스템
런타임 표정 제어
// MetaHumanFacialAnimator.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MetaHumanFacialAnimator.generated.h"
/**
* MetaHuman 페이셜 애니메이션 컴포넌트
* 블렌드쉐이프 기반 표정 애니메이션
*/
USTRUCT(BlueprintType)
struct FFacialExpression
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName ExpressionName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TMap<FName, float> BlendShapeValues;
};
UCLASS(ClassGroup=(MetaHuman), meta=(BlueprintSpawnableComponent))
class MYGAME_API UMetaHumanFacialAnimator : public UActorComponent
{
GENERATED_BODY()
public:
UMetaHumanFacialAnimator();
virtual void TickComponent(float DeltaTime,
ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
// 표정 재생
UFUNCTION(BlueprintCallable, Category = "Facial")
void PlayExpression(const FFacialExpression& Expression, float BlendTime = 0.2f);
// 사전 정의 표정 재생
UFUNCTION(BlueprintCallable, Category = "Facial")
void PlayPresetExpression(FName PresetName, float BlendTime = 0.2f);
// 말하기 애니메이션 (립싱크)
UFUNCTION(BlueprintCallable, Category = "Facial")
void StartLipSync(UAudioComponent* AudioSource);
UFUNCTION(BlueprintCallable, Category = "Facial")
void StopLipSync();
// 눈 깜빡임
UFUNCTION(BlueprintCallable, Category = "Facial")
void Blink();
UFUNCTION(BlueprintCallable, Category = "Facial")
void SetAutoBlinkEnabled(bool bEnabled);
protected:
// 사전 정의 표정 맵
UPROPERTY(EditDefaultsOnly, Category = "Presets")
TMap<FName, FFacialExpression> PresetExpressions;
// 현재 표정 상태
TMap<FName, float> CurrentBlendShapes;
TMap<FName, float> TargetBlendShapes;
float BlendProgress = 1.0f;
float BlendDuration = 0.2f;
// 자동 눈 깜빡임
bool bAutoBlinkEnabled = true;
float NextBlinkTime = 0.0f;
float BlinkCooldown = 0.0f;
// 립싱크
UPROPERTY()
TObjectPtr<UAudioComponent> LipSyncAudio;
void UpdateBlendShapes(float DeltaTime);
void UpdateAutoBlink(float DeltaTime);
void UpdateLipSync(float DeltaTime);
USkeletalMeshComponent* GetFaceMesh() const;
};
// MetaHumanFacialAnimator.cpp
#include "MetaHumanFacialAnimator.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/AudioComponent.h"
UMetaHumanFacialAnimator::UMetaHumanFacialAnimator()
{
PrimaryComponentTick.bCanEverTick = true;
// 기본 표정 프리셋 설정
FFacialExpression HappyExpression;
HappyExpression.ExpressionName = FName("Happy");
HappyExpression.BlendShapeValues.Add(FName("mouthSmile_L"), 1.0f);
HappyExpression.BlendShapeValues.Add(FName("mouthSmile_R"), 1.0f);
HappyExpression.BlendShapeValues.Add(FName("cheekSquint_L"), 0.5f);
HappyExpression.BlendShapeValues.Add(FName("cheekSquint_R"), 0.5f);
PresetExpressions.Add(FName("Happy"), HappyExpression);
FFacialExpression SadExpression;
SadExpression.ExpressionName = FName("Sad");
SadExpression.BlendShapeValues.Add(FName("browInnerUp"), 0.8f);
SadExpression.BlendShapeValues.Add(FName("mouthFrown_L"), 0.7f);
SadExpression.BlendShapeValues.Add(FName("mouthFrown_R"), 0.7f);
PresetExpressions.Add(FName("Sad"), SadExpression);
FFacialExpression AngryExpression;
AngryExpression.ExpressionName = FName("Angry");
AngryExpression.BlendShapeValues.Add(FName("browDown_L"), 1.0f);
AngryExpression.BlendShapeValues.Add(FName("browDown_R"), 1.0f);
AngryExpression.BlendShapeValues.Add(FName("noseSneer_L"), 0.5f);
AngryExpression.BlendShapeValues.Add(FName("noseSneer_R"), 0.5f);
PresetExpressions.Add(FName("Angry"), AngryExpression);
}
void UMetaHumanFacialAnimator::TickComponent(
float DeltaTime,
ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateBlendShapes(DeltaTime);
UpdateAutoBlink(DeltaTime);
UpdateLipSync(DeltaTime);
}
void UMetaHumanFacialAnimator::PlayExpression(
const FFacialExpression& Expression,
float BlendTime)
{
TargetBlendShapes = Expression.BlendShapeValues;
BlendDuration = FMath::Max(BlendTime, 0.01f);
BlendProgress = 0.0f;
}
void UMetaHumanFacialAnimator::PlayPresetExpression(
FName PresetName,
float BlendTime)
{
if (const FFacialExpression* Preset = PresetExpressions.Find(PresetName))
{
PlayExpression(*Preset, BlendTime);
}
}
void UMetaHumanFacialAnimator::Blink()
{
// 눈 깜빡임 블렌드쉐이프
USkeletalMeshComponent* FaceMesh = GetFaceMesh();
if (!FaceMesh) return;
// 빠른 깜빡임 애니메이션
FaceMesh->SetMorphTarget(FName("eyeBlink_L"), 1.0f);
FaceMesh->SetMorphTarget(FName("eyeBlink_R"), 1.0f);
// 타이머로 눈 뜨기
GetWorld()->GetTimerManager().SetTimerForNextTick([this, FaceMesh]()
{
if (FaceMesh)
{
FaceMesh->SetMorphTarget(FName("eyeBlink_L"), 0.0f);
FaceMesh->SetMorphTarget(FName("eyeBlink_R"), 0.0f);
}
});
}
void UMetaHumanFacialAnimator::UpdateBlendShapes(float DeltaTime)
{
if (BlendProgress >= 1.0f) return;
USkeletalMeshComponent* FaceMesh = GetFaceMesh();
if (!FaceMesh) return;
BlendProgress = FMath::Clamp(BlendProgress + (DeltaTime / BlendDuration), 0.0f, 1.0f);
float Alpha = FMath::InterpEaseInOut(0.0f, 1.0f, BlendProgress, 2.0f);
// 현재 값과 타겟 값 보간
for (const auto& Pair : TargetBlendShapes)
{
float CurrentValue = 0.0f;
if (float* Found = CurrentBlendShapes.Find(Pair.Key))
{
CurrentValue = *Found;
}
float NewValue = FMath::Lerp(CurrentValue, Pair.Value, Alpha);
FaceMesh->SetMorphTarget(Pair.Key, NewValue);
CurrentBlendShapes.FindOrAdd(Pair.Key) = NewValue;
}
}
void UMetaHumanFacialAnimator::UpdateAutoBlink(float DeltaTime)
{
if (!bAutoBlinkEnabled) return;
BlinkCooldown -= DeltaTime;
if (BlinkCooldown <= 0.0f)
{
Blink();
// 2-6초 사이 랜덤 간격
BlinkCooldown = FMath::FRandRange(2.0f, 6.0f);
}
}
MetaHuman 최적화와 LOD
성능 설정, LOD 레벨, 블루프린트 통합
MetaHuman은 매우 높은 품질의 캐릭터를 제공하지만, 다수의 NPC가 동시에 렌더링되는 오픈월드 RPG에서는 체계적인 LOD 관리와 성능 최적화가 필수적입니다. 거리 기반 LOD, Groom 최적화, 그리고 블루프린트 연동을 통해 성능과 품질의 균형을 맞출 수 있습니다.
LOD 관리 시스템
// MetaHumanLODController.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MetaHumanLODController.generated.h"
/**
* MetaHuman LOD 레벨 정의
* LOD 0: 풀 퀄리티 (대화, 컷신)
* LOD 1: 중거리 (일반 NPC)
* LOD 2: 원거리 (군중)
* LOD 3: 최저 퀄리티 (먼 배경)
*/
USTRUCT(BlueprintType)
struct FMetaHumanLODConfig
{
GENERATED_BODY()
// LOD 전환 거리 (cm)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float TransitionDistance = 500.0f;
// Groom(헤어) 활성화 여부
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bEnableGroom = true;
// 페이셜 애니메이션 활성화
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bEnableFacialAnim = true;
// 그림자 캐스팅
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bCastShadow = true;
// 물리 시뮬레이션 (헤어, 옷)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bEnablePhysics = true;
};
UCLASS(ClassGroup=(MetaHuman), meta=(BlueprintSpawnableComponent))
class MYGAME_API UMetaHumanLODController : public UActorComponent
{
GENERATED_BODY()
public:
UMetaHumanLODController();
virtual void TickComponent(float DeltaTime,
ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
// LOD 레벨별 설정
UPROPERTY(EditAnywhere, Category = "LOD")
TArray<FMetaHumanLODConfig> LODConfigs;
// 현재 LOD 레벨
UPROPERTY(BlueprintReadOnly, Category = "LOD")
int32 CurrentLODLevel = 0;
// 강제 LOD 설정 (컷신 등)
UFUNCTION(BlueprintCallable, Category = "LOD")
void ForceLODLevel(int32 LODLevel);
// 자동 LOD 복원
UFUNCTION(BlueprintCallable, Category = "LOD")
void ResetToAutoLOD();
protected:
void UpdateLOD();
void ApplyLODConfig(const FMetaHumanLODConfig& Config, int32 LODLevel);
bool bForcedLOD = false;
float LODUpdateInterval = 0.5f;
float TimeSinceLastUpdate = 0.0f;
};
// MetaHumanLODController.cpp (발췌)
UMetaHumanLODController::UMetaHumanLODController()
{
PrimaryComponentTick.bCanEverTick = true;
// 기본 LOD 설정 (4단계)
LODConfigs.SetNum(4);
// LOD 0: 근거리 (0~500cm) - 풀 퀄리티
LODConfigs[0].TransitionDistance = 500.0f;
LODConfigs[0].bEnableGroom = true;
LODConfigs[0].bEnableFacialAnim = true;
LODConfigs[0].bCastShadow = true;
LODConfigs[0].bEnablePhysics = true;
// LOD 1: 중거리 (500~2000cm) - Groom Cards 전환
LODConfigs[1].TransitionDistance = 2000.0f;
LODConfigs[1].bEnableGroom = true; // Cards 모드
LODConfigs[1].bEnableFacialAnim = false;
LODConfigs[1].bCastShadow = true;
LODConfigs[1].bEnablePhysics = false;
// LOD 2: 원거리 (2000~5000cm) - 헤어 메시 전환
LODConfigs[2].TransitionDistance = 5000.0f;
LODConfigs[2].bEnableGroom = false;
LODConfigs[2].bEnableFacialAnim = false;
LODConfigs[2].bCastShadow = false;
LODConfigs[2].bEnablePhysics = false;
// LOD 3: 최원거리 (5000cm+) - 최소 메시
LODConfigs[3].TransitionDistance = 100000.0f;
LODConfigs[3].bEnableGroom = false;
LODConfigs[3].bEnableFacialAnim = false;
LODConfigs[3].bCastShadow = false;
LODConfigs[3].bEnablePhysics = false;
}
void UMetaHumanLODController::UpdateLOD()
{
if (bForcedLOD) return;
// 카메라와의 거리 계산
APlayerCameraManager* CamManager =
UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0);
if (!CamManager) return;
float Distance = FVector::Dist(
GetOwner()->GetActorLocation(),
CamManager->GetCameraLocation());
// 적절한 LOD 레벨 결정
int32 NewLOD = LODConfigs.Num() - 1;
for (int32 i = 0; i < LODConfigs.Num(); ++i)
{
if (Distance < LODConfigs[i].TransitionDistance)
{
NewLOD = i;
break;
}
}
// LOD 변경 시에만 적용
if (NewLOD != CurrentLODLevel)
{
CurrentLODLevel = NewLOD;
ApplyLODConfig(LODConfigs[NewLOD], NewLOD);
}
}
void UMetaHumanLODController::ApplyLODConfig(
const FMetaHumanLODConfig& Config,
int32 LODLevel)
{
AActor* Owner = GetOwner();
if (!Owner) return;
// 스켈레탈 메시 LOD 설정
if (USkeletalMeshComponent* Mesh =
Owner->FindComponentByClass<USkeletalMeshComponent>())
{
Mesh->SetForcedLOD(LODLevel);
Mesh->SetCastShadow(Config.bCastShadow);
}
// Groom 컴포넌트 제어
TArray<UGroomComponent*> GroomComponents;
Owner->GetComponents<UGroomComponent>(GroomComponents);
for (UGroomComponent* Groom : GroomComponents)
{
Groom->SetVisibility(Config.bEnableGroom);
if (Config.bEnableGroom)
{
// Strand -> Cards 전환 (LOD 1+)
Groom->SetForcedLOD(LODLevel);
}
}
// 페이셜 애니메이터 제어
if (UMetaHumanFacialAnimator* FacialAnim =
Owner->FindComponentByClass<UMetaHumanFacialAnimator>())
{
FacialAnim->SetComponentTickEnabled(Config.bEnableFacialAnim);
}
}
- LOD 0 (근거리): Strand 헤어 + 페이셜 애니메이션 + 풀 물리 (~4ms GPU/캐릭터)
- LOD 1 (중거리): Cards 헤어 + 바디 애니메이션만 (~1.5ms GPU/캐릭터)
- LOD 2 (원거리): 메시 헤어 + 기본 애니메이션 (~0.5ms GPU/캐릭터)
- LOD 3 (최원거리): 최소 메시 + 그림자 없음 (~0.1ms GPU/캐릭터)
NPC 블루프린트에서 UMetaHumanLODController를 추가하고, 대화 시스템 진입 시 ForceLODLevel(0)을 호출하여 대화 상대 NPC를 풀 퀄리티로 전환하세요. 대화 종료 후 ResetToAutoLOD()로 자동 LOD 관리로 복귀합니다.
핵심 요약
- UE 5.6부터 MetaHuman Creator가 웹 앱에서 엔진 내부로 이전
- 로컬 처리로 더 빠른 워크플로우 가능
- UE 5.7에서 Linux, macOS 플랫폼 지원 추가
- Python/Blueprint 배치 처리 자동화 지원
- 런타임 블렌드쉐이프 기반 표정 애니메이션 제어
도전 과제
배운 내용을 직접 실습해보세요
MetaHuman Creator에서 RPG NPC 캐릭터를 생성하고, UE5 프로젝트에 임포트하세요. SkeletalMesh, Animation Blueprint, Groom(헤어) 컴포넌트가 올바르게 설정되는지 확인하세요.
MetaHuman의 페이셜 리그를 활용하여 NPC 대화 시 립싱크와 표정 애니메이션을 적용하세요. LiveLink 또는 Sequencer를 사용하여 페이셜 애니메이션을 제작하세요.
다수의 MetaHuman NPC가 동시에 화면에 나오는 RPG 마을 씬에서 성능을 최적화하세요. LOD 설정, Groom LOD, Strand-to-Cards 변환, 거리 기반 애니메이션 LOD를 적용하세요.