Initial commit for editor LUT generation.

Signed-off-by: rbarrand <rbarrand@amazon.com>
This commit is contained in:
Robin
2021-10-06 16:15:15 -07:00
committed by rbarrand
parent 4a133f1251
commit 2313f912bc
18 changed files with 4903 additions and 8 deletions
File diff suppressed because it is too large Load Diff
@@ -105,6 +105,11 @@
}
]
},
{
"Name": "LutGenerationPass",
"TemplateName": "LutGenerationTemplate",
"Enabled": true
},
{
"Name": "LookModificationTransformPass",
"TemplateName": "LookModificationTransformTemplate",
@@ -0,0 +1,199 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "LutGenerationTemplate",
"PassClass": "LutGenerationPass",
"Slots": [
{
"Name": "LutOutput",
"SlotType": "Output",
"ScopeAttachmentUsage": "RenderTarget",
"LoadStoreAction": {
"LoadAction": "DontCare"
}
}
],
"ImageAttachments": [
{
"Name": "ColorGradingLut",
"ImageDescriptor": {
"Format": "R32G32B32A32_FLOAT",
"BindFlags": [
"ShaderWrite"
]
}
}
],
"Connections": [
{
"LocalSlot": "LutOutput",
"AttachmentRef": {
"Pass": "This",
"Attachment": "ColorGradingLut"
}
}
],
"PassData": {
"$type": "FullscreenTrianglePassData",
"ShaderAsset": {
"FilePath": "Shaders/ColorGrading/LutGeneration.shader"
},
"ShaderDataMappings": {
"FloatMappings": [
{
"Name": "m_colorGradingExposure",
"Value": 0.0 // unconstrained, log2 stops
},
{
"Name": "m_colorGradingContrast",
"Value": 0.0 // -100 ... 100
},
{
"Name": "m_colorGradingHueShift",
"Value": 0.0 // 0 ... 1, can wrap
},
{
"Name": "m_colorGradingPreSaturation",
"Value": 1.0 // -100 ... 100
},
{
"Name": "m_colorFilterIntensity",
"Value": 1.0 // unconstrained, log2 stops
},
{
"Name": "m_colorFilterMultiply",
"Value": 0.0 // modulate, 0 ... 1
},
{
"Name": "m_whiteBalanceKelvin",
"Value": 6600.0 // 1000.0f ... 40000.0f kelvin
},
{
"Name": "m_whiteBalanceTint",
"Value": 0.0 // -100 ... 100
},
{
"Name": "m_splitToneBalance",
"Value": 0.0 // -1 ... 1
},
{
"Name": "m_splitToneWeight",
"Value": 0.0 // 0 ... 1
},
{
"Name": "m_colorGradingPostSaturation",
"Value": 1.0 // -100 ... 100
},
{
"Name": "m_smhShadowsStart",
"Value": 0.0 // 0 ... 1
},
{
"Name": "m_smhShadowsEnd",
"Value": 0.3 // 0 ... 1
},
{
"Name": "m_smhHighlightsStart",
"Value": 0.55 // 0 ... 1
},
{
"Name": "m_smhHighlightsEnd",
"Value": 1.0 // 0 ... 1
},
{
"Name": "m_smhWeight",
"Value": 0.0 // 0 ... 1
}
],
// The colors defined here are expected to be in linear rgb color space.
// These are converted to ACEScg color space within the HDRColorGrading.azsl shader
"ColorMappings": [
{
"Name": "m_colorFilterSwatch",
"Value": [
1.0,
0.5,
0.5,
1.0
]
},
{
"Name": "m_splitToneShadowsColor",
"Value": [
1.0,
0.1,
0.1,
1.0
]
},
{
"Name": "m_splitToneHighlightsColor",
"Value": [
0.1,
1.0,
0.1,
1.0
]
},
{
"Name": "m_smhShadowsColor",
"Value": [
1.0,
0.25,
0.25,
1.0
]
},
{
"Name": "m_smhMidtonesColor",
"Value": [
0.1,
0.1,
1.0,
1.0
]
},
{
"Name": "m_smhHighlightsColor",
"Value": [
1.0,
0.0,
1.0,
1.0
]
}
],
"Float3Mappings": [
{
"Name": "m_channelMixingRed",
"Value": [
1.0,
0.0,
0.0
]
},
{
"Name": "m_channelMixingGreen",
"Value": [
0.0,
1.0,
0.0
]
},
{
"Name": "m_channelMixingBlue",
"Value": [
0.0,
0.0,
1.0
]
}
]
}
}
}
}
}
@@ -511,7 +511,11 @@
{
"Name": "HDRColorGradingTemplate",
"Path": "Passes/HDRColorGrading.pass"
}
},
{
"Name": "LutGenerationTemplate",
"Path": "Passes/LutGeneration.pass"
}
]
}
}
@@ -59,7 +59,7 @@ UNDISCLOSED.
#pragma once
static const float HALF_MAX = 65504.0f;
#include <Atom/Features/PostProcessing/Aces.azsli>
float AcesCcToLinear(float value)
{
@@ -0,0 +1,256 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI/Math.azsli>
#include <Atom/Features/SrgSemantics.azsli>
#include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
#include <Atom/Features/ColorManagement/TransformColor.azsli>
#include <Atom/Features/PostProcessing/Aces.azsli>
#include <Atom/Features/PostProcessing/Shapers.azsli>
#include <Atom/Features/PostProcessing/AcesColorSpaceConversion.azsli>
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli>
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli>
#include <3rdParty/Features/PostProcessing/KelvinToRgb.azsli>
static const float FloatEpsilon = 1.192092896e-07; // 1.0 + FloatEpsilon != 1.0, smallest positive float
static const float FloatMin = FLOAT_32_MIN; // Min float number that is positive
static const float FloatMax = FLOAT_32_MAX; // Max float number representable
static const float AcesCcMidGrey = 0.4135884;
float3 convert2Dto3DLutCoords(float2 uv)
{
const float height = 16.0;
const float width = 256.0;
float2 adjustedUv = float2(uv.x * width, uv.y * height);
float3 coords = float3(adjustedUv.x%height, adjustedUv.x/height, adjustedUv.y);
return coords/height;
}
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
{
// framebuffer sampler
Sampler LinearSampler
{
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
AddressW = Clamp;
};
// Identity LUTs
Texture3D<float4> m_identityLut16x16x16;
float m_colorGradingExposure;
float m_colorGradingContrast;
float m_colorGradingHueShift;
float m_colorGradingPreSaturation;
float m_colorFilterIntensity;
float m_colorFilterMultiply;
float m_whiteBalanceKelvin;
float m_whiteBalanceTint;
float m_splitToneBalance;
float m_splitToneWeight;
float m_colorGradingPostSaturation;
float m_smhShadowsStart;
float m_smhShadowsEnd;
float m_smhHighlightsStart;
float m_smhHighlightsEnd;
float m_smhWeight;
float3 m_channelMixingRed;
float3 m_channelMixingGreen;
float3 m_channelMixingBlue;
float4 m_colorFilterSwatch;
float4 m_splitToneShadowsColor;
float4 m_splitToneHighlightsColor;
float4 m_smhShadowsColor;
float4 m_smhMidtonesColor;
float4 m_smhHighlightsColor;
}
float SaturateWithEpsilon(float value)
{
return clamp(value, FloatEpsilon, 1.0f);
}
// Below are the color grading functions. These expect the frame color to be in ACEScg space.
// Note that some functions may have some quirks in their implementation and is subject to change.
float3 ColorGradePostExposure (float3 frameColor, float exposure)
{
frameColor *= pow(2.0f, exposure);
return frameColor;
}
// The contrast equation is performed in ACEScc (logarithmic) color space.
float3 ColorGradingContrast (float3 frameColor, float midgrey, float amount)
{
const float contrastAdjustment = amount * 0.01f + 1.0f;
frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScg, ColorSpaceId::ACEScc);
frameColor = (frameColor - midgrey) * contrastAdjustment + midgrey;
return frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScc, ColorSpaceId::ACEScg);
}
// The swatchColor param expects a linear RGB value.
float3 ColorGradeColorFilter (float3 frameColor, float3 swatchColor, float alpha)
{
swatchColor = TransformColor(swatchColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
swatchColor *= pow(2.0f, PassSrg::m_colorFilterIntensity);
const float3 frameAdjust = frameColor * swatchColor;
return frameColor = lerp(frameColor, frameAdjust, alpha);
}
float3 ColorGradeHueShift (float3 frameColor, float amount)
{
float3 frameHsv = RgbToHsv(frameColor);
const float hue = frameHsv.x + amount;
frameHsv.x = RotateHue(hue, 0.0, 1.0);
return HsvToRgb(frameHsv);
}
float3 ColorGradeSaturation (float3 frameColor, float control)
{
const float vLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
return (frameColor - vLuminance) * control + vLuminance;
}
float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin)
{
const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL
return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance
}
// pow(f, e) won't work if f is negative, or may cause inf/NAN.
float3 NoNanPow(float3 base, float3 power)
{
return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power);
}
float3 ColorGradeSplitTone (float3 frameColor, float balance, float weight)
{
float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2);
const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance);
const float3 shadows = lerp(0.5, PassSrg::m_splitToneShadowsColor.rgb, 1.0 - t);
const float3 highlights = lerp(0.5, PassSrg::m_splitToneHighlightsColor.rgb, t);
frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows);
frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights);
frameSplitTone = NoNanPow(frameSplitTone, 2.2);
return lerp(frameColor.rgb, frameSplitTone.rgb, weight);
}
float3 ColorGradeChannelMixer (float3 frameColor)
{
return mul(float3x3(PassSrg::m_channelMixingRed.rgb,
PassSrg::m_channelMixingGreen.rgb,
PassSrg::m_channelMixingBlue.rgb),
frameColor);
}
float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd,
float highlightsStart, float highlightsEnd, float weight,
float4 shadowsColor, float4 midtonesColor, float4 highlightsColor)
{
const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
const float3 midtonesColorACEScg = TransformColor(midtonesColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
const float3 highlightsColorACEScg = TransformColor(highlightsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
const float cLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
const float shadowsWeight = 1.0 - smoothstep(shadowsStart, shadowsEnd, cLuminance);
const float highlightsWeight = smoothstep(highlightsStart, highlightsEnd, cLuminance);
const float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight;
const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight +
frameColor * midtonesColorACEScg * midtonesWeight +
frameColor * highlightsColorACEScg * highlightsWeight;
return lerp(frameColor.rgb, frameSmh.rgb, weight);
}
float3 ColorGrade(float3 frameColor)
{
frameColor = ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure);
frameColor = ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin);
frameColor = ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast);
frameColor = ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb,
PassSrg::m_colorFilterMultiply);
frameColor = max(frameColor, 0.0);
frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation);
frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight);
frameColor = ColorGradeChannelMixer(frameColor);
frameColor = max(frameColor, 0.0);
frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd,
PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight,
PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor);
frameColor = ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift);
frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation);
return frameColor.rgb;
}
struct PSOutput
{
float4 m_lutOutput : SV_Target0;
};
float3 GetSourceLutLinearColor(float3 baseColor, Texture3D<float4> sourceLut, ShaperType shaperType, float shaperBias, float shaperScale)
{
// Convert from reference linearColor to the lutCoordinate for this Lut
float3 lutCoord = LinearToShaper(baseColor, shaperType, shaperBias, shaperScale);
// Adjust coordinate to the domain excluding the outer half texel in all directions
uint3 outputDimensions;
sourceLut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z);
float3 coordBias = 1.0 / (2.0 * outputDimensions);
float3 coordScale = (outputDimensions - 1.0) / outputDimensions;
lutCoord = (lutCoord * coordScale) + coordBias;
float3 lutColor = sourceLut.SampleLevel(PassSrg::LinearSampler, lutCoord, 0).rgb;
// Convert to linear
float3 linearColor = ShaperToLinear(lutColor, shaperType, shaperBias, shaperScale);
return linearColor;
}
float3 InverseGamma(float3 color)
{
return pow(color, 2.2);
}
PSOutput MainPS(VSOutput IN)
{
int shaperNum = 0;
ShaperType shaperType = (ShaperType)shaperNum;
//ShaperType shaperType = ShaperType::ShaperLinear;
float bias = 0.0;
float scale = 1.0;
PSOutput OUT;
float3 baseCoords = convert2Dto3DLutCoords(IN.m_texCoord);
float3 baseColor = ShaperToLinear(baseCoords, shaperType, bias, scale);
float3 lutColor = PassSrg::m_identityLut16x16x16.Sample(PassSrg::LinearSampler, baseColor, 0.0).rgb;
//float3 lutColor = GetSourceLutLinearColor(baseColor, PassSrg::m_identityLut16x16x16, shaperType, bias, scale);
//float3 gradedColor = float4(ColorGrade(lutColor), 1.0);
//float3 finalColor = LinearToShaper(gradedColor, shaperType, bias, scale);
OUT.m_lutOutput = float4(lutColor, 1.0);
return OUT;
}
@@ -0,0 +1,22 @@
{
"Source" : "LutGeneration",
"DepthStencilState" : {
"Depth" : { "Enable" : false }
},
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "MainVS",
"type": "Vertex"
},
{
"name": "MainPS",
"type": "Fragment"
}
]
}
}
@@ -86,7 +86,6 @@ set(FILES
Passes/CascadedShadowmaps.pass
Passes/CheckerboardResolveColor.pass
Passes/CheckerboardResolveDepth.pass
Passes/HDRColorGrading.pass
Passes/ContrastAdaptiveSharpening.pass
Passes/ConvertToAcescg.pass
Passes/DebugOverlayParent.pass
@@ -144,6 +143,7 @@ set(FILES
Passes/ForwardSubsurfaceMSAA.pass
Passes/FullscreenCopy.pass
Passes/FullscreenOutputOnly.pass
Passes/HDRColorGrading.pass
Passes/ImGui.pass
Passes/KawaseShadowBlur.pass
Passes/LightAdaptationParent.pass
@@ -158,6 +158,7 @@ set(FILES
Passes/LowEndPipeline.pass
Passes/LuminanceHeatmap.pass
Passes/LuminanceHistogramGenerator.pass
Passes/LutGeneration.pass
Passes/MainPipeline.pass
Passes/MainPipelineRenderToTexture.pass
Passes/ThumbnailPipeline.pass
@@ -316,6 +317,8 @@ set(FILES
Shaders/BRDFTexture/BRDFTextureCS.shader
Shaders/Checkerboard/CheckerboardColorResolveCS.azsl
Shaders/Checkerboard/CheckerboardColorResolveCS.shader
Shaders/ColorGrading/LutGeneration.azsl
Shaders/ColorGrading/LutGeneration.shader
Shaders/Depth/DepthPass.azsl
Shaders/Depth/DepthPass.shader
Shaders/Depth/DepthPassTransparentMax.shader
@@ -39,6 +39,7 @@ ly_add_target(
Gem::Atom_Utils.Static
Gem::Atom_Feature_Common.Public
Gem::ImGui.imguilib
3rdParty::tiff
#3rdParty::lux_core # AZ_TRAIT_LUXCORE_SUPPORTED is disabled in every platform, Issue #3915 will remove
RUNTIME_DEPENDENCIES
Gem::ImGui.imguilib
@@ -10,6 +10,7 @@
// PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...)
AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false)
AZ_GFX_BOOL_PARAM(GenerateLut, m_generateLut, false)
AZ_GFX_FLOAT_PARAM(ColorGradingExposure, m_colorGradingExposure, 0.0)
AZ_GFX_FLOAT_PARAM(ColorGradingContrast, m_colorGradingContrast, 0.0)
AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0)
@@ -0,0 +1,100 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma optimize("", off)
#include <ColorGrading/LutGenerationPass.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
namespace AZ
{
namespace Render
{
RPI::Ptr<LutGenerationPass> LutGenerationPass::Create(const RPI::PassDescriptor& descriptor)
{
RPI::Ptr<LutGenerationPass> pass = aznew LutGenerationPass(descriptor);
return AZStd::move(pass);
}
LutGenerationPass::LutGenerationPass(const RPI::PassDescriptor& descriptor)
: HDRColorGradingPass(descriptor)
{
}
void LutGenerationPass::BuildInternal()
{
auto scene = GetScene();
if (scene)
{
AcesDisplayMapperFeatureProcessor* dmfp = scene->GetFeatureProcessor<AcesDisplayMapperFeatureProcessor>();
if (dmfp)
{
// load the image assets
auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(
"lookuptables/lut_identitylinear_16x16x16.azasset", AZ::RPI::AssetUtils::TraceLevel::Error);
AZ_Assert(assetId.IsValid(), "LUT Asset is not valid.");
dmfp->GetLutFromAssetId(m_colorGradingLut, assetId);
// set srg image index
m_shaderResourceGroup->SetImageView(m_identityLut16x16x16Index, m_colorGradingLut.m_lutStreamingImage->GetImageView());
// Get the output image attachment
RPI::Ptr<RPI::PassAttachment> attachment = FindOwnedAttachment(Name{ "ColorGradingLut" });
RHI::ImageDescriptor& imageDescriptor = attachment->m_descriptor.m_image;
imageDescriptor.m_size = RHI::Size(
m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_width*m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_width,
m_colorGradingLut.m_lutStreamingImage->GetDescriptor().m_size.m_height,
1);
}
}
HDRColorGradingPass::BuildInternal();
}
void LutGenerationPass::InitializeInternal()
{
HDRColorGradingPass::InitializeInternal();
m_identityLut16x16x16Index.Reset();
m_identityLut32x32x32Index.Reset();
m_identityLut64x64x64Index.Reset();
//// load the image assets
//DisplayMapperAssetLut m_colorGradingLut;
//auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath("lookuptables/lut_identitylinear_16x16x16.azasset", AZ::RPI::AssetUtils::TraceLevel::Error);
//AcesDisplayMapperFeatureProcessor* dmfp = GetScene()->GetFeatureProcessor<AcesDisplayMapperFeatureProcessor>();
//dmfp->GetLutFromAssetId(m_colorGradingLut, assetId);
//// set srg image index
//m_shaderResourceGroup->SetImageView(m_identityLut16x16x16Index, m_colorGradingLut.m_lutStreamingImage->GetImageView());
}
void LutGenerationPass::FrameBeginInternal(FramePrepareParams params)
{
HDRColorGradingPass::FrameBeginInternal(params);
}
void LutGenerationPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
{
HDRColorGradingPass::BuildCommandListInternal(context);
}
bool LutGenerationPass::IsEnabled() const
{
//const auto* colorGradingSettings = GetHDRColorGradingSettings();
//return colorGradingSettings ? colorGradingSettings->GetGenerateLut() : false;
return true;
}
} // namespace Render
} // namespace AZ
#pragma optimize("", on)
@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <PostProcessing/HDRColorGradingPass.h>
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
#include <PostProcess/ColorGrading/HDRColorGradingSettings.h>
#include <Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h>
namespace AZ
{
namespace Render
{
class LutGenerationPass
: public AZ::Render::HDRColorGradingPass
{
public:
AZ_RTTI(LutGenerationPass, "{C21DABA8-B538-4C80-BA18-5B97CC9259E5}", AZ::RPI::FullscreenTrianglePass);
AZ_CLASS_ALLOCATOR(LutGenerationPass, SystemAllocator, 0);
virtual ~LutGenerationPass() = default;
//! Creates a ColorGradingPass
static RPI::Ptr<LutGenerationPass> Create(const RPI::PassDescriptor& descriptor);
protected:
LutGenerationPass(const RPI::PassDescriptor& descriptor);
void BuildInternal() override;
void InitializeInternal() override;
void FrameBeginInternal(FramePrepareParams params) override;
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
bool IsEnabled() const override;
private:
RHI::ShaderInputNameIndex m_identityLut16x16x16Index = "m_identityLut16x16x16";
RHI::ShaderInputNameIndex m_identityLut32x32x32Index = "m_identityLut32x32x32";
RHI::ShaderInputNameIndex m_identityLut64x64x64Index = "m_identityLut64x64x64";
DisplayMapperAssetLut m_colorGradingLut;
bool m_isInitialized = false;
};
} // namespace Render
} // namespace AZ
@@ -39,6 +39,7 @@
#include <Atom/Feature/AuxGeom/AuxGeomFeatureProcessor.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <ColorGrading/LutGenerationPass.h>
#include <PostProcess/PostProcessFeatureProcessor.h>
#include <PostProcessing/BlendColorGradingLutsPass.h>
#include <PostProcessing/BloomParentPass.h>
@@ -230,6 +231,7 @@ namespace AZ
passSystem->AddPassCreator(Name("HDRColorGradingPass"), &HDRColorGradingPass::Create);
passSystem->AddPassCreator(Name("LookModificationCompositePass"), &LookModificationCompositePass::Create);
passSystem->AddPassCreator(Name("LookModificationTransformPass"), &LookModificationPass::Create);
passSystem->AddPassCreator(Name("LutGenerationPass"), &LutGenerationPass::Create);
passSystem->AddPassCreator(Name("SMAAEdgeDetectionPass"), &SMAAEdgeDetectionPass::Create);
passSystem->AddPassCreator(Name("SMAABlendingWeightCalculationPass"), &SMAABlendingWeightCalculationPass::Create);
passSystem->AddPassCreator(Name("SMAANeighborhoodBlendingPass"), &SMAANeighborhoodBlendingPass::Create);
@@ -35,6 +35,7 @@
#include <AzCore/Preprocessor/EnumReflectUtils.h>
#include <AzCore/Console/Console.h>
#include <tiffio.h>
namespace AZ
{
namespace Render
@@ -106,6 +107,48 @@ namespace AZ
return FrameCaptureOutputResult{FrameCaptureResult::InternalError, "Unable to save frame capture output to '" + outputFilePath + "'"};
}
FrameCaptureOutputResult TiffFrameCaptureOutput(
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
AZStd::shared_ptr<AZStd::vector<uint8_t>> buffer = readbackResult.m_dataBuffer;
const uint32_t width = readbackResult.m_imageDescriptor.m_size.m_width;
const uint32_t height = readbackResult.m_imageDescriptor.m_size.m_height;
const uint32_t numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format);
const uint32_t bytesPerChannel = AZ::RHI::GetFormatSize(readbackResult.m_imageDescriptor.m_format) / numChannels;
const uint32_t bitsPerChannel = bytesPerChannel * 8;
TIFF* out = TIFFOpen(outputFilePath.c_str(), "w");
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, numChannels);
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitsPerChannel);
TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); // interpret each pixel as a float
size_t pitch = width * numChannels * bytesPerChannel;
AZ_Assert((pitch * height) == buffer->size(), "Image buffer does not match allocated bytes for tiff saving.")
unsigned char* raster = (unsigned char*)_TIFFmalloc((tsize_t)(pitch * height));
memcpy(raster, buffer->data(), pitch * height);
bool success = true;
for (uint32_t h = 0; h < height; ++h)
{
size_t offset = h * pitch;
int err = TIFFWriteScanline(out, raster + offset, h, 0);
if (err < 0)
{
success = false;
break;
}
}
_TIFFfree(raster);
TIFFClose(out);
return success ? FrameCaptureOutputResult{ FrameCaptureResult::Success, AZStd::nullopt }
: FrameCaptureOutputResult{ FrameCaptureResult::InternalError, "Unable to save tif frame capture output to " + outputFilePath };
}
FrameCaptureOutputResult DdsFrameCaptureOutput(
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
@@ -488,6 +531,12 @@ namespace AZ
m_result = ddsFrameCapture.m_result;
m_latestCaptureInfo = ddsFrameCapture.m_errorMessage.value_or("");
}
else if (extension == "tiff" || extension == "tif")
{
const auto tifFrameCapture = TiffFrameCaptureOutput(m_outputFilePath, readbackResult);
m_result = tifFrameCapture.m_result;
m_latestCaptureInfo = tifFrameCapture.m_errorMessage.value_or("");
}
else if (extension == "png")
{
if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM ||
@@ -41,10 +41,9 @@ namespace AZ
void FrameBeginInternal(FramePrepareParams params) override;
bool IsEnabled() const override;
private:
const HDRColorGradingSettings* GetHDRColorGradingSettings() const;
void SetSrgConstants();
virtual void SetSrgConstants();
private:
RHI::ShaderInputNameIndex m_colorGradingExposureIndex = "m_colorGradingExposure";
RHI::ShaderInputNameIndex m_colorGradingContrastIndex = "m_colorGradingContrast";
RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift";
@@ -66,6 +66,8 @@ set(FILES
Source/AuxGeom/DynamicPrimitiveProcessor.h
Source/AuxGeom/FixedShapeProcessor.cpp
Source/AuxGeom/FixedShapeProcessor.h
Source/ColorGrading/LutGenerationPass.cpp
Source/ColorGrading/LutGenerationPass.h
Source/CoreLights/CapsuleLightFeatureProcessor.h
Source/CoreLights/CapsuleLightFeatureProcessor.cpp
Source/CoreLights/CascadedShadowmapsPass.h
@@ -212,8 +214,6 @@ set(FILES
Source/PostProcessing/BloomCompositePass.cpp
Source/PostProcessing/BloomParentPass.h
Source/PostProcessing/BloomParentPass.cpp
Source/PostProcessing/HDRColorGradingPass.cpp
Source/PostProcessing/HDRColorGradingPass.h
Source/PostProcessing/DepthOfFieldCompositePass.h
Source/PostProcessing/DepthOfFieldCompositePass.cpp
Source/PostProcessing/DepthOfFieldBokehBlurPass.h
@@ -234,6 +234,8 @@ set(FILES
Source/PostProcessing/EyeAdaptationPass.h
Source/PostProcessing/FastDepthAwareBlurPasses.cpp
Source/PostProcessing/FastDepthAwareBlurPasses.h
Source/PostProcessing/HDRColorGradingPass.cpp
Source/PostProcessing/HDRColorGradingPass.h
Source/PostProcessing/LookModificationCompositePass.cpp
Source/PostProcessing/LookModificationCompositePass.h
Source/PostProcessing/LookModificationTransformPass.cpp
@@ -7,6 +7,7 @@
*/
#include <PostProcess/ColorGrading/EditorHDRColorGradingComponent.h>
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
namespace AZ
{
@@ -31,6 +32,12 @@ namespace AZ
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing.
->ClassElement(AZ::Edit::ClassElements::Group, "LUT Generation")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->UIElement(AZ::Edit::UIHandlers::Button, "Generate LUT", "Generates a LUT from the scene's enabled color grading blend.")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->Attribute(AZ::Edit::Attributes::ButtonText, "Generate LUT")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHDRColorGradingComponent::GenerateLut)
;
editContext->Class<HDRColorGradingComponentController>(
@@ -136,6 +143,69 @@ namespace AZ
{
}
void EditorHDRColorGradingComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if (m_controller.m_configuration.m_generateLut && m_frameCounter)
{
--m_frameCounter;
return;
}
else if (m_controller.m_configuration.m_generateLut && m_lutGenerationInProgress)
{
const char* LutAttachment = "LutOutput";
const AZStd::vector<AZStd::string> LutGenerationPassHierarchy{ "LutGenerationPass" };
// capture frame
AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect();
bool startedCapture = false;
AZ::Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture,
&AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachment,
LutGenerationPassHierarchy,
AZStd::string(LutAttachment), TempTiffFilePath,
AZ::RPI::PassAttachmentReadbackOption::Output);
m_lutGenerationInProgress = !startedCapture;
}
}
void EditorHDRColorGradingComponent::OnCaptureFinished([[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]]const AZStd::string& info)
{
char resolvedInputFilePath[AZ_MAX_PATH_LEN] = { 0 };
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(TempTiffFilePath, resolvedInputFilePath, AZ_MAX_PATH_LEN);
char resolvedOutputFilePath[AZ_MAX_PATH_LEN] = { 0 };
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(GeneratedLutFilePath, resolvedOutputFilePath, AZ_MAX_PATH_LEN);
AZStd::vector<AZStd::string_view> pythonArgs
{
"--i", resolvedInputFilePath,
"--o", resolvedOutputFilePath
};
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs,
"@devroot@/Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/tiff_to_3dl_azasset.py", pythonArgs);
m_controller.m_configuration.m_generateLut = false;
m_controller.OnConfigChanged();
AZ::TickBus::Handler::BusDisconnect();
AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
}
void EditorHDRColorGradingComponent::GenerateLut()
{
// turn on lut generation pass
m_lutGenerationInProgress = true;
m_controller.m_configuration.m_generateLut = true;
m_controller.OnConfigChanged();
m_frameCounter = FramesToWait;
AZ::TickBus::Handler::BusConnect();
}
u32 EditorHDRColorGradingComponent::OnConfigurationChanged()
{
m_controller.OnConfigChanged();
@@ -8,16 +8,23 @@
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentAdapter.h>
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <PostProcess/ColorGrading/HDRColorGradingComponent.h>
namespace AZ
{
namespace Render
{
static const char* const TempTiffFilePath{ "@projectcache@/LutGeneration/SavedLut.tiff" };
static const char* const GeneratedLutFilePath{ "@projectcache@/LutGeneration/SavedLut" };
class EditorHDRColorGradingComponent final
: public AzToolsFramework::Components::
EditorComponentAdapter<HDRColorGradingComponentController, HDRColorGradingComponent, HDRColorGradingComponentConfig>
, private TickBus::Handler
, private FrameCaptureNotificationBus::Handler
{
public:
using BaseClass = AzToolsFramework::Components::EditorComponentAdapter<HDRColorGradingComponentController, HDRColorGradingComponent, HDRColorGradingComponentConfig>;
@@ -25,11 +32,27 @@ namespace AZ
static void Reflect(AZ::ReflectContext* context);
static const int FramesToWait = 5;
//static const char* LutAttachment;
//static const AZStd::vector<AZStd::string> LutGenerationPassHierarchy;
EditorHDRColorGradingComponent() = default;
EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config);
//! EditorRenderComponentAdapter overrides...
AZ::u32 OnConfigurationChanged() override;
private:
// AZ::TickBus overrides ...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// FrameCaptureNotificationBus overrides ...
void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override;
void GenerateLut();
AZStd::atomic_bool m_lutGenerationInProgress = false;
int m_frameCounter;
};
} // namespace Render
} // namespace AZ