BIN
Content/Shaders/CAS.flax
(Stored with Git LFS)
Normal file
BIN
Content/Shaders/CAS.flax
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -198,6 +198,10 @@ void AntiAliasingSettings::BlendWith(AntiAliasingSettings& other, float weight)
|
|||||||
BLEND_FLOAT(TAA_Sharpness);
|
BLEND_FLOAT(TAA_Sharpness);
|
||||||
BLEND_FLOAT(TAA_StationaryBlending);
|
BLEND_FLOAT(TAA_StationaryBlending);
|
||||||
BLEND_FLOAT(TAA_MotionBlending);
|
BLEND_FLOAT(TAA_MotionBlending);
|
||||||
|
BLEND_FLOAT(CAS_SharpeningAmount);
|
||||||
|
BLEND_FLOAT(CAS_EdgeSharpening);
|
||||||
|
BLEND_FLOAT(CAS_MinEdgeThreshold);
|
||||||
|
BLEND_FLOAT(CAS_OverBlurLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostFxMaterialsSettings::BlendWith(PostFxMaterialsSettings& other, float weight)
|
void PostFxMaterialsSettings::BlendWith(PostFxMaterialsSettings& other, float weight)
|
||||||
|
|||||||
@@ -1858,10 +1858,30 @@ API_ENUM(Attributes="Flags") enum class AntiAliasingSettingsOverride : int32
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
TAA_MotionBlending = 1 << 4,
|
TAA_MotionBlending = 1 << 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides <see cref="AntiAliasingSettings.CAS_SharpeningAmount"/> property.
|
||||||
|
/// </summary>
|
||||||
|
CAS_SharpeningAmount = 1 << 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides <see cref="AntiAliasingSettings.CAS_EdgeSharpening"/> property.
|
||||||
|
/// </summary>
|
||||||
|
CAS_EdgeSharpening = 1 << 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides <see cref="AntiAliasingSettings.CAS_MinEdgeThreshold"/> property.
|
||||||
|
/// </summary>
|
||||||
|
CAS_MinEdgeThreshold = 1 << 7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides <see cref="AntiAliasingSettings.CAS_OverBlurLimit"/> property.
|
||||||
|
/// </summary>
|
||||||
|
CAS_OverBlurLimit = 1 << 8,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All properties.
|
/// All properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
All = Mode | TAA_JitterSpread | TAA_Sharpness | TAA_StationaryBlending | TAA_MotionBlending,
|
All = Mode | TAA_JitterSpread | TAA_Sharpness | TAA_StationaryBlending | TAA_MotionBlending | CAS_SharpeningAmount | CAS_EdgeSharpening | CAS_MinEdgeThreshold | CAS_OverBlurLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1909,6 +1929,30 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
|
|||||||
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\"), VisibleIf(nameof(ShowTAASettings))")
|
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\"), VisibleIf(nameof(ShowTAASettings))")
|
||||||
float TAA_MotionBlending = 0.85f;
|
float TAA_MotionBlending = 0.85f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sharpening strength for the Contrast Adaptive Sharpening (CAS) pass. Ignored when using TAA that contains own contrast filter.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "Limit(0, 10f, 0.001f), EditorOrder(10), PostProcessSetting((int)AntiAliasingSettingsOverride.CAS_SharpeningAmount), EditorDisplay(null, \"CAS Sharpening Amount\"), VisibleIf(nameof(ShowTAASettings), true)")
|
||||||
|
float CAS_SharpeningAmount = 0.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The edge sharpening strength for the Contrast Adaptive Sharpening (CAS) pass. Ignored when using TAA that contains own contrast filter.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "Limit(0, 10f, 0.001f), EditorOrder(11), PostProcessSetting((int)AntiAliasingSettingsOverride.CAS_EdgeSharpening), EditorDisplay(null, \"CAS Edge Sharpening\"), VisibleIf(nameof(ShowTAASettings), true)")
|
||||||
|
float CAS_EdgeSharpening = 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum edge threshold for the Contrast Adaptive Sharpening (CAS) pass. Ignored when using TAA that contains own contrast filter.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "Limit(0, 10f, 0.001f), EditorOrder(12), PostProcessSetting((int)AntiAliasingSettingsOverride.CAS_MinEdgeThreshold), EditorDisplay(null, \"CAS Min Edge Threshold\"), VisibleIf(nameof(ShowTAASettings), true)")
|
||||||
|
float CAS_MinEdgeThreshold = 0.03f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The over-blur limit for the Contrast Adaptive Sharpening (CAS) pass. Ignored when using TAA that contains own contrast filter.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "Limit(0, 100f, 0.001f), EditorOrder(13), PostProcessSetting((int)AntiAliasingSettingsOverride.CAS_OverBlurLimit), EditorDisplay(null, \"CAS Over-blur Limit\"), VisibleIf(nameof(ShowTAASettings), true)")
|
||||||
|
float CAS_OverBlurLimit = 1.0f;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Blends the settings using given weight.
|
/// Blends the settings using given weight.
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ void FXAA::Dispose()
|
|||||||
void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
|
void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
|
||||||
{
|
{
|
||||||
auto context = GPUDevice::Instance->GetMainContext();
|
auto context = GPUDevice::Instance->GetMainContext();
|
||||||
|
context->SetRenderTarget(output);
|
||||||
if (checkIfSkipPass())
|
if (checkIfSkipPass())
|
||||||
{
|
{
|
||||||
// Resources are missing. Do not perform rendering, just copy input frame.
|
// Resources are missing. Do not perform rendering, just copy input frame.
|
||||||
context->SetRenderTarget(output);
|
|
||||||
context->Draw(input);
|
context->Draw(input);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,6 @@ void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureVie
|
|||||||
context->BindSR(0, input);
|
context->BindSR(0, input);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
context->SetRenderTarget(output);
|
|
||||||
const auto qualityLevel = Math::Clamp(static_cast<int32>(Graphics::AAQuality), 0, static_cast<int32>(Quality::MAX) - 1);
|
const auto qualityLevel = Math::Clamp(static_cast<int32>(Graphics::AAQuality), 0, static_cast<int32>(Quality::MAX) - 1);
|
||||||
context->SetState(_psFXAA.Get(qualityLevel));
|
context->SetState(_psFXAA.Get(qualityLevel));
|
||||||
context->DrawFullscreenTriangle();
|
context->DrawFullscreenTriangle();
|
||||||
|
|||||||
97
Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp
Normal file
97
Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "ContrastAdaptiveSharpeningPass.h"
|
||||||
|
#include "RenderList.h"
|
||||||
|
#include "Engine/Content/Assets/Shader.h"
|
||||||
|
#include "Engine/Content/Content.h"
|
||||||
|
#include "Engine/Graphics/GPUContext.h"
|
||||||
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
|
#include "Engine/Graphics/RenderTargetPool.h"
|
||||||
|
#include "Engine/Graphics/RenderTask.h"
|
||||||
|
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||||
|
|
||||||
|
GPU_CB_STRUCT(Data {
|
||||||
|
Float2 InputSizeInv;
|
||||||
|
Float2 Padding;
|
||||||
|
float SharpeningAmount;
|
||||||
|
float EdgeSharpening;
|
||||||
|
float MinEdgeThreshold;
|
||||||
|
float OverBlurLimit;
|
||||||
|
});
|
||||||
|
|
||||||
|
String ContrastAdaptiveSharpeningPass::ToString() const
|
||||||
|
{
|
||||||
|
return TEXT("ContrastAdaptiveSharpening");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContrastAdaptiveSharpeningPass::Dispose()
|
||||||
|
{
|
||||||
|
RendererPass::Dispose();
|
||||||
|
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psCAS);
|
||||||
|
_shader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContrastAdaptiveSharpeningPass::setupResources()
|
||||||
|
{
|
||||||
|
// Lazy-load shader
|
||||||
|
if (!_shader)
|
||||||
|
{
|
||||||
|
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/CAS"));
|
||||||
|
if (!_shader)
|
||||||
|
return false;
|
||||||
|
#if COMPILE_WITH_DEV_ENV
|
||||||
|
_shader.Get()->OnReloading.Bind<ContrastAdaptiveSharpeningPass, &ContrastAdaptiveSharpeningPass::OnShaderReloading>(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (!_shader->IsLoaded())
|
||||||
|
return true;
|
||||||
|
const auto shader = _shader->GetShader();
|
||||||
|
|
||||||
|
// Validate shader constant buffer size
|
||||||
|
if (shader->GetCB(0)->GetSize() != sizeof(Data))
|
||||||
|
{
|
||||||
|
REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipeline stage
|
||||||
|
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||||
|
psDesc.PS = shader->GetPS("PS_CAS");
|
||||||
|
_psCAS = GPUDevice::Instance->CreatePipelineState();
|
||||||
|
if (_psCAS->Init(psDesc))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContrastAdaptiveSharpeningPass::CanRender(const RenderContext& renderContext)
|
||||||
|
{
|
||||||
|
const AntiAliasingSettings& antiAliasing = renderContext.List->Settings.AntiAliasing;
|
||||||
|
return EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::AntiAliasing) &&
|
||||||
|
antiAliasing.CAS_SharpeningAmount > ZeroTolerance &&
|
||||||
|
!checkIfSkipPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContrastAdaptiveSharpeningPass::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
|
||||||
|
{
|
||||||
|
ASSERT_LOW_LAYER(CanRender(renderContext));
|
||||||
|
PROFILE_GPU_CPU("Contrast Adaptive Sharpening");
|
||||||
|
auto device = GPUDevice::Instance;
|
||||||
|
auto context = device->GetMainContext();
|
||||||
|
const AntiAliasingSettings& antiAliasing = renderContext.List->Settings.AntiAliasing;
|
||||||
|
|
||||||
|
Data data;
|
||||||
|
data.InputSizeInv = Float2::One / input->Size();
|
||||||
|
data.SharpeningAmount = antiAliasing.CAS_SharpeningAmount;
|
||||||
|
data.EdgeSharpening = antiAliasing.CAS_EdgeSharpening;
|
||||||
|
data.MinEdgeThreshold = antiAliasing.CAS_MinEdgeThreshold;
|
||||||
|
data.OverBlurLimit = antiAliasing.CAS_OverBlurLimit;
|
||||||
|
const auto cb = _shader->GetShader()->GetCB(0);
|
||||||
|
context->UpdateCB(cb, &data);
|
||||||
|
context->BindCB(0, cb);
|
||||||
|
context->BindSR(0, input);
|
||||||
|
context->SetState(_psCAS);
|
||||||
|
context->SetRenderTarget(output);
|
||||||
|
context->DrawFullscreenTriangle();
|
||||||
|
}
|
||||||
39
Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h
Normal file
39
Source/Engine/Renderer/ContrastAdaptiveSharpeningPass.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "RendererPass.h"
|
||||||
|
#include "Engine/Graphics/PostProcessSettings.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contrast Adaptive Sharpening (CAS) provides a mixed ability to sharpen and optionally scale an image. Based on AMD FidelityFX implementation.
|
||||||
|
/// </summary>
|
||||||
|
class ContrastAdaptiveSharpeningPass : public RendererPass<ContrastAdaptiveSharpeningPass>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool _loadShader = true;
|
||||||
|
AssetReference<Shader> _shader;
|
||||||
|
GPUPipelineState* _psCAS = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool CanRender(const RenderContext& renderContext);
|
||||||
|
void Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if COMPILE_WITH_DEV_ENV
|
||||||
|
void OnShaderReloading(Asset* obj)
|
||||||
|
{
|
||||||
|
_psCAS->ReleaseGPU();
|
||||||
|
invalidateResources();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
// [RendererPass]
|
||||||
|
String ToString() const override;
|
||||||
|
void Dispose() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// [RendererPass]
|
||||||
|
bool setupResources() override;
|
||||||
|
};
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "VolumetricFogPass.h"
|
#include "VolumetricFogPass.h"
|
||||||
#include "HistogramPass.h"
|
#include "HistogramPass.h"
|
||||||
#include "AtmospherePreCompute.h"
|
#include "AtmospherePreCompute.h"
|
||||||
|
#include "ContrastAdaptiveSharpeningPass.h"
|
||||||
#include "GlobalSignDistanceFieldPass.h"
|
#include "GlobalSignDistanceFieldPass.h"
|
||||||
#include "GI/GlobalSurfaceAtlasPass.h"
|
#include "GI/GlobalSurfaceAtlasPass.h"
|
||||||
#include "GI/DynamicDiffuseGlobalIllumination.h"
|
#include "GI/DynamicDiffuseGlobalIllumination.h"
|
||||||
@@ -126,16 +127,41 @@ void RendererService::Dispose()
|
|||||||
void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output, const Viewport& outputViewport)
|
void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output, const Viewport& outputViewport)
|
||||||
{
|
{
|
||||||
auto context = GPUDevice::Instance->GetMainContext();
|
auto context = GPUDevice::Instance->GetMainContext();
|
||||||
context->SetViewportAndScissors(outputViewport);
|
|
||||||
const auto aaMode = renderContext.List->Settings.AntiAliasing.Mode;
|
const auto aaMode = renderContext.List->Settings.AntiAliasing.Mode;
|
||||||
|
if (ContrastAdaptiveSharpeningPass::Instance()->CanRender(renderContext))
|
||||||
|
{
|
||||||
|
if (aaMode == AntialiasingMode::FastApproximateAntialiasing ||
|
||||||
|
aaMode == AntialiasingMode::SubpixelMorphologicalAntialiasing)
|
||||||
|
{
|
||||||
|
// AA -> CAS -> Output
|
||||||
|
auto tmpImage = RenderTargetPool::Get(input->GetDescription());
|
||||||
|
RENDER_TARGET_POOL_SET_NAME(tmpImage, "TmpImage");
|
||||||
|
context->SetViewportAndScissors((float)input->Width(), (float)input->Height());
|
||||||
if (aaMode == AntialiasingMode::FastApproximateAntialiasing)
|
if (aaMode == AntialiasingMode::FastApproximateAntialiasing)
|
||||||
|
FXAA::Instance()->Render(renderContext, input, tmpImage->View());
|
||||||
|
else
|
||||||
|
SMAA::Instance()->Render(renderContext, input, tmpImage->View());
|
||||||
|
context->ResetSR();
|
||||||
|
context->ResetRenderTarget();
|
||||||
|
context->SetViewportAndScissors(outputViewport);
|
||||||
|
ContrastAdaptiveSharpeningPass::Instance()->Render(renderContext, tmpImage, output);
|
||||||
|
RenderTargetPool::Release(tmpImage);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
|
// CAS -> Output
|
||||||
|
context->SetViewportAndScissors(outputViewport);
|
||||||
|
ContrastAdaptiveSharpeningPass::Instance()->Render(renderContext, input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// AA -> Output
|
||||||
|
context->SetViewportAndScissors(outputViewport);
|
||||||
|
if (aaMode == AntialiasingMode::FastApproximateAntialiasing)
|
||||||
FXAA::Instance()->Render(renderContext, input, output);
|
FXAA::Instance()->Render(renderContext, input, output);
|
||||||
}
|
|
||||||
else if (aaMode == AntialiasingMode::SubpixelMorphologicalAntialiasing)
|
else if (aaMode == AntialiasingMode::SubpixelMorphologicalAntialiasing)
|
||||||
{
|
|
||||||
SMAA::Instance()->Render(renderContext, input, output);
|
SMAA::Instance()->Render(renderContext, input, output);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PROFILE_GPU("Copy frame");
|
PROFILE_GPU("Copy frame");
|
||||||
@@ -143,6 +169,7 @@ void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPU
|
|||||||
context->Draw(input);
|
context->Draw(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Renderer::IsReady()
|
bool Renderer::IsReady()
|
||||||
{
|
{
|
||||||
|
|||||||
47
Source/Shaders/CAS.shader
Normal file
47
Source/Shaders/CAS.shader
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "./Flax/Common.hlsl"
|
||||||
|
|
||||||
|
META_CB_BEGIN(0, Data)
|
||||||
|
float2 InputSizeInv;
|
||||||
|
float2 Padding;
|
||||||
|
float SharpeningAmount;
|
||||||
|
float EdgeSharpening;
|
||||||
|
float MinEdgeThreshold;
|
||||||
|
float OverBlurLimit;
|
||||||
|
META_CB_END
|
||||||
|
|
||||||
|
Texture2D Input : register(t0);
|
||||||
|
|
||||||
|
// Pixel Shader for Contrast Adaptive Sharpening (CAS) filter. Based on AMD FidelityFX implementation.
|
||||||
|
META_PS(true, FEATURE_LEVEL_ES2)
|
||||||
|
float4 PS_CAS(Quad_VS2PS input) : SV_Target0
|
||||||
|
{
|
||||||
|
// Sample the color texture
|
||||||
|
float4 color = Input.SampleLevel(SamplerLinearClamp, input.TexCoord, 0);
|
||||||
|
|
||||||
|
// Sample neighboring pixels
|
||||||
|
float3 blurred = color.rgb;
|
||||||
|
float3 edges = 0.0;
|
||||||
|
for (int x = -2; x <= 2; x++)
|
||||||
|
{
|
||||||
|
for (int y = -2; y <= 2; y++)
|
||||||
|
{
|
||||||
|
float2 uv = float2(x, y) * InputSizeInv + input.TexCoord;
|
||||||
|
float3 neighbor = Input.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||||
|
blurred += neighbor;
|
||||||
|
edges += abs(neighbor - color.rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blurred /= 25.0;
|
||||||
|
edges /= 25.0;
|
||||||
|
|
||||||
|
// Sharpen based on edge detection
|
||||||
|
float edgeAmount = saturate((dot(edges, edges) - MinEdgeThreshold) / (0.001 + dot(edges, edges)));
|
||||||
|
float sharpen = (1.0 - edgeAmount) * SharpeningAmount + edgeAmount * EdgeSharpening;
|
||||||
|
float3 sharpened = color.rgb + (color.rgb - blurred) * sharpen;
|
||||||
|
|
||||||
|
// Limit sharpening to avoid over-blurring
|
||||||
|
sharpened = lerp(color.rgb, sharpened, saturate(OverBlurLimit / (OverBlurLimit + dot(abs(sharpened - color.rgb), float3(1.0, 1.0, 1.0)))));
|
||||||
|
return float4(sharpened, color.a);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user