利用第三方后期处理材质(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++ 语言 | 加密解密函数说明 )
572 0
|
算法 C++ 计算机视觉
寻找复杂背景下物体的轮廓(OpenCV / C++ - Filling holes)
一、问题提出 这是一个来自"answerOpenCV"(http://answers.opencv.org/question/200422/opencv-c-filling-holes/)整编如下: title:OpenCV / C++ - Filling holes content: H...
2787 0
|
4天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
1天前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
1天前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
|
1天前
|
存储 程序员 C语言
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
70 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
52 13
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
54 5