UDN
Search public documentation:

DevelopmentKitGemsRealTimeDeformationKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > UDK 젬 > 실시간 디포메이션
UE3 홈 > 머티리얼과 텍스처 > 실시간 디포메이션

실시간 디포메이션


문서 변경내역: James Tan 작성. 홍성진 번역.
2011년 6월 UDK 버전으로 최종 테스팅, PC DX11 전용

개요


UDK 에 DirectX 11 이 도입된 이래로 실시간 테셀레이션(GPU로 굴곡면을 자연스럽게 만드는 기능)과 디스플레이스먼트(변위, 노멀방향으로 돌출시키는) 기능이 추가되어 언리얼 렌더링 씬의 비주얼 퀄리티가 극적으로 향상되었습니다. DirectX 11 의 디스플레이스먼트는 텍스처 샘플러를 사용할 수 있기에 World Position Offset 보다 강력하며, 이것만으로도 더욱 구성진 버텍스 디스플레이먼트를 만들 수 있습니다. 이 젬에서는 게임 플레이 상호작용에 따라 디폼(변형) 가능한 표면을 만드는 법에 대해 알아보겠습니다.

이 스크린 샷에서는 링크 건을 사용한 모든 디포메이션 작업이 실시간으로 처리되고 있습니다. 링크 프로젝타일에 맞은 곳이 굽는 철제 벽을 가지고도 두 가지 다른 종류의 머티리얼이 있습니다. 바닥은 바위 디퓨즈를 사용하고, 디폼되면 슬라임 레이어를 패닝시키며 표면을 웨이브 스타일 시뮬레이션으로 변위시키기도 합니다.

DX11RealTimeDeformation.jpg

: 버텍스 디스플레이스먼트를 사용하려면 DirectX 11 이 필수지만, 데칼을 사용할 필요 없이 텍스처를 가지고 메시 표면을 칠하기만 하려는 경우엔 필요치 않습니다. 사용자가 DirectX 11 호환 카드를 갖고 있지 않은 이상 DirectX 11 은 자동으로 꺼지며, 셰이더도 자동으로 그 기능을 끕니다.

테크니컬 셋업


이 젬에서는 두 가지 기술을 동시에 복합적으로 사용하여 실시간 디포메이션을 만들어냅니다. 히트 마스크 를 사용하여 표면에 적용된 모든 디포메이션을 저장합니다. 이 컴포넌트는 자동으로 표면 근처의 월드 좌표를 UV 스페이스 좌표로 변환한 다음 렌더 타겟 속으로 원을 그려 넣습니다. 머티리얼 안에 히트 마스크를 사용하여, 보통 상태와 피해 상태로 구성된 두 가지 작업을 보간하면 됩니다. 마지막으로 스크립트 기반 상호작용으로 사용하기 위해 언리얼스크립트로 합쳐 모읍니다.

SceneCapture2DHitMaskComponent

보통은 Hit Mask KR 문서에 정의된 대로 특정 텍스처 좌표를 가지고 히트 마스크 컴포넌트를 사용하게 됩니다. 불행히도 이 텍스처 좌표 셋업은 DirectX 11 의 WorldDisplacement 노드와 호환되지 않습니다. 그러나 실험을 통해 알아낸 바로는, 하나의 면이 전체 UV 좌표 세트를 (양 축에 [0.f - 1.f]) 리퍼런스하는 경우 그냥 [0.5f - 1.f] x [0.f - 1.f] 를 사용하여 룩업 좌표를 단순히 시프트시켜 히트 마스크 렌더 타겟을 사용하는 것이 가능했습니다. 이때문에 이 젬의 내용이 평면에만 국한되지는 않습니다.

머티리얼

이 머티리얼 시스템은 히트 마스크 컴포넌트의 렌더 타겟을 마스크로 사용, 디폼된 부분과 되지 않은 부분을 나타내는 셰이더 줄기 각각을 선형보간하는 것입니다.

아래는 히트 마스크 컴포넌트의 렌더 타겟을 단순히 샘플링하기만 하는 노드입니다. 수정된 텍스처 좌표는, 보통 히트 마스크 컴포넌트를 사용할 때면 필요해지는 UnmirrorU 를 사용해야만 하는 이펙트를 오프셋시킵니다. 보간 용도로는 빨강 채널만 사용되며, 다른 채널은 유용한 정보가 없어 사용되지 않습니다.

DX11DeformMaterialLayout_HitMask.jpg

아래는 히트 마스크 컴포넌트의 렌더 타겟에 노이즈를 더해주는 노드입니다. 히트 마스크 컴포넌트는, 그 자체로는 단조롭기 서울역에 그지없는 하얀 원을 놓기만 할 뿐입니다. 노이즈에 이 하얀 원을 곱한 다음 더해주는 것으로, 결국에는 좀 더 구성진 디포메이션이 적용되는 것입니다.

DX11DeformMaterialLayout_NoiseMask.jpg

요주의 머티리얼에 적용하기 위해 머티리얼이 사용하는 디스플레이스먼트 벡터입니다. 언리얼스크립트가 이 값을 바꾸기 때문에 디폴트로 0.f, 0.f, 0.f, 1.f 로 설정됩니다. 여기에 로테이션 방향을 맞춘 디스플레이스먼트 벡터를 사용하는 것이 중요한데, 안그러면 디스플레이스먼트 방향이 엇나가기 때문입니다. 이 벡터는 언리얼스크립트에서 설정됩니다.

DX11DeformMaterialLayout_DisplacementVectors.jpg

여기서 나머지 머티리얼에는 별달리 언급할 부분이 없습니다. 디퓨즈, 스페큘러, 스페큘러 파워, 노멀 등등은 모두 디포메이션 결과를 더더욱 명확히 하기 위한 보간 목적으로, 노이즈가 낀 히트 마스크 컴포넌트 렌더 타겟을 사용해야 합니다.

DX11DeformMaterialLayout_Thumbnail.jpg

언리얼스크립트

여기서는 언리얼스크립트를 사용하여 기술을 한데 묶습니다. 월드에 이 액터가 처음 생성될 때, PostBeginPlay() 를 실행합니다. PostBeginPlay 는 C++ 에 존재하는 컨스트럭터 패러다임에 해당합니다. PostBeginPlay 에서 히트 마스크 컴포넌트가 사용할 렌더 타겟이 먼저 생성됩니다. 선택적으로, SetFadingStartTimeSinceHit 에 양수 값을 설정하면 대미지가 서서히 희미해지는 효과를 낼 수 있습니다. 즉 "표면"이 천천히 원래 상태로 돌아오는 효과를 낼 수 있으니, 유기체로 된 벽같은 것을 만들기에 좋습니다. 그 이후 CreateAndSetMaterialInstanceConstant 를 사용해서 스켈레탈 메시에 대한 머티리얼 인스턴스 불변을 만듭니다. 생성된 머티리얼 인스턴스 불변은 히트 마스크 파라미터를 앞서 만든 렌더 타겟으로 설정합니다. 마지막으로 디스플레이스먼트 벡터를 그 액터의 로테이션에 따라 설정합니다.

액터가 소멸되면 Destroyed 가 실행되고, 가비지 콜렉션을 위해 오브젝트 리퍼런스를 무로 돌립니다.

액터가 대미지를 받으면 TakeDamage 가 실행됩니다. 여기가 바로 액터를 링크 건(이나 다른 총)으로 쐈을 때 실시간 디포메이션이 일어날 수 있도록, 히트 마스크 컴포넌트를 업데이트하는 곳입니다. 여기서는 단순한 이펙트가 일어나며, 확장하여 굽이가 더 크고 작은 다른 종류의 무기 시뮬레이션을 할 수도 있습니다. 하늘만 건드리지 않으면 말이죠.

디폴트 프로퍼티는 단순히 필요한 오브젝트 전부를 만들고 액터가 블로킹되는지 콜리전 있는지를 확인합니다.

DX11DeformableMesh.uc
  class DX11DeformableMesh extends Actor
    placeable;
  
  var() const LightEnvironmentComponent LightEnvironmentComponent;
  var() const SkeletalMeshComponent SkeletalMeshComponent;
  var() const SceneCapture2DHitMaskComponent SceneCapture2DHitMaskComponent;
  var() const IntPoint HitMaskSize;
  var() const Name HitMaskMaterialParameterName;
  var() const Name MinimumDisplacementParameterName;
  var() const Name MaximumDisplacementParameterName;
  var() const float MinimumDisplacement;
  var() const float MaximumDisplacement;
  
  var TextureRenderTarget2D HitMaskRenderTarget;
  
  simulated function PostBeginPlay()
  {
    local MaterialInstanceConstant MaterialInstanceConstant;
    local LinearColor LC;
    local Vector V;
  
    Super.PostBeginPlay();
  
    HitMaskRenderTarget = class'TextureRenderTarget2D'.static.Create(HitMaskSize.X, HitMaskSize.Y, PF_G8, MakeLinearColor(0, 0, 0, 1));
    if (HitMaskRenderTarget != None)
    {
      SceneCapture2DHitMaskComponent.SetCaptureTargetTexture(HitMaskRenderTarget);
      SceneCapture2DHitMaskComponent.SetFadingStartTimeSinceHit(-1.f);
  
      if (SkeletalMeshComponent != None)
      {
        MaterialInstanceConstant = SkeletalMeshComponent.CreateAndSetMaterialInstanceConstant(0);
        if (MaterialInstanceConstant != None)
        {
          MaterialInstanceConstant.SetTextureParameterValue(HitMaskMaterialParameterName, HitMaskRenderTarget);
  
          V = Vector(Rotation);
          V *= MinimumDisplacement;
          LC.R = V.X;
          LC.G = V.Y;
          LC.B = V.Z;
          MaterialInstanceConstant.SetVectorParameterValue(MinimumDisplacementParameterName, LC);
  
          V = Vector(Rotation);
          V *= MaximumDisplacement;
          LC.R = V.X;
          LC.G = V.Y;
          LC.B = V.Z;
          MaterialInstanceConstant.SetVectorParameterValue(MaximumDisplacementParameterName, LC);
        }
      }
    }
  }
  
  simulated function Destroyed()
  {
    Super.Destroyed();
  
    SceneCapture2DHitMaskComponent.SetCaptureTargetTexture(None);
    HitMaskRenderTarget = None;
  }
  
  simulated function TakeDamage(int DamageAmount, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
  {
    Super.TakeDamage(DamageAmount, EventInstigator, HitLocation, Momentum, DamageType, HitInfo, DamageCauser);
  
    SceneCapture2DHitMaskComponent.SetCaptureParameters(HitLocation, 16.f, HitLocation, false);
  }
  
  defaultproperties
  {
    Begin Object Class=ArrowComponent Name=Arrow
      ArrowColor=(R=150,G=200,B=255)
      bTreatAsASprite=True
    End Object
    Components.Add(Arrow)
  
    Begin Object Class=DynamicLightEnvironmentComponent Name=MyDynamicLightEnvironmentComponent
    End Object
    LightEnvironmentComponent=MyDynamicLightEnvironmentComponent
    Components.Add(MyDynamicLightEnvironmentComponent)
  
    Begin Object Class=SkeletalMeshComponent Name=MySkeletalMeshComponent
      LightEnvironment=MyDynamicLightEnvironmentComponent
          bHasPhysicsAssetInstance=true
      CollideActors=true
      BlockActors=true
      BlockZeroExtent=true
      BlockNonZeroExtent=true
      BlockRigidBody=true
    End Object
    SkeletalMeshComponent=MySkeletalMeshComponent
    CollisionComponent=MySkeletalMeshComponent
    Components.Add(MySkeletalMeshComponent);
  
    Begin Object Class=SceneCapture2DHitMaskComponent Name=MySceneCapture2DHitMaskComponent
    End Object
    SceneCapture2DHitMaskComponent=MySceneCapture2DHitMaskComponent
    Components.Add(MySceneCapture2DHitMaskComponent)
  
    CollisionType=COLLIDE_BlockAll
    BlockRigidBody=true
    bCollideActors=true
    bBlockActors=true
    HitMaskSize=(X=512,Y=512)
    HitMaskMaterialParameterName="HeightMask"
  }
  

게임에서 DX11DeformableMesh 사용 방법


게임에서 DX11DeformableMesh 를 사용하는 것은 언리얼 엔진 3 의 다른 액터를 사용하는 것 만큼이나 간단합니다. 콘텐츠 브라우저, 액터 클래스 탭에서 액터를 찾습니다.

DX11Deform_ActorClasses.jpg

콘텐츠 브라우저에서 선택한 뒤, 게임 뷰포트에 우클릭하면 월드에 추가할 수 있습니다.

DX11Deform_PlaceActor.jpg

거기서 프로퍼티를 설정할 필요가 있습니다. 스켈레탈 메시, 피직스 애셋, 머티리얼 같은 스켈레탈 메시 컴포넌트 프로퍼티를 설정해 줘야 합니다.

DX11Deform_DX11DeformSkelProp.jpg

DX11Deform_DX11DeformMatProp.jpg

그 후 디포메이션 프로퍼티 일부를 조정하는 것이 좋습니다. 머티리얼 파라미터 이름이 사용중인 머티리얼 이름과 일치하는지 확인해 주시고요.

DX11Deform_DX11DeformProp.jpg

마지막으로 액터가 향하는 방향이 디폼되는 표면쪽을 가리키는지 확인해 줘야 합니다. 화살표로 방향을 올바르게 맞추는 데 도움이 됩니다. 사용되는 평면 메시로도 이 작업이 가능하긴 하지만, 이 기법의 정상 작동을 위해 꼭 평면일 필요는 없습니다. 예를 들어 모든 부분이 디폼 가능하지는 않은 메시를 만들 수도 있습니다.

DX11Deform_Orientation.jpg

결론


여러가지 기술을 혼합하면 게임을 향상시켜 다른 것보다 돋보이게 만드는 재미난 신기술을 만들어낼 수도 있습니다.

내려받기


관련 토픽