Adding Motion Sensibility to Beautify in Unreal Engine

Kronnect
3 min readJul 14, 2020

--

Recently we submitted a version of our Beautify asset for Unreal engine. This plugin improves the quality of the image in real time and works as a post-processing effect.

Here’s a video of the setup:

The effect works out of the box and includes controls to tweak the strength and behaviour.

One aspect we may want to control is the sharpen strength from code, especially when the camera moves or rotates. Usually we want sharp images only when camera is not rotating fast contributing to a motion blur effect. Also, by reducing the sharpen intensity when camera moves fast, we can reduce any flickering caused by some bright pixels (especially in VR).

Below you can find the component source which can be added to any Actor in the scene. This component exposes two properties:

  • Beautify Material Instance: drag & drop your material instance to this field. The component will create a dynamic material instance in order to modify the sharpen intensity value dynamically.
  • Motion Sensibility: this is a value in the 0..1 range that acts as a threshold for the motion sensibility. The greater the value, the more sensitive to any camera movement/rotation.

Here’s the code:

BeautifyManager.h

#pragma once#include “CoreMinimal.h”
#include “Components/ActorComponent.h”
#include “BeautifyManager.generated.h”
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BEAUTIFY_API UBeautifyManager : public UActorComponent
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = “Motion Sensibility”, meta = (ClampMin = “0.0”, ClampMax = “1.0”, UIMin = “0.0”, UIMax = “1.0”))
float MotionSenbility = 0.5;

UPROPERTY(EditAnywhere, Category = “Motion Sensibility”)
UMaterialInterface* BeautifyMaterialInstance;

public:
// Sets default values for this component’s properties
UBeautifyManager();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

And BeautifyManager.cpp:

#include “BeautifyManager.h”
#include “Interfaces/Interface_PostProcessVolume.h”
float sharpen;
FVector prevCamPos;
FVector prevCamForward;
float currSens;
UMaterialInstanceDynamic *bMat;
// Sets default values for this component’s properties
UBeautifyManager::UBeautifyManager()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don’t need them.
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UBeautifyManager::BeginPlay()
{
Super::BeginPlay();

if (!BeautifyMaterialInstance) return;

UWorld* world = GetWorld();
int32 count = world->PostProcessVolumes.Num();

// Find global Post Processing volume
for (int32 x = 0; x < count; ++x) {
FPostProcessVolumeProperties volume = world->PostProcessVolumes[x]->GetProperties();
if (volume.bIsUnbound) {
FPostProcessSettings* settings = (FPostProcessSettings*)volume.Settings;
FWeightedBlendables blendables = settings->WeightedBlendables;

bMat = UMaterialInstanceDynamic::Create(BeautifyMaterialInstance, this);
bMat->GetScalarParameterValue(FName(“SharpenIntensity”), sharpen);
blendables.Array.Empty();
FWeightedBlendable blendable;
blendable.Object = bMat;
blendable.Weight = 1.0f;
blendables.Array.Add(blendable);

settings->WeightedBlendables = blendables;
return;
}
}
}
float AngleBetweenVectors(FVector v1, FVector v2) {
float dotProduct = FVector::DotProduct(v1, v2);
float lengthProduct = v1.Size() * v2.Size();
// Angle
float angle = FMath::Acos( dotProduct / lengthProduct ) * 360.0 / 3.1415927;
return angle;
}
// Called every frame
void UBeautifyManager::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{

Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!bMat) return;

// Motion sensibility
APlayerCameraManager *camManager = GetWorld()->GetFirstPlayerController()->PlayerCameraManager;
FVector cameraPosition = camManager->GetCameraLocation();
FVector cameraForward = camManager->GetCameraRotation().Vector();
float angleDiff = AngleBetweenVectors(prevCamForward, cameraForward) * MotionSenbility;
float posDiff = (cameraPosition — prevCamPos).SizeSquared() * 10.0 * MotionSenbility;
float diff = angleDiff + posDiff;
if (diff > 0.1) {
prevCamForward = cameraForward;
prevCamPos = cameraPosition;
if (diff > MotionSenbility)
diff = MotionSenbility;
currSens += diff;
float min = sharpen * MotionSenbility * 0.75f;
float max = sharpen * (1.0 + MotionSenbility) * 0.5f;
currSens = FMath::Clamp(currSens, min, max);
} else {
if (currSens <= 0.001f)
return;
currSens *= 0.75f;
}
float tempSharpen = FMath::Clamp(sharpen — currSens, 0.0f, sharpen);
bMat->SetScalarParameterValue(FName(“SharpenIntensity”), tempSharpen);

}

Download the sources

--

--

Kronnect

Premium #unity3d & #unreal assets. check them on http://kronnect.com. Asset developer & publisher since 2015.