利用第三方后期处理材质(PostProcess Material)对物体进行描边【UE4】【C++】

简介: 利用第三方后期处理材质(PostProcess Material)对物体进行描边【UE4】【C++】

效果图:

image.png

第一步,创建C++ Basic Code

image.png

第二步,定义键盘和鼠标输入的映射

image.png

第三步,修改 Rendering 中的 Custom Depth - Stencil Pass

image.png

第四步,找到GlobalPostProcessVolume [如果没有的话自行拖放一个PostProcessVolume组件]

image.png

将 unbound 勾选上

image.png

再修改 Blendables 为 PPI_OutlineColored

image.png

image.png

完整代码如下:

MyPlayer.h

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Character.h"
#include "MyPlayer.generated.h"
UCLASS()
class OUTLINECPLUSPLUS_API AMyPlayer : public ACharacter
{
  GENERATED_BODY()
public:
  // Sets default values for this character's properties
  AMyPlayer();
  void MoveForward(float val);
  void MoveRight(float val);
  void LookYaw(float val);
  void LookPitch(float val);
  void Use();
  class AInteractableActor* FindFocusedActor();
  void HandleHighlight();
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;
  // Called every frame
  virtual void Tick( float DeltaSeconds ) override;
  // Called to bind functionality to input
  virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
private:
  UPROPERTY(EditDefaultsOnly)
  float InteractionDistance = 300.f;  // 交互的范围
  class AInteractableActor* FocusedActor;
  // 用于 LineTraceSingleByChannel
  FCollisionQueryParams TraceParams;
};

MyPlayer.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "InteractableActor.h"
#include "MyPlayer.h"
// Sets default values
AMyPlayer::AMyPlayer()
{
  // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
  PrimaryActorTick.bCanEverTick = true;
  TraceParams = FCollisionQueryParams(FName(TEXT("TraceParams")), false, this);
  TraceParams.bTraceComplex = false;
  TraceParams.bTraceAsyncScene = false;
  TraceParams.bReturnPhysicalMaterial = false;
}
// Called when the game starts or when spawned
void AMyPlayer::BeginPlay()
{
  Super::BeginPlay();
}
// Called every frame
void AMyPlayer::Tick( float DeltaTime )
{
  Super::Tick( DeltaTime );
  if (Controller && Controller->IsLocalController())
  {
    HandleHighlight();
  }
}
// Called to bind functionality to input
void AMyPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
  Super::SetupPlayerInputComponent(PlayerInputComponent);
  InputComponent->BindAxis("MoveForward", this, &AMyPlayer::MoveForward);
  InputComponent->BindAxis("MoveRight", this, &AMyPlayer::MoveRight);
  InputComponent->BindAxis("LookYaw", this, &AMyPlayer::LookYaw);
  InputComponent->BindAxis("LookPitch", this, &AMyPlayer::LookPitch);
  InputComponent->BindAction("Use", IE_Pressed, this, &AMyPlayer::Use);
}
// 前后移动
void AMyPlayer::MoveForward(float val)
{
  FRotator Rotation(0, GetActorRotation().Yaw, 0);  // Roll, Yaw, Pitch
  FVector forward = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);
  AddMovementInput(forward, val);
}
// 左右移动
void AMyPlayer::MoveRight(float val)
{
  FRotator Rotation(0, GetActorRotation().Yaw, 0);  // Roll, Yaw, Pitch
  FVector right = FRotationMatrix(Rotation).GetScaledAxis(EAxis::Y);
  AddMovementInput(right, val);
}
// 左右转向
void AMyPlayer::LookYaw(float val)
{
  AddControllerYawInput(val);
}
// 上下转向
void AMyPlayer::LookPitch(float val)
{
  // 注意方向相反
  AddControllerPitchInput(val);
}
// 按 E 键与激活对象进行交互
void AMyPlayer::Use()
{
  AInteractableActor* Interactable = FindFocusedActor();
  if (Interactable)
  {
    // OnInteract_Implementation
    Interactable->OnInteract(this);
  }
}
AInteractableActor* AMyPlayer::FindFocusedActor()
{
  if (!Controller)
  {
    return nullptr;
  }
  FVector Location;
  FRotator Rotation;
  FHitResult Hit(ForceInit);
  Controller->GetPlayerViewPoint(Location, Rotation);
  FVector Start = Location;
  FVector End = Start + (Rotation.Vector() * InteractionDistance);
  // 通过 “射线拾取” 选定对象
  GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Camera, TraceParams);
  if (Hit.bBlockingHit) // 击中
  {
    // 获取当前被击中的对象的引用
    AInteractableActor* MyCastActor = Cast<AInteractableActor>(Hit.GetActor());
    if (MyCastActor)
    {
      return MyCastActor;
    }
  }
  return nullptr;
}
void AMyPlayer::HandleHighlight()
{
  AInteractableActor* NewHighlight = FindFocusedActor();
  if (NewHighlight)
  {
    // 如果当前描边和新激活的对象不是同一个
    if (FocusedActor != NewHighlight)
    {
      if (FocusedActor)
      {
        // 当前描边对象取消描边
        FocusedActor->OnEndFocus();
      }
      // 描边新激活对象
      NewHighlight->OnBeginFocus();
      FocusedActor = NewHighlight;
    }
  }
  else
  {
    if (FocusedActor)
    {
      // 取消描边
      FocusedActor->OnEndFocus();
      FocusedActor = nullptr;
    }
  }
}

InteractableActor.h

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "OutlineCPlusPlus.h"
#include "InteractableActor.generated.h"
UCLASS()
class OUTLINECPLUSPLUS_API AInteractableActor : public AActor
{
  GENERATED_BODY()
public: 
  // Sets default values for this actor's properties
  AInteractableActor();
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;
  // Called every frame
  virtual void Tick( float DeltaSeconds ) override;
  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Interaction)
  void OnInteract(AActor* Caller) ;
  virtual void OnInteract_Implementation(AActor* Caller);
  void OnBeginFocus();
  void OnEndFocus();
private:
  UPROPERTY(EditDefaultsOnly)
  uint32 bCanInteract : 1;
  TArray<UMeshComponent*> Meshes;
  UPROPERTY(EditDefaultsOnly)
  EStencilColor Color = EStencilColor::SC_Green;
};

InteractableActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "MyPlayer.h"
#include "InteractableActor.h"
// Sets default values
AInteractableActor::AInteractableActor()
{
  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
  PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AInteractableActor::BeginPlay()
{
  Super::BeginPlay();
  for (UActorComponent* Mesh : GetComponentsByClass(UMeshComponent::StaticClass()))
  {
    UMeshComponent* thisMesh = Cast<UMeshComponent>(Mesh);
    if (thisMesh)
    {
      Meshes.Push(thisMesh);
    }
  }
}
// Called every frame
void AInteractableActor::Tick( float DeltaTime )
{
  Super::Tick( DeltaTime );
}
void AInteractableActor::OnInteract_Implementation(AActor* Caller)
{
  AMyPlayer* Player = Cast<AMyPlayer>(Caller);
  if (Player)
  {
    GEngine->AddOnScreenDebugMessage(-1,   
        5.f,   
        FColor::Red,   
        FString::Printf(TEXT("Now deleting the interactable actor!  "))
    ); 
    // 销毁自己
    Destroy();
  }
}
void AInteractableActor::OnBeginFocus()
{
    if (bCanInteract)
    {
        for (UMeshComponent* Mesh : Meshes)
        {
          Mesh->SetRenderCustomDepth(true);
          Mesh->SetCustomDepthStencilValue((uint8)Color);
        }
    }
}
void AInteractableActor::OnEndFocus()
{
    if (bCanInteract)
    {
        for (UMeshComponent* Mesh : Meshes)
        {
          Mesh->SetRenderCustomDepth(false);
        }
    }
}

颜色 的 Enum

UENUM(BlueprintType)
enum class EStencilColor : uint8
{
  SC_Green = 250  UMETA(DisplayName = "Green"),
  SC_Blue = 251  UMETA(DisplayName = "Blue"),
  SC_Red = 252  UMETA(DisplayName = "Red"),
  SC_White = 253  UMETA(DisplayName = "White")
};

第三方材质下载链接

PostProcess 官方文档

目录
相关文章
【opencv3】透视变换后帧差法检测运动物体C++
【opencv3】透视变换后帧差法检测运动物体C++
|
数据安全/隐私保护 C语言 C++
【C 语言】文件操作 ( 文件加密解密 | 使用第三方 DES 加密解密库 | 头文件导入 | 兼容 C++ 语言 | 加密解密函数说明 )
【C 语言】文件操作 ( 文件加密解密 | 使用第三方 DES 加密解密库 | 头文件导入 | 兼容 C++ 语言 | 加密解密函数说明 )
536 0
|
算法 C++ 计算机视觉
寻找复杂背景下物体的轮廓(OpenCV / C++ - Filling holes)
一、问题提出 这是一个来自"answerOpenCV"(http://answers.opencv.org/question/200422/opencv-c-filling-holes/)整编如下: title:OpenCV / C++ - Filling holes content: H...
2770 0
|
14天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
19 4
|
14天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
16 4
|
13天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
14 1
|
24天前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
24天前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
25天前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
51 1
|
26天前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
17 1