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_StationaryBlending);
|
||||
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)
|
||||
|
||||
@@ -1858,10 +1858,30 @@ API_ENUM(Attributes="Flags") enum class AntiAliasingSettingsOverride : int32
|
||||
/// </summary>
|
||||
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>
|
||||
/// All properties.
|
||||
/// </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>
|
||||
@@ -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))")
|
||||
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:
|
||||
/// <summary>
|
||||
/// Blends the settings using given weight.
|
||||
|
||||
@@ -65,10 +65,10 @@ void FXAA::Dispose()
|
||||
void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
|
||||
{
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
context->SetRenderTarget(output);
|
||||
if (checkIfSkipPass())
|
||||
{
|
||||
// Resources are missing. Do not perform rendering, just copy input frame.
|
||||
context->SetRenderTarget(output);
|
||||
context->Draw(input);
|
||||
return;
|
||||
}
|
||||
@@ -83,7 +83,6 @@ void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureVie
|
||||
context->BindSR(0, input);
|
||||
|
||||
// Render
|
||||
context->SetRenderTarget(output);
|
||||
const auto qualityLevel = Math::Clamp(static_cast<int32>(Graphics::AAQuality), 0, static_cast<int32>(Quality::MAX) - 1);
|
||||
context->SetState(_psFXAA.Get(qualityLevel));
|
||||
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 "HistogramPass.h"
|
||||
#include "AtmospherePreCompute.h"
|
||||
#include "ContrastAdaptiveSharpeningPass.h"
|
||||
#include "GlobalSignDistanceFieldPass.h"
|
||||
#include "GI/GlobalSurfaceAtlasPass.h"
|
||||
#include "GI/DynamicDiffuseGlobalIllumination.h"
|
||||
@@ -126,21 +127,47 @@ void RendererService::Dispose()
|
||||
void RenderAntiAliasingPass(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output, const Viewport& outputViewport)
|
||||
{
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
context->SetViewportAndScissors(outputViewport);
|
||||
const auto aaMode = renderContext.List->Settings.AntiAliasing.Mode;
|
||||
if (aaMode == AntialiasingMode::FastApproximateAntialiasing)
|
||||
if (ContrastAdaptiveSharpeningPass::Instance()->CanRender(renderContext))
|
||||
{
|
||||
FXAA::Instance()->Render(renderContext, input, output);
|
||||
}
|
||||
else if (aaMode == AntialiasingMode::SubpixelMorphologicalAntialiasing)
|
||||
{
|
||||
SMAA::Instance()->Render(renderContext, input, output);
|
||||
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)
|
||||
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
|
||||
{
|
||||
PROFILE_GPU("Copy frame");
|
||||
context->SetRenderTarget(output);
|
||||
context->Draw(input);
|
||||
// AA -> Output
|
||||
context->SetViewportAndScissors(outputViewport);
|
||||
if (aaMode == AntialiasingMode::FastApproximateAntialiasing)
|
||||
FXAA::Instance()->Render(renderContext, input, output);
|
||||
else if (aaMode == AntialiasingMode::SubpixelMorphologicalAntialiasing)
|
||||
SMAA::Instance()->Render(renderContext, input, output);
|
||||
else
|
||||
{
|
||||
PROFILE_GPU("Copy frame");
|
||||
context->SetRenderTarget(output);
|
||||
context->Draw(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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