利用第三方后期处理材质(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++ 语言 | 加密解密函数说明 )
571 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
|
2天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
50 13
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
52 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
40 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4
|
1月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
32 3