首页 > 代码库 > 利用第三方材质对物体进行描边【UE4】【C++】

利用第三方材质对物体进行描边【UE4】【C++】


效果图:

技术分享


第一步,创建C++ Basic Code

技术分享


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

技术分享


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

技术分享


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

技术分享


将 unbound 勾选上

技术分享


再修改 Blendables 为 PPI_OutlineColored

技术分享


技术分享


完整代码如下:

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 官方文档







利用第三方材质对物体进行描边【UE4】【C++】