├── Resources └── Icon128.png ├── Source └── Animepoy │ ├── Public │ ├── AnimepoyModule.h │ ├── AnimepoySubsystem.h │ └── Animepoy.h │ ├── Private │ ├── PostProcessKuwaharaFilter.h │ ├── PostProcessLineArt.h │ ├── PostProcessDiffusionFilter.h │ ├── AnimepoyModule.cpp │ ├── AnimepoySceneViewExtension.h │ ├── AnimepoySubsystem.cpp │ ├── Animepoy.cpp │ ├── AnimepoySceneViewExtension.cpp │ ├── PostProcessDiffusionFilter.cpp │ ├── PostProcessKuwaharaFilter.cpp │ └── PostProcessLineArt.cpp │ └── Animepoy.Build.cs ├── Animepoy.uplugin ├── LICENSE ├── .gitignore ├── README.md └── Shaders └── Private ├── PostProcessDiffusionFilter.usf ├── PostProcessKuwaharaFilter.usf └── PostProcessLineArt.usf /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pachira762/Animepoy/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/Animepoy/Public/AnimepoyModule.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FAnimepoyModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessKuwaharaFilter.h: -------------------------------------------------------------------------------- 1 | // @Custom 2 | #pragma once 3 | 4 | #include "ScreenPass.h" 5 | 6 | enum class EKuwaharaFilterTargetType 7 | { 8 | SceneColor, 9 | BaseColor, 10 | Normal, 11 | Material, 12 | }; 13 | 14 | struct FKuwaharaFilterInputs 15 | { 16 | FRDGTextureRef Target; 17 | EKuwaharaFilterTargetType TargetType; 18 | int32 FilterSize; 19 | }; 20 | 21 | void AddKuwaharaFilterPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FKuwaharaFilterInputs& Inputs); 22 | -------------------------------------------------------------------------------- /Animepoy.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Animepoy", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "Animepoy", 20 | "Type": "Runtime", 21 | "LoadingPhase": "PostConfigInit" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessLineArt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "ScreenPass.h" 5 | #include "SceneTexturesConfig.h" 6 | 7 | struct FLineArtPassInputs 8 | { 9 | TRDGUniformBufferRef SceneTextures; 10 | float DepthLineIntensity; 11 | float NormalLineIntensity; 12 | float MaterialLineIntensity; 13 | float PlanarLineIntensity; 14 | float NonLineSpecular; 15 | int32 LineWidth; 16 | FLinearColor LineColor; 17 | bool bPreview; 18 | }; 19 | 20 | void AddLineArtPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FLineArtPassInputs& Inputs); 21 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessDiffusionFilter.h: -------------------------------------------------------------------------------- 1 | // @Custom 2 | 3 | #pragma once 4 | 5 | #include "ScreenPass.h" 6 | 7 | class FSceneTextureParameters; 8 | 9 | struct FPostProcessDiffusionInputs 10 | { 11 | FScreenPassRenderTarget OverrideOutput; 12 | FScreenPassTexture SceneColor; 13 | FRDGTextureRef PreTonemapColor; 14 | float Intensity; 15 | float LuminanceMin; 16 | float LuminanceMax; 17 | bool bPreTonemapLuminance; 18 | float BlurPercentage; 19 | int32 BlendMode; 20 | bool bDebugMask; 21 | }; 22 | 23 | FScreenPassTexture AddPostProcessDiffusionPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FPostProcessDiffusionInputs& Inputs); -------------------------------------------------------------------------------- /Source/Animepoy/Private/AnimepoyModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "AnimepoyModule.h" 4 | #include "Interfaces/IPluginManager.h" 5 | 6 | #define LOCTEXT_NAMESPACE "FAnimepoyModule" 7 | 8 | void FAnimepoyModule::StartupModule() 9 | { 10 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 11 | FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("Animepoy"))->GetBaseDir(), TEXT("Shaders")); 12 | AddShaderSourceDirectoryMapping(TEXT("/AnimepoyShaders"), PluginShaderDir); 13 | } 14 | 15 | void FAnimepoyModule::ShutdownModule() 16 | { 17 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 18 | // we call this function before unloading the module. 19 | } 20 | 21 | #undef LOCTEXT_NAMESPACE 22 | 23 | IMPLEMENT_MODULE(FAnimepoyModule, Animepoy) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Pachira762 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Source/Animepoy/Public/AnimepoySubsystem.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Subsystems/WorldSubsystem.h" 7 | #include "Animepoy.h" 8 | #include "AnimepoySubsystem.generated.h" 9 | 10 | UCLASS() 11 | class ANIMEPOY_API UAnimepoySubsystem : public UWorldSubsystem 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | virtual void Initialize(FSubsystemCollectionBase& Collection)override; 17 | 18 | virtual void Deinitialize()override; 19 | 20 | public: 21 | void OnActorSpawned(AActor* Actor); 22 | 23 | void OnActorDeleted(AActor* Actor); 24 | 25 | void OnActorListChanged(); 26 | 27 | void SetAnimepoyRenderProxy(const FAnimepoyRenderProxy& NewAnimepoyRenderProxy) 28 | { 29 | FScopeLock Lock(&Mutex); 30 | AnimepoyRenderProxy = NewAnimepoyRenderProxy; 31 | } 32 | 33 | FAnimepoyRenderProxy GetAnimepoyRenderProxy() const 34 | { 35 | FScopeLock Lock(&Mutex); 36 | return AnimepoyRenderProxy; 37 | } 38 | 39 | private: 40 | TSharedPtr AnimepoySceneViewExtension; 41 | 42 | AAnimepoy* Animepoy; 43 | 44 | mutable FCriticalSection Mutex; 45 | 46 | FAnimepoyRenderProxy AnimepoyRenderProxy = {}; 47 | }; 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Animepoy 2 | 3 | **アニメっぽいエフェクト集** 4 | 5 | Unreal Engine 5 でアニメっぽいエフェクトを追加するためのプラグインです。 6 | 7 | 8 | ![Screenshot](https://github.com/Pachira762/Animepoy/assets/34003267/9360a5c0-2914-4b6c-b052-c4dad54f0652) 9 | ![Screenshot Line](https://github.com/Pachira762/Animepoy/assets/34003267/49ef3619-1c15-4f69-81f6-4a70b65d06a0) 10 | 11 | ## 機能 12 | 13 | * ラインアート 14 | * ポストプロセスベースのラインエフェクト 15 | * DoF や Fog と調和 16 | * シーンカラー / ベースカラー への描画 17 | 18 | * Kuwahara フィルター 19 | * シーンカラー、ベースカラー、ワールド法線、Metallic Specular Roughness へのフィルター 20 | * ディフュージョンフィルター 21 | 22 | ## 前提条件 23 | 24 | * HLSL Shader Model 6.5 に対応した環境 25 | 26 | ## プラグインのインストール方法 27 | 28 | [Releases](https://github.com/Pachira762/Animepoy/releases/latest) からプラグインをダウンロードし、`Animepoy`フォルダをプロジェクトの`Plugins`フォルダにコピーしてください。 29 | 30 | ## プラグインの使用方法 31 | 32 | 1. プロジェクトの設定から `デフォルトのRHI` を `DirectX 12` に、 33 | `D3D12 Targeted Shader Formats` を `SM6` に設定します。 34 | 2. コンテンツブラウザから `Plugins\Animepoy C++クラス\Animepoy\Public\AnimepoySettings` を選び、レベルに配置してください。 35 | 3. AnimepoySettings アクタの詳細からエフェクトの設定を行います。 36 | 4. ディフュージョンフィルターのブラーの半径を大きくする場合 `r.Filter.LoopMode` を `1` に設定します。 37 | 38 | ## 注意事項 39 | 40 | * KuwaharaFilter シェーダーに Wave Intrinsic を使用しています。 41 | * HLSL Shader Model 6.5 に対応していない環境では、KuwaharaFilter が正しく動作しません。 42 | 43 | ## ライセンス 44 | 45 | MIT License 46 | 47 | ## 連絡 48 | [@pachira762](https://twitter.com/pachira762) 49 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/AnimepoySceneViewExtension.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SceneViewExtension.h" 7 | #include "AnimepoySubsystem.h" 8 | 9 | class FAnimepoySceneViewExtension : public FSceneViewExtensionBase 10 | { 11 | public: 12 | FAnimepoySceneViewExtension(const FAutoRegister& AutoRegister, UAnimepoySubsystem* WorldSubsystem); 13 | 14 | virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override {} 15 | virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override; 16 | virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {} 17 | 18 | #if USE_POST_DEFERRED_LIGHTING_PASS 19 | // Called right after deferred lighting, before fog rendering. 20 | virtual void PostDeferredLighting_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView, TRDGUniformBufferRef SceneTextures) override; 21 | #endif // USE_POST_DEFERRED_LIGHTING_PASS 22 | 23 | virtual void PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& InView, const FPostProcessingInputs& Inputs) override; 24 | virtual void SubscribeToPostProcessingPass(EPostProcessingPass Pass, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled) override; 25 | 26 | private: 27 | UAnimepoySubsystem* WorldSubsystem{}; 28 | FAnimepoyRenderProxy AnimepoyRenderProxy; 29 | bool bEnable; 30 | 31 | bool ShouldProcessThisView() const; 32 | }; 33 | -------------------------------------------------------------------------------- /Source/Animepoy/Animepoy.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using System.IO; 4 | using UnrealBuildTool; 5 | 6 | public class Animepoy : ModuleRules 7 | { 8 | public Animepoy(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 11 | 12 | PublicIncludePaths.AddRange( 13 | new string[] { 14 | // ... add public include paths required here ... 15 | } 16 | ); 17 | 18 | 19 | PrivateIncludePaths.AddRange( 20 | new string[] { 21 | // ... add other private include paths required here ... 22 | Path.Combine(GetModuleDirectory("Renderer"), "Private"), 23 | } 24 | ); 25 | 26 | 27 | PublicDependencyModuleNames.AddRange( 28 | new string[] 29 | { 30 | "Core", 31 | // ... add other public dependencies that you statically link with here ... 32 | } 33 | ); 34 | 35 | 36 | PrivateDependencyModuleNames.AddRange( 37 | new string[] 38 | { 39 | "CoreUObject", 40 | "Engine", 41 | "RHI", 42 | "Renderer", 43 | "RenderCore", 44 | "Projects", 45 | "MaterialShaderQualitySettings", 46 | "ApplicationCore", 47 | } 48 | ); 49 | 50 | 51 | DynamicallyLoadedModuleNames.AddRange( 52 | new string[] 53 | { 54 | // ... add any modules that your module loads dynamically here ... 55 | } 56 | ); 57 | 58 | PrivateDefinitions.Add("USE_POST_DEFERRED_LIGHTING_PASS=0"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/AnimepoySubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "AnimepoySubsystem.h" 5 | #include "EngineUtils.h" 6 | #include "Animepoy.h" 7 | #include "AnimepoySceneViewExtension.h" 8 | 9 | void UAnimepoySubsystem::Initialize(FSubsystemCollectionBase& Collection) 10 | { 11 | Super::Initialize(Collection); 12 | 13 | AnimepoySceneViewExtension = FSceneViewExtensions::NewExtension(this); 14 | 15 | #if WITH_EDITOR 16 | if (GetWorld()->WorldType == EWorldType::Editor) 17 | { 18 | GEngine->OnLevelActorAdded().AddUObject(this, &UAnimepoySubsystem::OnActorSpawned); 19 | GEngine->OnLevelActorDeleted().AddUObject(this, &UAnimepoySubsystem::OnActorDeleted); 20 | GEngine->OnLevelActorListChanged().AddUObject(this, &UAnimepoySubsystem::OnActorListChanged); 21 | } 22 | #endif 23 | } 24 | 25 | void UAnimepoySubsystem::Deinitialize() 26 | { 27 | Super::Deinitialize(); 28 | 29 | #if WITH_EDITOR 30 | if (GetWorld()->WorldType == EWorldType::Editor) 31 | { 32 | GEngine->OnLevelActorAdded().RemoveAll(this); 33 | GEngine->OnLevelActorDeleted().RemoveAll(this); 34 | GEngine->OnLevelActorListChanged().RemoveAll(this); 35 | } 36 | #endif 37 | } 38 | 39 | void UAnimepoySubsystem::OnActorSpawned(AActor* Actor) 40 | { 41 | AAnimepoy* AsAnimepoy = Cast(Actor); 42 | if (AsAnimepoy) 43 | { 44 | Animepoy = AsAnimepoy; 45 | } 46 | } 47 | 48 | void UAnimepoySubsystem::OnActorDeleted(AActor* Actor) 49 | { 50 | if ((AActor*)Animepoy == Actor) 51 | { 52 | Animepoy = nullptr; 53 | } 54 | } 55 | 56 | void UAnimepoySubsystem::OnActorListChanged() 57 | { 58 | for (TActorIterator It(GetWorld()); It; ++It) 59 | { 60 | Animepoy = *It; 61 | break; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/Animepoy.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "Animepoy.h" 5 | #include "AnimepoySubsystem.h" 6 | 7 | // Sets default values 8 | AAnimepoy::AAnimepoy() 9 | { 10 | PrimaryActorTick.bCanEverTick = true; 11 | } 12 | 13 | // Called when the game starts or when spawned 14 | void AAnimepoy::BeginPlay() 15 | { 16 | Super::BeginPlay(); 17 | 18 | if (const UWorld* World = GetWorld()) 19 | { 20 | if (UAnimepoySubsystem* WorldSubsystem = World->GetSubsystem()) 21 | { 22 | WorldSubsystem->OnActorSpawned(this); 23 | } 24 | } 25 | } 26 | 27 | void AAnimepoy::BeginDestroy() 28 | { 29 | if (const UWorld* World = GetWorld()) 30 | { 31 | if (UAnimepoySubsystem* WorldSubsystem = World->GetSubsystem()) 32 | { 33 | WorldSubsystem->OnActorDeleted(this); 34 | } 35 | } 36 | 37 | Super::BeginDestroy(); 38 | } 39 | 40 | void AAnimepoy::Tick(float DeltaSeconds) 41 | { 42 | Super::Tick(DeltaSeconds); 43 | 44 | if (const UWorld* World = GetWorld()) 45 | { 46 | if (UAnimepoySubsystem* AnimepoySubsystem = World->GetSubsystem()) 47 | { 48 | FAnimepoyRenderProxy TempSettings; 49 | TempSettings.bEnable = !this->IsHidden(); 50 | 51 | TempSettings.bLineArt = bLineArt && LineWidth > 0 && LineColor.A != 0.f; 52 | TempSettings.LineColor = LineColor; 53 | TempSettings.LineWidth = LineWidth; 54 | TempSettings.DepthLineIntensity = DepthLineIntensity; 55 | TempSettings.NormalLineIntensity = NormalLineIntensity; 56 | TempSettings.MaterialLineIntensity = MaterialLineIntensity; 57 | TempSettings.PlanarLineIntensity = PlanarLineIntensity; 58 | TempSettings.bPreviewLine = bPreviewLine; 59 | 60 | TempSettings.bPrePostProcessKuwaharaFilter = bPrePostProcessKuwaharaFilter; 61 | TempSettings.PrePostProcessKuwaharaFilterSize = PrePostProcessKuwaharaFilterSize; 62 | 63 | TempSettings.bDiffusionFilter = bDiffusionFilter && DiffusionFilterIntensity != 0.f; 64 | TempSettings.DiffusionFilterIntensity = DiffusionFilterIntensity; 65 | TempSettings.DiffusionLuminanceMin = DiffusionLuminanceMin; 66 | TempSettings.DiffusionLuminanceMax = DiffusionLuminanceMax; 67 | TempSettings.DiffusionBlurPercentage = DiffusionBlurPercentage; 68 | TempSettings.DiffusionBlendMode = DiffusionBlendMode; 69 | TempSettings.bPreviewDiffusionMask = bPreviewDiffusionMask; 70 | 71 | AnimepoySubsystem->SetAnimepoyRenderProxy(TempSettings); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Source/Animepoy/Public/Animepoy.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "Animepoy.generated.h" 8 | 9 | UENUM(BlueprintType) 10 | enum class EAnimeDiffusionBlendMode : uint8 11 | { 12 | Lighten, 13 | Screen, 14 | Overlay, 15 | SoftLight, 16 | }; 17 | 18 | struct FAnimepoyRenderProxy 19 | { 20 | bool bEnable; 21 | 22 | // Line Art 23 | bool bLineArt; 24 | FLinearColor LineColor; 25 | int32 LineWidth; 26 | float DepthLineIntensity; 27 | float NormalLineIntensity; 28 | float MaterialLineIntensity; 29 | float PlanarLineIntensity; 30 | bool bPreviewLine; 31 | 32 | // Kuwahara Filter 33 | bool bPrePostProcessKuwaharaFilter; 34 | int32 PrePostProcessKuwaharaFilterSize; 35 | 36 | // Diffusion Filter 37 | bool bDiffusionFilter; 38 | float DiffusionFilterIntensity; 39 | float DiffusionLuminanceMin; 40 | float DiffusionLuminanceMax; 41 | float DiffusionBlurPercentage; 42 | EAnimeDiffusionBlendMode DiffusionBlendMode; 43 | bool bPreviewDiffusionMask; 44 | }; 45 | 46 | UCLASS() 47 | class ANIMEPOY_API AAnimepoy : public AActor 48 | { 49 | GENERATED_BODY() 50 | 51 | public: 52 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art") 53 | bool bLineArt = false; 54 | 55 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art") 56 | FLinearColor LineColor = FLinearColor(0.f, 0.f, 0.f, 1.f); 57 | 58 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art", meta = (ClampMin = "1", ClampMax = "7")) 59 | int32 LineWidth = 1; 60 | 61 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art", meta = (ClampMin = "0.0", ClampMax = "1.0")) 62 | float DepthLineIntensity = 0.9f; 63 | 64 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art", meta = (ClampMin = "0.0", ClampMax = "1.0")) 65 | float NormalLineIntensity = 0.75f; 66 | 67 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art", meta = (ClampMin = "0.0", ClampMax = "1.0")) 68 | float PlanarLineIntensity = 0.0f; 69 | 70 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art", meta = (ClampMin = "0.0", ClampMax = "1.0")) 71 | float MaterialLineIntensity = 0.75f; 72 | 73 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Line Art") 74 | bool bPreviewLine = false; 75 | 76 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Kuwahara Filter") 77 | bool bPrePostProcessKuwaharaFilter = false; 78 | 79 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Kuwahara Filter", meta = (ClampMin = "1", ClampMax = "7")) 80 | int32 PrePostProcessKuwaharaFilterSize = 1; 81 | 82 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter") 83 | bool bDiffusionFilter = false; 84 | 85 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter", meta = (ClampMin = "0.0", ClampMax = "1.0")) 86 | float DiffusionFilterIntensity = 0.5f; 87 | 88 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter", meta = (ClampMin = "0.0", ClampMax = "1.0")) 89 | float DiffusionLuminanceMin = 0.f; 90 | 91 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter", meta = (ClampMin = "0.0", ClampMax = "1.0")) 92 | float DiffusionLuminanceMax = 1.f; 93 | 94 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter", meta = (ClampMin = "0.0", ClampMax = "100.0")) 95 | float DiffusionBlurPercentage = 8.f; 96 | 97 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter") 98 | EAnimeDiffusionBlendMode DiffusionBlendMode = EAnimeDiffusionBlendMode::Overlay; 99 | 100 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Diffusion Filter") 101 | bool bPreviewDiffusionMask = false; 102 | 103 | public: 104 | AAnimepoy(); 105 | 106 | protected: 107 | virtual void BeginPlay() override; 108 | 109 | virtual void BeginDestroy() override; 110 | 111 | virtual void Tick(float DeltaSeconds); 112 | 113 | virtual bool ShouldTickIfViewportsOnly() const 114 | { 115 | return true; 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /Shaders/Private/PostProcessDiffusionFilter.usf: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | PostProcessDiffusion.usf: PostProcess Diffusion Filter 3 | =============================================================================*/ 4 | 5 | #include "/Engine/Private/Common.ush" 6 | #include "/Engine/Private/ScreenPass.ush" 7 | #include "/Engine/Private/PostProcessCommon.ush" 8 | 9 | SCREEN_PASS_TEXTURE_VIEWPORT(Input) 10 | SCREEN_PASS_TEXTURE_VIEWPORT(PreTonemap) 11 | SCREEN_PASS_TEXTURE_VIEWPORT(Output) 12 | 13 | Texture2D SceneColorTexture; 14 | Texture2D PreTonemapColorTexture; 15 | Texture2D BlurredColorTexture; 16 | 17 | // 18 | // Generate Mask 19 | // 20 | 21 | float LuminanceMin; 22 | float InvLuminanceWidth; 23 | RWTexture2D OutMaskTexture; 24 | 25 | float3 SampleSceneColor(int2 PixelPos) 26 | { 27 | int2 SrcPos = DOWNSAMPLE_FACTOR * PixelPos + Input_ViewportMin; 28 | float3 Color = (float3) 0; 29 | 30 | UNROLL 31 | for(int y = 0; y < DOWNSAMPLE_FACTOR; ++y) 32 | { 33 | UNROLL 34 | for(int x = 0; x < DOWNSAMPLE_FACTOR; ++x) 35 | { 36 | Color += SceneColorTexture[SrcPos + int2(x, y)].rgb; 37 | } 38 | } 39 | 40 | return Color / (DOWNSAMPLE_FACTOR * DOWNSAMPLE_FACTOR); 41 | } 42 | 43 | float3 SamplePreTonemapColor(int2 PixelPos) 44 | { 45 | int2 SrcPos = PreTonemap_ViewportSize * (PixelPos * Output_ExtentInverse) + PreTonemap_ViewportMin; 46 | float3 Color = (float3) 0; 47 | 48 | UNROLL 49 | for(int y = 0; y < DOWNSAMPLE_FACTOR; ++y) 50 | { 51 | UNROLL 52 | for(int x = 0; x < DOWNSAMPLE_FACTOR; ++x) 53 | { 54 | Color += PreTonemapColorTexture[SrcPos + int2(x, y)].rgb; 55 | } 56 | } 57 | 58 | return Color / (DOWNSAMPLE_FACTOR * DOWNSAMPLE_FACTOR); 59 | } 60 | 61 | [numthreads(8,8,1)] 62 | void GenerateMaskCS(uint2 Id: SV_DispatchThreadID) 63 | { 64 | int2 PixelPos = Id; 65 | 66 | if (all(PixelPos < Output_Extent)) 67 | { 68 | float3 SceneColor = SampleSceneColor(PixelPos); 69 | 70 | #if USE_PRE_TONEMAP_LUMINANCE 71 | float3 PreTonemapColor = SamplePreTonemapColor(PixelPos); 72 | float Alpha = saturate((Luminance(PreTonemapColor) - LuminanceMin) * InvLuminanceWidth); 73 | #else 74 | float Alpha = saturate((Luminance(SceneColor) - LuminanceMin) * InvLuminanceWidth); 75 | #endif 76 | 77 | OutMaskTexture[PixelPos] = float4(SceneColor, Alpha); 78 | } 79 | } 80 | 81 | // 82 | // Composite 83 | // 84 | 85 | float3 BlendLighten(float3 Base, float3 Blend) 86 | { 87 | return max(Base, Blend); 88 | } 89 | 90 | float3 BlendScreen(float3 Base, float3 Blend) 91 | { 92 | return float3( 93 | 1.0 - (1.0 - Base.r) * (1.0 - Blend.r), 94 | 1.0 - (1.0 - Base.g) * (1.0 - Blend.g), 95 | 1.0 - (1.0 - Base.b) * (1.0 - Blend.b) 96 | ); 97 | } 98 | 99 | float3 BlendOverlay(float3 Base, float3 Blend) 100 | { 101 | return float3( 102 | Base.r < 0.5 ? (2.0 * Base.r * Blend.r) : (1.0 - 2.0 * (1.0 - Base.r) * (1.0 - Blend.r)), 103 | Base.g < 0.5 ? (2.0 * Base.g * Blend.g) : (1.0 - 2.0 * (1.0 - Base.g) * (1.0 - Blend.g)), 104 | Base.b < 0.5 ? (2.0 * Base.b * Blend.b) : (1.0 - 2.0 * (1.0 - Base.b) * (1.0 - Blend.b)) 105 | ); 106 | } 107 | 108 | float3 BlendSoftLight(float3 Base, float3 Blend) 109 | { 110 | return float3( 111 | (1.0 - 2.0 * Blend.r) * Base.r * Base.r + 2.0 * Blend.r * Base.r, 112 | (1.0 - 2.0 * Blend.g) * Base.g * Base.g + 2.0 * Blend.g * Base.g, 113 | (1.0 - 2.0 * Blend.b) * Base.b * Base.b + 2.0 * Blend.b * Base.b 114 | ); 115 | } 116 | 117 | #define DIFFUSION_BLEND_MODE_LIGHTEN 0 118 | #define DIFFUSION_BLEND_MODE_SCREEN 1 119 | #define DIFFUSION_BLEND_MODE_OVERLAY 2 120 | #define DIFFUSION_BLEND_MODE_SOFTLIGHT 3 121 | #define DIFFUSION_BLEND_MODE_DEBUG 4 122 | 123 | float BlendAmount; 124 | 125 | void CompositePS(float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0) 126 | { 127 | const float2 ViewportUV = (SvPosition.xy - Output_ViewportMin) * Output_ViewportSizeInverse; 128 | 129 | float4 SceneColor = SceneColorTexture[Input_ViewportSize * ViewportUV + Input_ViewportMin]; 130 | float4 BlurredColor = Texture2DSampleLevel(BlurredColorTexture, GlobalBilinearClampedSampler, ViewportUV, 0); 131 | 132 | float3 BlendedColor; 133 | 134 | #if DIFFUSION_BLEND_MODE == DIFFUSION_BLEND_MODE_LIGHTEN 135 | BlendedColor = BlendLighten(SceneColor.rgb, BlurredColor.rgb); 136 | 137 | #elif DIFFUSION_BLEND_MODE == DIFFUSION_BLEND_MODE_SCREEN 138 | BlendedColor = BlendScreen(SceneColor.rgb, BlurredColor.rgb); 139 | 140 | #elif DIFFUSION_BLEND_MODE == DIFFUSION_BLEND_MODE_OVERLAY 141 | BlendedColor = BlendOverlay(SceneColor.rgb, BlurredColor.rgb); 142 | 143 | #elif DIFFUSION_BLEND_MODE == DIFFUSION_BLEND_MODE_SOFTLIGHT 144 | BlendedColor = BlendSoftLight(SceneColor.rgb, BlurredColor.rgb); 145 | 146 | #elif DIFFUSION_BLEND_MODE == DIFFUSION_BLEND_MODE_DEBUG 147 | BlendedColor = BlurredColor.rgb * BlurredColor.a; 148 | BlurredColor.a = 1.f; 149 | #endif 150 | 151 | float Alpha = BlendAmount * BlurredColor.a; 152 | OutColor = float4(lerp(SceneColor.rgb, BlendedColor.rgb, Alpha), 0); 153 | } 154 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/AnimepoySceneViewExtension.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project AnimepoyRenderProxy. 2 | 3 | 4 | #include "AnimepoySceneViewExtension.h" 5 | #include "ShaderCompilerCore.h" 6 | #include "ScreenPass.h" 7 | #include "SceneTexturesConfig.h" 8 | #include "SceneTextureParameters.h" 9 | #include "ShaderParameterUtils.h" 10 | #include "PixelShaderUtils.h" 11 | #include "PostProcess/PostProcessing.h" 12 | #include "PostProcess/PostProcessMaterialInputs.h" 13 | #include "AnimepoySubsystem.h" 14 | #include "Animepoy.h" 15 | #include "PostProcessLineArt.h" 16 | #include "PostProcessKuwaharaFilter.h" 17 | #include "PostProcessDiffusionFilter.h" 18 | 19 | FAnimepoySceneViewExtension::FAnimepoySceneViewExtension(const FAutoRegister& AutoRegister, UAnimepoySubsystem* WorldSubsystem) 20 | : FSceneViewExtensionBase(AutoRegister) 21 | , WorldSubsystem(WorldSubsystem) 22 | { 23 | } 24 | 25 | void FAnimepoySceneViewExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) 26 | { 27 | AnimepoyRenderProxy = WorldSubsystem->GetAnimepoyRenderProxy(); 28 | bEnable = InView.Family->Scene->GetWorld() == WorldSubsystem->GetWorld() && AnimepoyRenderProxy.bEnable; 29 | } 30 | 31 | #if USE_POST_DEFERRED_LIGHTING_PASS 32 | void FAnimepoySceneViewExtension::PostDeferredLighting_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView, TRDGUniformBufferRef SceneTextures) 33 | { 34 | check(InView.bIsViewInfo); 35 | auto& View = static_cast(InView); 36 | 37 | if (ShouldProcessThisView() && AnimepoyRenderProxy.bLineArt) 38 | { 39 | FLineArtPassInputs PassInputs; 40 | PassInputs.SceneTextures = SceneTextures; 41 | PassInputs.DepthLineIntensity = AnimepoyRenderProxy.DepthLineIntensity; 42 | PassInputs.NormalLineIntensity = AnimepoyRenderProxy.NormalLineIntensity; 43 | PassInputs.PlanarLineIntensity = AnimepoyRenderProxy.PlanarLineIntensity; 44 | PassInputs.MaterialLineIntensity = AnimepoyRenderProxy.MaterialLineIntensity; 45 | PassInputs.LineWidth = AnimepoyRenderProxy.LineWidth; 46 | PassInputs.LineColor = AnimepoyRenderProxy.LineColor; 47 | PassInputs.bPreview = AnimepoyRenderProxy.bPreviewLine; 48 | 49 | AddLineArtPass(GraphBuilder, View, PassInputs); 50 | } 51 | } 52 | #endif // USE_POST_DEFERRED_LIGHTING_PASS 53 | 54 | void FAnimepoySceneViewExtension::PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& InView, const FPostProcessingInputs& Inputs) 55 | { 56 | check(InView.bIsViewInfo); 57 | auto& View = static_cast(InView); 58 | 59 | if (ShouldProcessThisView() && AnimepoyRenderProxy.bPrePostProcessKuwaharaFilter) 60 | { 61 | FKuwaharaFilterInputs PassInputs; 62 | PassInputs.Target = (*Inputs.SceneTextures)->SceneColorTexture; 63 | PassInputs.TargetType = EKuwaharaFilterTargetType::SceneColor; 64 | PassInputs.FilterSize = AnimepoyRenderProxy.PrePostProcessKuwaharaFilterSize; 65 | 66 | AddKuwaharaFilterPass(GraphBuilder, View, PassInputs); 67 | } 68 | 69 | #if !USE_POST_DEFERRED_LIGHTING_PASS 70 | if (ShouldProcessThisView() && AnimepoyRenderProxy.bLineArt) 71 | { 72 | FLineArtPassInputs PassInputs; 73 | PassInputs.SceneTextures = Inputs.SceneTextures; 74 | PassInputs.DepthLineIntensity = AnimepoyRenderProxy.DepthLineIntensity; 75 | PassInputs.NormalLineIntensity = AnimepoyRenderProxy.NormalLineIntensity; 76 | PassInputs.PlanarLineIntensity = AnimepoyRenderProxy.PlanarLineIntensity; 77 | PassInputs.MaterialLineIntensity = AnimepoyRenderProxy.MaterialLineIntensity; 78 | PassInputs.LineWidth = AnimepoyRenderProxy.LineWidth; 79 | PassInputs.LineColor = AnimepoyRenderProxy.LineColor; 80 | PassInputs.bPreview = AnimepoyRenderProxy.bPreviewLine; 81 | 82 | AddLineArtPass(GraphBuilder, View, PassInputs); 83 | } 84 | #endif // !USE_POST_DEFERRED_LIGHTING_PASS 85 | } 86 | 87 | void FAnimepoySceneViewExtension::SubscribeToPostProcessingPass(EPostProcessingPass Pass, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled) 88 | { 89 | if (Pass == EPostProcessingPass::Tonemap && ShouldProcessThisView() && AnimepoyRenderProxy.bDiffusionFilter) 90 | { 91 | InOutPassCallbacks.Add(FAfterPassCallbackDelegate::CreateLambda([this](FRDGBuilder& GraphBuilder, const FSceneView& InView, const FPostProcessMaterialInputs& Inputs) ->FScreenPassTexture { 92 | check(InView.bIsViewInfo); 93 | auto& View = static_cast(InView); 94 | 95 | FPostProcessDiffusionInputs PassInputs; 96 | PassInputs.OverrideOutput = Inputs.OverrideOutput; 97 | PassInputs.SceneColor = FScreenPassTexture::CopyFromSlice(GraphBuilder, Inputs.GetInput(EPostProcessMaterialInput::SceneColor)); 98 | PassInputs.PreTonemapColor = (*Inputs.SceneTextures.SceneTextures.GetUniformBuffer())->SceneColorTexture; 99 | PassInputs.Intensity = AnimepoyRenderProxy.DiffusionFilterIntensity; 100 | PassInputs.LuminanceMin = AnimepoyRenderProxy.DiffusionLuminanceMin; 101 | PassInputs.LuminanceMax = AnimepoyRenderProxy.DiffusionLuminanceMax; 102 | PassInputs.BlurPercentage = AnimepoyRenderProxy.DiffusionBlurPercentage; 103 | PassInputs.BlendMode = (int32)AnimepoyRenderProxy.DiffusionBlendMode; 104 | PassInputs.bDebugMask = AnimepoyRenderProxy.bPreviewDiffusionMask; 105 | 106 | return AddPostProcessDiffusionPass(GraphBuilder, View, PassInputs); 107 | })); 108 | } 109 | } 110 | 111 | bool FAnimepoySceneViewExtension::ShouldProcessThisView() const 112 | { 113 | return bEnable; 114 | } 115 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessDiffusionFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "PostProcessDiffusionFilter.h" 2 | #include "PostProcess/PostProcessDownsample.h" 3 | #include "PostProcess/PostProcessWeightedSampleSum.h" 4 | #include "DataDrivenShaderPlatformInfo.h" 5 | #include "ShaderCompiler.h" 6 | #include "SceneRendering.h" 7 | #include "SceneTextureParameters.h" 8 | #include "PixelShaderUtils.h" 9 | #include "UnrealEngine.h" 10 | 11 | namespace { 12 | const int32 GTileSizeX = 8; 13 | const int32 GTileSizeY = 8; 14 | const int32 GDownsampleFactor = 4; 15 | 16 | class FGenerateMaskCS : public FGlobalShader 17 | { 18 | public: 19 | DECLARE_GLOBAL_SHADER(FGenerateMaskCS); 20 | SHADER_USE_PARAMETER_STRUCT(FGenerateMaskCS, FGlobalShader); 21 | 22 | class FPreTonemapLuminance : SHADER_PERMUTATION_BOOL("USE_PRE_TONEMAP_LUMINANCE"); 23 | using FPermutationDomain = TShaderPermutationDomain; 24 | 25 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 26 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 27 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, PreTonemap) 28 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output) 29 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture) 30 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PreTonemapColorTexture) 31 | SHADER_PARAMETER(float, LuminanceMin) 32 | SHADER_PARAMETER(float, InvLuminanceWidth) 33 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutMaskTexture) 34 | END_SHADER_PARAMETER_STRUCT() 35 | 36 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) 37 | { 38 | FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); 39 | OutEnvironment.SetDefine(TEXT("DOWNSAMPLE_FACTOR"), GDownsampleFactor); 40 | } 41 | }; 42 | 43 | IMPLEMENT_GLOBAL_SHADER(FGenerateMaskCS, "/AnimepoyShaders/Private/PostProcessDiffusionFilter.usf", "GenerateMaskCS", SF_Compute); 44 | 45 | class FCompositePS : public FGlobalShader 46 | { 47 | public: 48 | DECLARE_GLOBAL_SHADER(FCompositePS); 49 | SHADER_USE_PARAMETER_STRUCT(FCompositePS, FGlobalShader); 50 | 51 | enum class EBlendMode : uint8 52 | { 53 | Lighten, 54 | Screen, 55 | Overlay, 56 | SoftLight, 57 | Debug, 58 | MAX 59 | }; 60 | 61 | class FBlendMode : SHADER_PERMUTATION_ENUM_CLASS("DIFFUSION_BLEND_MODE", EBlendMode); 62 | using FPermutationDomain = TShaderPermutationDomain; 63 | 64 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 65 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 66 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output) 67 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture) 68 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BlurredColorTexture) 69 | SHADER_PARAMETER(float, BlendAmount) 70 | RENDER_TARGET_BINDING_SLOTS() 71 | END_SHADER_PARAMETER_STRUCT() 72 | }; 73 | 74 | IMPLEMENT_GLOBAL_SHADER(FCompositePS, "/AnimepoyShaders/Private/PostProcessDiffusionFilter.usf", "CompositePS", SF_Pixel); 75 | } 76 | 77 | FScreenPassTexture AddPostProcessDiffusionPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FPostProcessDiffusionInputs& Inputs) 78 | { 79 | RDG_EVENT_SCOPE(GraphBuilder, "AnimeDiffusionFilter"); 80 | 81 | FRDGTextureRef MaskTexture; 82 | { 83 | FIntPoint MaskTextureExtent = FIntPoint::DivideAndRoundUp(Inputs.SceneColor.ViewRect.Size(), GDownsampleFactor); 84 | 85 | FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(MaskTextureExtent, PF_B8G8R8A8, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV); 86 | MaskTexture = GraphBuilder.CreateTexture(Desc, TEXT("DiffusionMask")); 87 | 88 | FGenerateMaskCS::FPermutationDomain PermutationVector; 89 | PermutationVector.Set(Inputs.bPreTonemapLuminance); 90 | 91 | FGenerateMaskCS::FParameters* Parameters = GraphBuilder.AllocParameters(); 92 | Parameters->Input = GetScreenPassTextureViewportParameters(FScreenPassTextureViewport(Inputs.SceneColor)); 93 | Parameters->PreTonemap = GetScreenPassTextureViewportParameters(FScreenPassTextureViewport(View.ViewRect)); 94 | Parameters->Output = GetScreenPassTextureViewportParameters(FScreenPassTextureViewport(MaskTextureExtent)); 95 | Parameters->OutMaskTexture = GraphBuilder.CreateUAV(MaskTexture); 96 | Parameters->SceneColorTexture = Inputs.SceneColor.Texture; 97 | Parameters->PreTonemapColorTexture = Inputs.PreTonemapColor; 98 | Parameters->LuminanceMin = FMath::Clamp(Inputs.LuminanceMin, 0.0, 1.0); 99 | Parameters->InvLuminanceWidth = 1.f / FMath::Max(Inputs.LuminanceMax - Inputs.LuminanceMin, 0.00001f); 100 | 101 | FComputeShaderUtils::AddPass( 102 | GraphBuilder, 103 | RDG_EVENT_NAME("DiffusionGenerateMask"), 104 | TShaderMapRef(View.ShaderMap, PermutationVector), 105 | Parameters, 106 | FComputeShaderUtils::GetGroupCount(MaskTextureExtent, FIntPoint(GTileSizeX, GTileSizeY))); 107 | } 108 | 109 | FRDGTextureRef BlurredColorTexture; 110 | { 111 | FGaussianBlurInputs BlurInputs; 112 | BlurInputs.NameX = TEXT("DiffusionBlurX"); 113 | BlurInputs.NameY = TEXT("DiffusionBlurY"); 114 | BlurInputs.Filter = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, FScreenPassTexture(MaskTexture)); 115 | BlurInputs.TintColor = FLinearColor::White; 116 | BlurInputs.CrossCenterWeight = FVector2f::ZeroVector; 117 | BlurInputs.KernelSizePercent = Inputs.BlurPercentage; 118 | BlurInputs.UseMirrorAddressMode = true; 119 | 120 | BlurredColorTexture = AddGaussianBlurPass(GraphBuilder, View, BlurInputs).Texture; 121 | } 122 | 123 | FScreenPassRenderTarget Output = Inputs.OverrideOutput; 124 | { 125 | if (!Output.IsValid()) 126 | { 127 | Output = FScreenPassRenderTarget::CreateFromInput(GraphBuilder, Inputs.SceneColor, View.GetOverwriteLoadAction(), TEXT("Diffusion")); 128 | } 129 | 130 | FCompositePS::EBlendMode BlendMode = (FCompositePS::EBlendMode)FMath::Clamp(static_cast(Inputs.BlendMode), 0, (uint8)FCompositePS::EBlendMode::MAX - 1); 131 | 132 | FCompositePS::FPermutationDomain PermutationVector; 133 | PermutationVector.Set(Inputs.bDebugMask ? FCompositePS::EBlendMode::Debug : BlendMode); 134 | 135 | FCompositePS::FParameters* Parameters = GraphBuilder.AllocParameters(); 136 | Parameters->Input = GetScreenPassTextureViewportParameters(FScreenPassTextureViewport(Inputs.SceneColor)); 137 | Parameters->Output = GetScreenPassTextureViewportParameters(FScreenPassTextureViewport(Output)); 138 | Parameters->SceneColorTexture = Inputs.SceneColor.Texture; 139 | Parameters->BlurredColorTexture = BlurredColorTexture; 140 | Parameters->BlendAmount = Inputs.bDebugMask ? 1.f : FMath::Clamp(Inputs.Intensity, 0.f, 1.f); 141 | Parameters->RenderTargets[0] = Output.GetRenderTargetBinding(); 142 | 143 | FPixelShaderUtils::AddFullscreenPass( 144 | GraphBuilder, 145 | View.ShaderMap, 146 | RDG_EVENT_NAME("Diffusion Composite"), 147 | TShaderMapRef(View.ShaderMap, PermutationVector), 148 | Parameters, 149 | Output.ViewRect, 150 | TStaticBlendState::GetRHI()); 151 | } 152 | 153 | return MoveTemp(Output); 154 | } 155 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessKuwaharaFilter.cpp: -------------------------------------------------------------------------------- 1 | // @Custom 2 | #include "PostProcessKuwaharaFilter.h" 3 | #include "PostProcess/PostProcessDownsample.h" 4 | #include "PostProcess/PostProcessWeightedSampleSum.h" 5 | #include "DataDrivenShaderPlatformInfo.h" 6 | #include "ShaderCompilerCore.h" 7 | #include "SceneRendering.h" 8 | #include "SceneTextureParameters.h" 9 | #include "PixelShaderUtils.h" 10 | #include "RenderGraphUtils.h" 11 | #include "UnrealEngine.h" 12 | 13 | namespace { 14 | enum EValueType 15 | { 16 | Color, 17 | Normal, 18 | Material, 19 | MAX 20 | }; 21 | 22 | class FValueType : SHADER_PERMUTATION_ENUM_CLASS("VALUE_TYPE", EValueType); 23 | using FCommonDomain = TShaderPermutationDomain; 24 | 25 | class FKuwaharaFilterSetupCS : public FGlobalShader 26 | { 27 | DECLARE_GLOBAL_SHADER(FKuwaharaFilterSetupCS); 28 | SHADER_USE_PARAMETER_STRUCT(FKuwaharaFilterSetupCS, FGlobalShader); 29 | 30 | using FPermutationDomain = FCommonDomain; 31 | 32 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 33 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 34 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 35 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture) 36 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutSummedAreaTableTexture) 37 | END_SHADER_PARAMETER_STRUCT() 38 | 39 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 40 | { 41 | return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM6); 42 | } 43 | }; 44 | 45 | IMPLEMENT_GLOBAL_SHADER(FKuwaharaFilterSetupCS, "/AnimepoyShaders/Private/PostProcessKuwaharaFilter.usf", "KuwaharaFilterSetupCS", SF_Compute); 46 | 47 | class FKuwaharaFilterCS : public FGlobalShader 48 | { 49 | DECLARE_GLOBAL_SHADER(FKuwaharaFilterCS); 50 | SHADER_USE_PARAMETER_STRUCT(FKuwaharaFilterCS, FGlobalShader); 51 | 52 | using FPermutationDomain = FCommonDomain; 53 | 54 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 55 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 56 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SummedAreaTableTexture) 57 | SHADER_PARAMETER(int32, FilterSize) 58 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) 59 | END_SHADER_PARAMETER_STRUCT() 60 | 61 | static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& Environment) 62 | { 63 | FGlobalShader::ModifyCompilationEnvironment(Parameters, Environment); 64 | 65 | Environment.SetDefine(TEXT("USE_CACHE"), 1); 66 | } 67 | }; 68 | 69 | IMPLEMENT_GLOBAL_SHADER(FKuwaharaFilterCS, "/AnimepoyShaders/Private/PostProcessKuwaharaFilter.usf", "KuwaharaFilterCS", SF_Compute); 70 | 71 | class FKuwaharaFilterPS : public FGlobalShader 72 | { 73 | DECLARE_GLOBAL_SHADER(FKuwaharaFilterPS); 74 | SHADER_USE_PARAMETER_STRUCT(FKuwaharaFilterPS, FGlobalShader); 75 | 76 | using FPermutationDomain = FCommonDomain; 77 | 78 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 79 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 80 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SummedAreaTableTexture) 81 | SHADER_PARAMETER(int32, FilterSize) 82 | RENDER_TARGET_BINDING_SLOTS() 83 | END_SHADER_PARAMETER_STRUCT() 84 | 85 | static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& Environment) 86 | { 87 | FGlobalShader::ModifyCompilationEnvironment(Parameters, Environment); 88 | 89 | Environment.SetDefine(TEXT("USE_CACHE"), 0); 90 | } 91 | }; 92 | 93 | IMPLEMENT_GLOBAL_SHADER(FKuwaharaFilterPS, "/AnimepoyShaders/Private/PostProcessKuwaharaFilter.usf", "KuwaharaFilterPS", SF_Pixel); 94 | 95 | EPixelFormat GetSummedAreaTablePixelFormat(EKuwaharaFilterTargetType TargetType) 96 | { 97 | switch (TargetType) 98 | { 99 | case EKuwaharaFilterTargetType::SceneColor: return PF_A32B32G32R32F; 100 | case EKuwaharaFilterTargetType::BaseColor: return PF_FloatRGBA; 101 | case EKuwaharaFilterTargetType::Normal: return PF_FloatRGBA; 102 | case EKuwaharaFilterTargetType::Material: return PF_FloatRGBA; 103 | default: return PF_Unknown; 104 | } 105 | } 106 | 107 | EValueType GetValueType(EKuwaharaFilterTargetType TargetType) 108 | { 109 | switch (TargetType) 110 | { 111 | case EKuwaharaFilterTargetType::SceneColor: return EValueType::Color; 112 | case EKuwaharaFilterTargetType::BaseColor: return EValueType::Color; 113 | case EKuwaharaFilterTargetType::Normal: return EValueType::Normal; 114 | case EKuwaharaFilterTargetType::Material: return EValueType::Material; 115 | default: return EValueType::Color; 116 | } 117 | } 118 | } 119 | 120 | void AddKuwaharaFilterPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FKuwaharaFilterInputs& Inputs) 121 | { 122 | RDG_EVENT_SCOPE(GraphBuilder, "AnimeKuwaharaFilter"); 123 | 124 | FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); 125 | FScreenPassTextureViewport Viewport(View.ViewRect); 126 | 127 | FCommonDomain PermutationVector{}; 128 | PermutationVector.Set(GetValueType(Inputs.TargetType)); 129 | 130 | FRDGTextureRef SummedAreaTable{}; 131 | { 132 | FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(Inputs.Target->Desc.Extent, GetSummedAreaTablePixelFormat(Inputs.TargetType), FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV); 133 | SummedAreaTable = GraphBuilder.CreateTexture(Desc, TEXT("SummedAreaTable")); 134 | 135 | FKuwaharaFilterSetupCS::FParameters* Parameters = GraphBuilder.AllocParameters(); 136 | Parameters->View = View.ViewUniformBuffer; 137 | Parameters->Input = GetScreenPassTextureViewportParameters(Viewport); 138 | Parameters->InputTexture = Inputs.Target; 139 | Parameters->OutSummedAreaTableTexture = GraphBuilder.CreateUAV(SummedAreaTable); 140 | 141 | FComputeShaderUtils::AddPass( 142 | GraphBuilder, 143 | RDG_EVENT_NAME("KuwaharaFilterSetupCS"), 144 | TShaderMapRef(ShaderMap, PermutationVector), 145 | Parameters, 146 | FComputeShaderUtils::GetGroupCount(Viewport.Rect.Size(), FIntPoint(16, 16)) 147 | ); 148 | } 149 | 150 | bool bUAV = (int)Inputs.Target->Desc.Flags & (int)TexCreate_UAV; 151 | bool bSRGB = (int)Inputs.Target->Desc.Flags & (int)TexCreate_SRGB; 152 | bool bUseCompute = bUAV && !bSRGB; 153 | 154 | if (bUseCompute) 155 | { 156 | FKuwaharaFilterCS::FParameters* Parameters = GraphBuilder.AllocParameters(); 157 | Parameters->Input = GetScreenPassTextureViewportParameters(Viewport); 158 | Parameters->SummedAreaTableTexture = SummedAreaTable; 159 | Parameters->FilterSize = Inputs.FilterSize; 160 | Parameters->OutTexture = GraphBuilder.CreateUAV(Inputs.Target); 161 | 162 | FComputeShaderUtils::AddPass( 163 | GraphBuilder, 164 | RDG_EVENT_NAME("KuwaharaFilterCS"), 165 | TShaderMapRef(ShaderMap, PermutationVector), 166 | Parameters, 167 | FComputeShaderUtils::GetGroupCount(Viewport.Rect.Size(), FIntPoint(16, 16)) 168 | ); 169 | } 170 | else 171 | { 172 | FKuwaharaFilterPS::FParameters* Parameters = GraphBuilder.AllocParameters(); 173 | Parameters->Input = GetScreenPassTextureViewportParameters(Viewport); 174 | Parameters->SummedAreaTableTexture = SummedAreaTable; 175 | Parameters->FilterSize = Inputs.FilterSize; 176 | Parameters->RenderTargets[0] = FRenderTargetBinding(Inputs.Target, ERenderTargetLoadAction::ELoad); 177 | 178 | FPixelShaderUtils::AddFullscreenPass( 179 | GraphBuilder, 180 | ShaderMap, 181 | RDG_EVENT_NAME("KuwaharaFilterPS"), 182 | TShaderMapRef(ShaderMap, PermutationVector), 183 | Parameters, 184 | Viewport.Rect, 185 | TStaticBlendState::GetRHI()); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Source/Animepoy/Private/PostProcessLineArt.cpp: -------------------------------------------------------------------------------- 1 | #include "PostProcessLineArt.h" 2 | #include "ShaderCompiler.h" 3 | #include "SceneRendering.h" 4 | #include "SceneTextureParameters.h" 5 | #include "Substrate/Substrate.h" 6 | #include "PixelShaderUtils.h" 7 | 8 | namespace { 9 | class FDetectLineCS : public FGlobalShader 10 | { 11 | public: 12 | DECLARE_GLOBAL_SHADER(FDetectLineCS); 13 | SHADER_USE_PARAMETER_STRUCT(FDetectLineCS, FGlobalShader); 14 | 15 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 16 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 17 | SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures) 18 | SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) 19 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 20 | SHADER_PARAMETER(float, MaterialThreshold) 21 | SHADER_PARAMETER(float, DepthThreshold) 22 | SHADER_PARAMETER(float, NormalThreshold) 23 | SHADER_PARAMETER(float, PlanarThreshold) 24 | SHADER_PARAMETER(float, NonLineSpecular) 25 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutLineTexture) 26 | END_SHADER_PARAMETER_STRUCT() 27 | }; 28 | 29 | IMPLEMENT_GLOBAL_SHADER(FDetectLineCS, "/AnimepoyShaders/Private/PostProcessLineArt.usf", "DetectLineCS", SF_Compute); 30 | 31 | class FCompositeLinePS : public FGlobalShader 32 | { 33 | public: 34 | DECLARE_GLOBAL_SHADER(FCompositeLinePS); 35 | SHADER_USE_PARAMETER_STRUCT(FCompositeLinePS, FGlobalShader); 36 | 37 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 38 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 39 | SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input) 40 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LineTexture) 41 | SHADER_PARAMETER(FVector4f, LineColor) 42 | SHADER_PARAMETER(int, LineWidth) 43 | SHADER_PARAMETER(int, SearchRangeMin) 44 | SHADER_PARAMETER(int, SearchRangeMax) 45 | RENDER_TARGET_BINDING_SLOTS() 46 | END_SHADER_PARAMETER_STRUCT() 47 | }; 48 | 49 | IMPLEMENT_GLOBAL_SHADER(FCompositeLinePS, "/AnimepoyShaders/Private/PostProcessLineArt.usf", "CompositeLinePS", SF_Pixel); 50 | 51 | class FClearSceneColorAndGBufferPS : public FGlobalShader 52 | { 53 | public: 54 | DECLARE_GLOBAL_SHADER(FClearSceneColorAndGBufferPS); 55 | SHADER_USE_PARAMETER_STRUCT(FClearSceneColorAndGBufferPS, FGlobalShader); 56 | 57 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 58 | RENDER_TARGET_BINDING_SLOTS() 59 | END_SHADER_PARAMETER_STRUCT() 60 | }; 61 | 62 | IMPLEMENT_GLOBAL_SHADER(FClearSceneColorAndGBufferPS, "/AnimepoyShaders/Private/PostProcessLineArt.usf", "ClearSceneColorAndGBufferPS", SF_Pixel); 63 | } 64 | 65 | namespace 66 | { 67 | static TAutoConsoleVariable CVarSubstrate( 68 | TEXT("r.Substrate"), 69 | 0, 70 | TEXT("Enable Substrate materials (Beta)."), 71 | ECVF_ReadOnly | ECVF_RenderThreadSafe); 72 | 73 | bool IsSubstrateEnabled() 74 | { 75 | return CVarSubstrate.GetValueOnAnyThread() > 0; 76 | } 77 | 78 | TRDGUniformBufferRef BindSubstrateGlobalUniformParameters(const FViewInfo& View) 79 | { 80 | check(View.SubstrateViewData.SubstrateGlobalUniformParameters != nullptr || !IsSubstrateEnabled()); 81 | return View.SubstrateViewData.SubstrateGlobalUniformParameters; 82 | } 83 | } 84 | 85 | void AddLineArtPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FLineArtPassInputs& Inputs) 86 | { 87 | FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); 88 | FScreenPassTextureViewport Viewport = FScreenPassTextureViewport(View.ViewRect); 89 | 90 | FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, View.ViewRect); 91 | FScreenPassTexture SceneDepth((*Inputs.SceneTextures)->SceneDepthTexture, View.ViewRect); 92 | 93 | FRDGTextureRef LineTexture{}; 94 | { 95 | RDG_EVENT_SCOPE(GraphBuilder, "PostProcessLineDetection"); 96 | 97 | FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(Viewport.Extent, PF_R32_UINT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV); 98 | LineTexture = GraphBuilder.CreateTexture(Desc, TEXT("LineTexture")); 99 | 100 | FRDGTextureUAVRef LineTextureUAV = GraphBuilder.CreateUAV(LineTexture); 101 | AddClearUAVPass(GraphBuilder, LineTextureUAV, (uint32)0); 102 | 103 | FDetectLineCS::FParameters* Parameters = GraphBuilder.AllocParameters(); 104 | Parameters->View = View.ViewUniformBuffer; 105 | Parameters->SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); 106 | Parameters->Substrate = BindSubstrateGlobalUniformParameters(View); 107 | Parameters->Input = GetScreenPassTextureViewportParameters(Viewport); 108 | Parameters->MaterialThreshold = FMath::Lerp(2.f, 0.f, Inputs.MaterialLineIntensity); 109 | Parameters->DepthThreshold = FMath::Lerp(Inputs.DepthLineIntensity == 0.f ? 1.f : 0.01f, 0.f, Inputs.DepthLineIntensity); 110 | Parameters->NormalThreshold = FMath::Lerp(-1.f, 1.f, Inputs.NormalLineIntensity); 111 | Parameters->PlanarThreshold = FMath::Lerp(1.f, 0.f, Inputs.PlanarLineIntensity); 112 | Parameters->NonLineSpecular = static_cast(255.f * Inputs.NonLineSpecular) / 255.f; 113 | Parameters->OutLineTexture = LineTextureUAV; 114 | 115 | TShaderMapRef ComputeShader(ShaderMap); 116 | FComputeShaderUtils::AddPass( 117 | GraphBuilder, 118 | RDG_EVENT_NAME("DetectLineCS"), 119 | ComputeShader, 120 | Parameters, 121 | FComputeShaderUtils::GetGroupCount(Viewport.Rect.Size(), FIntPoint(8, 8))); 122 | } 123 | 124 | if (Inputs.bPreview) 125 | { 126 | RDG_EVENT_SCOPE(GraphBuilder, "PostProcessLineComposite Preview"); 127 | 128 | FClearSceneColorAndGBufferPS::FParameters* Parameters = GraphBuilder.AllocParameters(); 129 | Parameters->RenderTargets[0] = FRenderTargetBinding(SceneColor.Texture, ERenderTargetLoadAction::ENoAction); 130 | 131 | FRHIBlendState* BlendState = TStaticBlendState< 132 | CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero, 133 | CW_ALPHA 134 | >::GetRHI(); 135 | 136 | FPixelShaderUtils::AddFullscreenPass( 137 | GraphBuilder, 138 | ShaderMap, 139 | RDG_EVENT_NAME("ClearSceneColorPS"), 140 | TShaderMapRef(ShaderMap), 141 | Parameters, 142 | Viewport.Rect, 143 | BlendState); 144 | } 145 | 146 | { 147 | RDG_EVENT_SCOPE(GraphBuilder, "PostProcessLineComposite"); 148 | 149 | FCompositeLinePS::FParameters* Parameters = GraphBuilder.AllocParameters(); 150 | Parameters->View = View.ViewUniformBuffer; 151 | Parameters->Input = GetScreenPassTextureViewportParameters(Viewport); 152 | Parameters->LineTexture = LineTexture; 153 | Parameters->LineColor = Inputs.LineColor; 154 | Parameters->LineWidth = Inputs.LineWidth * Inputs.LineWidth; 155 | Parameters->SearchRangeMin = -(Inputs.LineWidth / 2); 156 | Parameters->SearchRangeMax = (Inputs.LineWidth - 1) / 2; 157 | Parameters->RenderTargets[0] = FRenderTargetBinding(SceneColor.Texture, ERenderTargetLoadAction::ELoad); 158 | Parameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneDepth.Texture, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilNop); 159 | 160 | FRHIBlendState* BlendState = TStaticBlendState::GetRHI(); 161 | FRHIDepthStencilState* DepthStencilState = TStaticDepthStencilState::GetRHI(); 162 | 163 | FPixelShaderUtils::AddFullscreenPass( 164 | GraphBuilder, 165 | ShaderMap, 166 | RDG_EVENT_NAME("CompositeLinePS"), 167 | TShaderMapRef(ShaderMap), 168 | Parameters, 169 | Viewport.Rect, 170 | BlendState, 171 | nullptr, 172 | DepthStencilState); 173 | } 174 | } -------------------------------------------------------------------------------- /Shaders/Private/PostProcessKuwaharaFilter.usf: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | PostProcessLine.usf: PostProcessing Line 3 | =============================================================================*/ 4 | 5 | #include "/Engine/Private/Common.ush" 6 | #include "/Engine/Private/ScreenPass.ush" 7 | #include "/Engine/Private/DeferredShadingCommon.ush" 8 | #include "/Engine/Private/PostProcessCommon.ush" 9 | #include "/Engine/Private/ColorSpace.ush" 10 | 11 | #define VALUE_TYPE_COLOR 0 12 | #define VALUE_TYPE_NORMAL 1 13 | #define VALUE_TYPE_MATERIAL 2 14 | 15 | SCREEN_PASS_TEXTURE_VIEWPORT(Input) 16 | 17 | int FilterSize; 18 | 19 | // 20 | // Common 21 | // 22 | 23 | float Luminance2(float3 Color) 24 | { 25 | return Pow2(Luminance(Color)); 26 | } 27 | 28 | float Orientation2(float3 Normal) 29 | { 30 | return Pow2(0.5 * dot(Normal, View.ViewForward) + 0.5); 31 | } 32 | 33 | float MaterialValue2(float3 Material) 34 | { 35 | return Pow2(Material.r + Material.g + Material.b); 36 | } 37 | 38 | // 39 | // Summed Area Table 40 | // 41 | 42 | groupshared float4 SummedAreaTable[16][16]; 43 | 44 | float4 CreateSummedAreaTable(float4 Value, int2 ThreadId) 45 | { 46 | const uint4 Mask = WaveMatch(ThreadId.y); 47 | 48 | Value += WaveMultiPrefixSum(Value, Mask); // Sum horizontally 49 | SummedAreaTable[ThreadId.y][ThreadId.x] = Value; 50 | 51 | GroupMemoryBarrierWithGroupSync(); 52 | 53 | Value = SummedAreaTable[ThreadId.x][ThreadId.y]; // Load column major 54 | Value += WaveMultiPrefixSum(Value, Mask); // Sum vertically 55 | 56 | return Value; 57 | } 58 | 59 | Texture2D SummedAreaTableTexture; 60 | 61 | groupshared float4 CachedSummedAreaTable[32][32]; 62 | 63 | void LoadSummedAreaTable(int2 PixelOffset, int2 ThreadId) 64 | { 65 | UNROLL 66 | for (int y = 0; y < 2; ++y) 67 | { 68 | UNROLL 69 | for (int x = 0; x < 2; ++x) 70 | { 71 | int2 CachePos = 16 * int2(x, y) + ThreadId; 72 | CachedSummedAreaTable[CachePos.y][CachePos.x] = SummedAreaTableTexture[CachePos + PixelOffset]; 73 | } 74 | } 75 | 76 | GroupMemoryBarrierWithGroupSync(); 77 | } 78 | 79 | #if USE_CACHE 80 | #define SUMMED_AREA_TABLE(P) CachedSummedAreaTable[P.y][P.x] 81 | #else 82 | #define SUMMED_AREA_TABLE(P) SummedAreaTableTexture[P] 83 | #endif 84 | 85 | float4 CalcAverageAndVariance(int4 Region, int2 Border) 86 | { 87 | Border.x = Region.z < Border.x + 16 ? Border.x : Border.x + 16; 88 | Border.y = Region.w < Border.y + 16 ? Border.y : Border.y + 16; 89 | 90 | float4 Value = (float4) 0; 91 | 92 | int2 P0 = Region.xy - 1; 93 | int2 P1 = Region.zw; 94 | Value += SUMMED_AREA_TABLE(P1); 95 | 96 | bool bOnBorderX = Region.x == Border.x; 97 | if (!bOnBorderX) 98 | { 99 | int2 Py = int2(P0.x, P1.y); 100 | Value -= SUMMED_AREA_TABLE(Py); 101 | } 102 | 103 | bool bOnBorderY = Region.y == Border.y; 104 | if (!bOnBorderY) 105 | { 106 | int2 Px = int2(P1.x, P0.y); 107 | Value -= SUMMED_AREA_TABLE(Px); 108 | } 109 | 110 | if (!bOnBorderX && !bOnBorderY) 111 | { 112 | Value += SUMMED_AREA_TABLE(P0); 113 | } 114 | 115 | bool bUnderBorderX = Region.x < Border.x; 116 | if (bUnderBorderX) 117 | { 118 | int2 Pv = int2(Border.x - 1, P1.y); 119 | Value += SUMMED_AREA_TABLE(Pv); 120 | 121 | if (!bOnBorderY) 122 | { 123 | int2 Ps = int2(Border.x - 1, P0.y); 124 | Value -= SUMMED_AREA_TABLE(Ps); 125 | } 126 | } 127 | 128 | bool bUnderBorderY = Region.y < Border.y; 129 | if (bUnderBorderY) 130 | { 131 | int2 Pu = int2(P1.x, Border.y - 1); 132 | Value += SUMMED_AREA_TABLE(Pu); 133 | 134 | if (!bOnBorderX) 135 | { 136 | int2 Pt = int2(P0.x, Border.y - 1); 137 | Value -= SUMMED_AREA_TABLE(Pt); 138 | } 139 | } 140 | 141 | if (bUnderBorderX && bUnderBorderY) 142 | { 143 | int2 Pc = Border - int2(1, 1); 144 | Value += SUMMED_AREA_TABLE(Pc); 145 | } 146 | 147 | Value /= (Region.z - Region.x + 1) * (Region.w - Region.y + 1); 148 | 149 | #if VALUE_TYPE == VALUE_TYPE_COLOR 150 | Value.a -= Luminance2(Value.rgb); 151 | #elif VALUE_TYPE == VALUE_TYPE_NORMAL 152 | Value.a -= Orientation2(Value.xyz); 153 | Value.xyz = normalize(Value.xyz); 154 | #elif VALUE_TYPE == VALUE_TYPE_MATERIAL 155 | Value.a -= MaterialValue2(Value.rgb); 156 | #endif 157 | 158 | return Value; 159 | } 160 | 161 | // 162 | // 163 | // 164 | 165 | float rand2(float2 n) { return frac(sin(dot(0.0000001f * n, float2(12.9898, 4.1414))) * 43758.5453); } 166 | 167 | Texture2D InputTexture; 168 | RWTexture2D OutSummedAreaTableTexture; 169 | 170 | [numthreads(16, 16, 1)] 171 | void KuwaharaFilterSetupCS(int2 Id : SV_DispatchThreadID, int2 ThreadId : SV_GroupThreadID) 172 | { 173 | int2 SrcPos = Input_ViewportMin + Id; 174 | float4 Value = InputTexture[SrcPos]; 175 | 176 | #if VALUE_TYPE == VALUE_TYPE_COLOR 177 | Value.a = Luminance2(Value.rgb); 178 | #elif VALUE_TYPE == VALUE_TYPE_NORMAL 179 | Value.xyz = DecodeNormal(Value.xyz); 180 | Value.a = Orientation2(Value.xyz); 181 | #elif VALUE_TYPE == VALUE_TYPE_MATERIAL 182 | Value.a = MaterialValue2(Value.rgb); 183 | #endif 184 | 185 | float4 SummedValue = CreateSummedAreaTable(Value, ThreadId); 186 | 187 | int2 DestPos = SrcPos - ThreadId + ThreadId.yx; // Store vertically 188 | if (all(DestPos < Input_ViewportMax)) 189 | { 190 | OutSummedAreaTableTexture[DestPos] = SummedValue; 191 | } 192 | } 193 | 194 | float4 KuwaharaFilter(int2 PixelPos, int2 PixelOffset, int2 ThreadId) 195 | { 196 | #if USE_CACHE 197 | int2 Borders[4] = { 198 | int2(-8, -8), 199 | int2(8, -8), 200 | int2(-8, 8), 201 | int2(8, 8), 202 | }; 203 | #else 204 | int2 Borders[4] = 205 | { 206 | PixelPos - ThreadId, 207 | PixelPos - ThreadId, 208 | PixelPos - ThreadId, 209 | PixelPos - ThreadId, 210 | }; 211 | #endif 212 | 213 | #if USE_CACHE 214 | LoadSummedAreaTable(PixelOffset, ThreadId); 215 | #endif 216 | 217 | int Cx = PixelPos.x - PixelOffset.x; 218 | int Cy = PixelPos.y - PixelOffset.y; 219 | int Left = max(PixelPos.x - FilterSize, 0) - PixelOffset.x; 220 | int Top = max(PixelPos.y - FilterSize, 0) - PixelOffset.y; 221 | int Right = min(PixelPos.x + FilterSize, Input_ViewportMax.x - 1) - PixelOffset.x; 222 | int Bottom = min(PixelPos.y + FilterSize, Input_ViewportMax.y - 1) - PixelOffset.y; 223 | 224 | int4 Regions[4] = 225 | { 226 | int4(Left, Top, Cx, Cy), 227 | int4(Cx, Top, Right, Cy), 228 | int4(Left, Cy, Cx, Bottom), 229 | int4(Cx, Cy, Right, Bottom) 230 | }; 231 | 232 | float4 ValueAndMinVariance = CalcAverageAndVariance(Regions[0], Borders[0]); 233 | 234 | UNROLL 235 | for (int i = 1; i < 4; ++i) 236 | { 237 | float4 ValueAndVariance = CalcAverageAndVariance(Regions[i], Borders[i]); 238 | if (ValueAndVariance.a < ValueAndMinVariance.a) 239 | { 240 | ValueAndMinVariance = ValueAndVariance; 241 | } 242 | } 243 | 244 | return ValueAndMinVariance; 245 | } 246 | 247 | RWTexture2D OutTexture; 248 | 249 | [numthreads(16, 16, 1)] 250 | void KuwaharaFilterCS(int2 Id : SV_DispatchThreadID, int2 ThreadId : SV_GroupThreadID) 251 | { 252 | int2 PixelPos = Input_ViewportMin + Id; 253 | int2 PixelOffset = PixelPos - ThreadId - 8; 254 | 255 | float4 ValueAndVariance = KuwaharaFilter(PixelPos, PixelOffset, ThreadId); 256 | 257 | if (all(PixelPos < Input_ViewportMax)) 258 | { 259 | #if VALUE_TYPE == VALUE_TYPE_COLOR 260 | OutTexture[PixelPos].rgb = ValueAndVariance.rgb; 261 | #elif VALUE_TYPE == VALUE_TYPE_NORMAL 262 | OutTexture[PixelPos].xyz = EncodeNormal(ValueAndVariance.xyz); 263 | #elif VALUE_TYPE == VALUE_TYPE_MATERIAL 264 | OutTexture[PixelPos].rgb = ValueAndVariance.rgb; 265 | #endif 266 | } 267 | } 268 | 269 | void KuwaharaFilterPS(float4 SvPosition : SV_POSITION, out float3 OutValue : SV_Target0) 270 | { 271 | int2 PixelPos = int2(SvPosition.xy); 272 | int2 ThreadId = (PixelPos - Input_ViewportMin) & 0x0F; 273 | 274 | float4 ValueAndVariance = KuwaharaFilter(PixelPos, 0, ThreadId); 275 | 276 | #if VALUE_TYPE == VALUE_TYPE_COLOR 277 | OutValue = ValueAndVariance.rgb; 278 | #elif VALUE_TYPE == VALUE_TYPE_NORMAL 279 | OutValue = EncodeNormal(ValueAndVariance.xyz); 280 | #elif VALUE_TYPE == VALUE_TYPE_MATERIAL 281 | OutValue = ValueAndVariance.rgb; 282 | #endif 283 | } 284 | -------------------------------------------------------------------------------- /Shaders/Private/PostProcessLineArt.usf: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | PostProcessLineArt.usf: PostProcessing Line 3 | =============================================================================*/ 4 | 5 | #include "/Engine/Private/Common.ush" 6 | #include "/Engine/Private/Substrate/Substrate.ush" 7 | #include "/Engine/Private/Substrate/SubstrateEvaluation.ush" 8 | #include "/Engine/Private/DeferredShadingCommon.ush" 9 | #include "/Engine/Private/PostProcessCommon.ush" 10 | #include "/Engine/Private/ScreenPass.ush" 11 | #include "/Engine/Private/SceneTextureParameters.ush" 12 | #include "/Engine/Private/PositionReconstructionCommon.ush" 13 | 14 | SCREEN_PASS_TEXTURE_VIEWPORT(Input) 15 | 16 | Texture2D LineTexture; 17 | 18 | float4 LineColor; 19 | float MaterialThreshold; 20 | float DepthThreshold; 21 | float NormalThreshold; 22 | float PlanarThreshold; 23 | float NonLineSpecular; 24 | int LineWidth; 25 | int SearchRangeMin; 26 | int SearchRangeMax; 27 | 28 | float Min4(float A, float B, float C, float D) 29 | { 30 | return min(min(A, B), min(C, D)); 31 | } 32 | 33 | uint EncodeLine(float DeviceZ) 34 | { 35 | return asuint(DeviceZ); 36 | } 37 | 38 | float DecodeLine(in uint Line) 39 | { 40 | return asfloat(Line); 41 | } 42 | 43 | uint CalcLineDistance2(int2 Offset) 44 | { 45 | int X = Offset.x < 0 ? 2 * Offset.x + 1 : 2 * Offset.x; 46 | int Y = Offset.y < 0 ? 2 * Offset.y + 1 : 2 * Offset.y; 47 | return X * X + Y * Y; 48 | } 49 | 50 | struct PixelData 51 | { 52 | float DeviceZ; 53 | float3 WorldPosition; 54 | float3 WorldNormal; 55 | float Fresnel; 56 | 57 | #if SUBSTRATE_ENABLED 58 | uint State; 59 | float Group; 60 | #else 61 | float Metallic; 62 | float Specular; 63 | float Roughness; 64 | uint ShadingModel; 65 | #endif // SUBSTRATE_ENABLED 66 | 67 | uint Flags; 68 | }; 69 | 70 | #define NOLINE_MASK 0x80 71 | #define NOLINEGROUP_MASK 0x40 72 | 73 | #define LINEFLAG_GET_NOLINE(Flags) Flags & NOLINE_MASK 74 | #define LINEFLAG_GET_NOLINEGROUP(Flags) Flags & NOLINEGROUP_MASK 75 | 76 | PixelData GetPixelData(int2 PixelPos) 77 | { 78 | PixelData OutPixel = (PixelData)0; 79 | 80 | float3 CameraVector; 81 | OutPixel.DeviceZ = LookupDeviceZ(PixelPos); 82 | ReconstructTranslatedWorldPositionAndCameraDirectionFromDeviceZ(PixelPos, OutPixel.DeviceZ, OutPixel.WorldPosition, CameraVector); 83 | 84 | #if SUBSTRATE_ENABLED 85 | FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel); 86 | FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture); 87 | FSubstrateBSDF BSDF = UnpackSubstrateBSDF(Substrate.MaterialTextureArray, SubstrateAddressing, SubstratePixelHeader); 88 | 89 | OutPixel.WorldNormal = SubstrateGetWorldNormal(SubstratePixelHeader, BSDF, SubstrateAddressing); 90 | OutPixel.Fresnel = 1.0 - abs(dot(OutPixel.WorldNormal, CameraVector)); 91 | 92 | OutPixel.State = SubstratePixelHeader.State; 93 | 94 | OutPixel.Group = 0.0; 95 | OutPixel.Flags = 0; 96 | if(BSDF_GETTYPE(BSDF) == SUBSTRATE_BSDF_TYPE_TOON) 97 | { 98 | float2 ToonParams = TOON_PARAMS(BSDF).xy; 99 | OutPixel.Group = ToonParams.y; 100 | OutPixel.Flags = uint(255.0 * ToonParams.x); 101 | } 102 | #else 103 | float4 GBufferB = SceneTexturesStruct.GBufferBTexture[PixelPos]; 104 | uint ShadingModelID = DecodeShadingModelId(GBufferB.a); 105 | 106 | OutPixel.Metallic = GBufferB.r; 107 | OutPixel.Specular = GBufferB.g; 108 | OutPixel.Roughness = GBufferB.b; 109 | OutPixel.ShadingModel = ShadingModelID; 110 | 111 | bool bHasNormal = ShadingModelID != SHADINGMODELID_UNLIT; 112 | if (bHasNormal) 113 | { 114 | OutPixel.WorldNormal = DecodeNormal(SceneTexturesStruct.GBufferATexture[PixelPos].xyz); 115 | OutPixel.Fresnel = 1 - abs(dot(OutPixel.WorldNormal, CameraVector)); 116 | } 117 | 118 | if (ShadingModelID == SHADINGMODELID_EYE) 119 | { 120 | OutPixel.Flags = 255.0 * GBufferB.r; // Metallic 121 | } 122 | #endif // SUBSTRATE_ENABLED 123 | 124 | return OutPixel; 125 | } 126 | 127 | bool DetectLine(PixelData Pixel0, PixelData Pixel1, out bool bShiftLine) 128 | { 129 | bool bNear = Pixel0.DeviceZ > Pixel1.DeviceZ; 130 | bShiftLine = !bNear; 131 | 132 | // Cancel line if near object has no line flag. 133 | bool bNoLine = LINEFLAG_GET_NOLINE(bNear ? Pixel0.Flags : Pixel1.Flags); 134 | bool bNoLineGroup = LINEFLAG_GET_NOLINEGROUP(Pixel0.Flags) & LINEFLAG_GET_NOLINEGROUP(Pixel1.Flags); 135 | if (bNoLine || bNoLineGroup) 136 | { 137 | return false; 138 | } 139 | 140 | #if SUBSTRATE_ENABLED 141 | if (Pixel0.State != Pixel1.State || Pixel0.Flags != Pixel1.Flags || Pixel0.Group != Pixel1.Group) 142 | { 143 | return true; 144 | } 145 | #else 146 | if (Pixel0.ShadingModel != Pixel1.ShadingModel || Pixel0.Specular !=Pixel1.Specular) 147 | { 148 | return true; 149 | } 150 | 151 | // MaterialLine 152 | float DeltaMetallic = abs(Pixel0.Metallic - Pixel1.Metallic); 153 | float DeltaRoughness = abs(Pixel0.Roughness - Pixel1.Roughness); 154 | if (DeltaMetallic + DeltaRoughness > MaterialThreshold) 155 | { 156 | return true; 157 | } 158 | #endif // SUBSTRATE_ENABLED 159 | 160 | // Depth Line 161 | float DepthFactor = lerp(1, 0.1f, bNear ? Pixel0.Fresnel : Pixel1.Fresnel); 162 | float DeltaZ = abs(Pixel0.DeviceZ - Pixel1.DeviceZ); 163 | if (DepthFactor * DeltaZ > DepthThreshold) 164 | { 165 | return true; 166 | } 167 | 168 | // Planar Line 169 | float3 V = normalize(Pixel0.WorldPosition - Pixel1.WorldPosition); 170 | float NoV = max(abs(dot(Pixel0.WorldNormal, V)), abs(dot(Pixel1.WorldNormal, V))); 171 | if (NoV > PlanarThreshold) 172 | { 173 | return true; 174 | } 175 | 176 | // Normal Line 177 | float DeltaNormal = dot(Pixel0.WorldNormal, Pixel1.WorldNormal); 178 | if (DeltaNormal < NormalThreshold) 179 | { 180 | bShiftLine = false; 181 | return true; 182 | } 183 | 184 | return false; 185 | } 186 | 187 | RWTexture2D OutLineTexture; 188 | 189 | [numthreads(8, 8, 1)] 190 | void DetectLineCS(int2 Id : SV_DispatchThreadID) 191 | { 192 | static const int2 Offsets[2] = 193 | { 194 | int2(1, 0), 195 | int2(0, 1), 196 | }; 197 | 198 | int2 PixelPos0 = Input_ViewportMin + Id; 199 | if (all(PixelPos0 < Input_ViewportMax)) 200 | { 201 | PixelData Pixel0 = GetPixelData(PixelPos0); 202 | 203 | for (int i = 0; i < 2; ++i) 204 | { 205 | int2 PixelPos1 = PixelPos0 + Offsets[i]; 206 | if (all(PixelPos1 < Input_ViewportMax)) 207 | { 208 | PixelData Pixel1 = GetPixelData(PixelPos1); 209 | 210 | bool bShiftLine = false; 211 | if (DetectLine(Pixel0, Pixel1, bShiftLine)) 212 | { 213 | int2 LinePos = bShiftLine ? PixelPos1 : PixelPos0; 214 | float DeviceZ = bShiftLine ? Pixel1.DeviceZ : Pixel0.DeviceZ; 215 | InterlockedMax(OutLineTexture[LinePos], EncodeLine(DeviceZ)); 216 | } 217 | } 218 | } 219 | } 220 | } 221 | 222 | float FindLine(int2 PixelPos, out float LineDepth) 223 | { 224 | static int2 SearchOffsets[5] = { int2(0, 0), int2(0, -1), int2(-1, 0), int2(+1, 0), int2(0, +1) }; 225 | 226 | LineDepth = 0.f; 227 | 228 | for (int y = SearchRangeMin; y <= SearchRangeMax; ++y) 229 | { 230 | for (int x = SearchRangeMin; x <= SearchRangeMax; ++x) 231 | { 232 | int2 Offset = int2(x, y); 233 | int2 LinePos = PixelPos + Offset; 234 | 235 | if (all(Input_ViewportMin <= LinePos) && all(LinePos < Input_ViewportMax)) 236 | { 237 | float Depth = DecodeLine(LineTexture[LinePos]); 238 | uint Distance = CalcLineDistance2(Offset); 239 | 240 | if (Depth > LineDepth && Distance < LineWidth) 241 | { 242 | LineDepth = Depth; 243 | } 244 | } 245 | } 246 | } 247 | 248 | return LineDepth; 249 | } 250 | 251 | void CompositeLinePS(float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0, out float OutDepth : SV_Depth) 252 | { 253 | int2 PixelPos = int2(SvPosition.xy); 254 | float Depth = FindLine(PixelPos, Depth); 255 | if (Depth == 0.0) 256 | { 257 | discard; 258 | return; 259 | } 260 | 261 | OutColor = float4(LineColor.a * LineColor.rgb, LineColor.a); 262 | OutDepth = Depth; 263 | } 264 | 265 | void ClearSceneColorAndGBufferPS(float4 SvPosition : SV_POSITION, out float4 OutSceneColor : SV_Target0) 266 | { 267 | OutSceneColor = float4(1, 1, 1, 0); 268 | } 269 | --------------------------------------------------------------------------------