diff --git a/sources/engine/Stride.Rendering/Rendering/Images/GroundTruthAmbientOcclusion/GroundTruthAmbientOcclusion.cs b/sources/engine/Stride.Rendering/Rendering/Images/GroundTruthAmbientOcclusion/GroundTruthAmbientOcclusion.cs
new file mode 100644
index 0000000000..f599178f27
--- /dev/null
+++ b/sources/engine/Stride.Rendering/Rendering/Images/GroundTruthAmbientOcclusion/GroundTruthAmbientOcclusion.cs
@@ -0,0 +1,314 @@
+// Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
+// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
+
+using System.ComponentModel;
+using Stride.Core;
+using Stride.Core.Annotations;
+using Stride.Core.Mathematics;
+using Stride.Graphics;
+
+namespace Stride.Rendering.Images
+{
+ ///
+ /// Applies an ambient occlusion effect to a scene. Ambient occlusion is a technique which fakes occlusion for objects close to other opaque objects.
+ /// It takes as input a color-buffer where the scene was rendered, with its associated depth-buffer.
+ /// You also need to provide the camera configuration you used when rendering the scene.
+ ///
+ [DataContract("GroundTruthAmbientOcclusion")]
+ public class GroundTruthAmbientOcclusion : ImageEffect
+ {
+ private ImageEffectShader aoRawImageEffect;
+ private ImageEffectShader blurH;
+ private ImageEffectShader blurV;
+ private string nameGaussianBlurH;
+ private string nameGaussianBlurV;
+ private float[] offsetsWeights;
+
+ private ImageEffectShader aoApplyImageEffect;
+
+ public GroundTruthAmbientOcclusion()
+ {
+ //Enabled = false;
+
+ NumberOfSamples = 13;
+ ParamProjScale = 0.5f;
+ ParamIntensity = 0.2f;
+ ParamBias = 0.01f;
+ ParamRadius = 1f;
+ NumberOfBounces = 2;
+ BlurScale = 1.85f;
+ EdgeSharpness = 3f;
+ TempSize = TemporaryBufferSize.SizeFull;
+ }
+
+ ///
+ /// The number of pixels sampled to determine how occluded a point is. Higher values reduce noise, but affect performance.
+ /// Use with "Blur count to find a balance between results and performance.
+ ///
+ [DataMember(10)]
+ [DefaultValue(13)]
+ [DataMemberRange(1, 50, 1, 5, 0)]
+ [Display("Samples")]
+ public int NumberOfSamples { get; set; } = 13;
+
+ ///
+ /// Scales the sample radius. In most cases, 1 (no scaling) produces the most accurate result.
+ ///
+ [DataMember(20)]
+ [DefaultValue(0.5f)]
+ [Display("Projection scale")]
+ public float ParamProjScale { get; set; } = 0.5f;
+
+ ///
+ /// The strength of the darkening effect in occluded areas
+ ///
+ [DataMember(30)]
+ [DefaultValue(0.2f)]
+ [Display("Intensity")]
+ public float ParamIntensity { get; set; } = 0.2f;
+
+ ///
+ /// The angle at which Stride considers an area of geometry an occluder. At high values, only narrow joins and crevices are considered occluders.
+ ///
+ [DataMember(40)]
+ [DefaultValue(0.01f)]
+ [Display("Sample bias")]
+ public float ParamBias { get; set; } = 0.01f;
+
+ ///
+ /// Use with "projection scale" to control the radius of the occlusion effect
+ ///
+ [DataMember(50)]
+ [DefaultValue(1f)]
+ [Display("Sample radius")]
+ public float ParamRadius { get; set; } = 1f;
+
+ ///
+ /// The number of times the ambient occlusion image is blurred. Higher numbers reduce noise, but can produce artifacts.
+ ///
+ [DataMember(70)]
+ [DefaultValue(2)]
+ [DataMemberRange(0, 3, 1, 1, 0)]
+ [Display("Blur count")]
+ public int NumberOfBounces { get; set; } = 2;
+
+ ///
+ /// The blur radius in pixels
+ ///
+ [DataMember(74)]
+ [DefaultValue(1.85f)]
+ [Display("Blur radius")]
+ public float BlurScale { get; set; } = 1.85f;
+
+ ///
+ /// How much the blur respects the depth differences of occluded areas. Lower numbers create more blur, but might blur unwanted areas (ie beyond occluded areas).
+ ///
+ [DataMember(78)]
+ [DefaultValue(3f)]
+ [Display("Edge sharpness")]
+ public float EdgeSharpness { get; set; } = 3f;
+
+ ///
+ /// The resolution the ambient occlusion is calculated at. The result is upscaled to the game resolution.
+ /// Larger sizes produce better results but use more memory and affect performance.
+ ///
+ [DataMember(100)]
+ [DefaultValue(TemporaryBufferSize.SizeFull)]
+ [Display("Buffer size")]
+ public TemporaryBufferSize TempSize { get; set; } = TemporaryBufferSize.SizeFull;
+
+ protected override void InitializeCore()
+ {
+ base.InitializeCore();
+
+ aoApplyImageEffect = ToLoadAndUnload(new ImageEffectShader("ApplyAmbientOcclusionShader"));
+
+ aoRawImageEffect = ToLoadAndUnload(new ImageEffectShader("AmbientOcclusionRawAOEffect"));
+ aoRawImageEffect.Initialize(Context);
+
+ blurH = ToLoadAndUnload(new ImageEffectShader("AmbientOcclusionBlurEffect"));
+ blurV = ToLoadAndUnload(new ImageEffectShader("AmbientOcclusionBlurEffect", true));
+ blurH.Initialize(Context);
+ blurV.Initialize(Context);
+
+ // Setup Horizontal parameters
+ blurH.Parameters.Set(AmbientOcclusionBlurKeys.VerticalBlur, false);
+ blurV.Parameters.Set(AmbientOcclusionBlurKeys.VerticalBlur, true);
+ }
+
+ protected override void Destroy()
+ {
+ base.Destroy();
+ }
+
+ ///
+ /// Provides a color buffer and a depth buffer to apply the depth-of-field to.
+ ///
+ /// A color buffer to process.
+ /// The depth buffer corresponding to the color buffer provided.
+ public void SetColorDepthNormalsInput(Texture colorBuffer, Texture depthBuffer, Texture normalsBuffer)
+ {
+ SetInput(0, colorBuffer);
+ SetInput(1, depthBuffer);
+ SetInput(2, normalsBuffer);
+ }
+
+ protected override void DrawCore(RenderDrawContext context)
+ {
+ var originalColorBuffer = GetSafeInput(0);
+ var originalDepthBuffer = GetSafeInput(1);
+
+ var outputTexture = GetSafeOutput(0);
+
+ var renderView = context.RenderContext.RenderView;
+
+ //---------------------------------
+ // Ambient Occlusion
+ //---------------------------------
+
+ var tempWidth = (originalColorBuffer.Width * (int)TempSize) / (int)TemporaryBufferSize.SizeFull;
+ var tempHeight = (originalColorBuffer.Height * (int)TempSize) / (int)TemporaryBufferSize.SizeFull;
+ var aoTexture1 = NewScopedRenderTarget2D(tempWidth, tempHeight, PixelFormat.R8_UNorm, 1);
+ var aoTexture2 = NewScopedRenderTarget2D(tempWidth, tempHeight, PixelFormat.R8_UNorm, 1);
+
+
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOKeys.Count, NumberOfSamples > 0 ? NumberOfSamples : 9);
+
+ // check whether the projection matrix is orthographic
+ var isOrthographic = renderView.Projection.M44 == 1;
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOKeys.IsOrthographic, isOrthographic);
+ blurH.Parameters.Set(AmbientOcclusionBlurKeys.IsOrthographic, isOrthographic);
+ blurV.Parameters.Set(AmbientOcclusionBlurKeys.IsOrthographic, isOrthographic);
+
+ Vector2 zProj;
+ if (isOrthographic)
+ {
+ zProj = new Vector2(renderView.NearClipPlane, renderView.FarClipPlane - renderView.NearClipPlane);
+ }
+ else
+ {
+ zProj = CameraKeys.ZProjectionACalculate(renderView.NearClipPlane, renderView.FarClipPlane);
+ }
+
+ // Set Near/Far pre-calculated factors to speed up the linear depth reconstruction
+ aoRawImageEffect.Parameters.Set(CameraKeys.ZProjection, ref zProj);
+
+
+ Vector4 screenSize = new Vector4(originalColorBuffer.Width, originalColorBuffer.Height, 0, 0);
+ screenSize.Z = screenSize.X / screenSize.Y;
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ScreenInfo, screenSize);
+
+ Vector4 projInfo;
+ if (isOrthographic)
+ {
+ // The orthographic scale to map the xy coordinates
+ float scaleX = 1 / renderView.Projection.M11;
+ float scaleY = 1 / renderView.Projection.M22;
+
+ // Constant factor to map the ProjScale parameter to the orthographic scale
+ float projZScale = System.Math.Max(scaleX, scaleY) * 4;
+
+ projInfo = new Vector4(scaleX, scaleY, projZScale, 0);
+ }
+ else
+ {
+ // Projection info used to reconstruct the View space position from linear depth
+ var p00 = renderView.Projection.M11;
+ var p11 = renderView.Projection.M22;
+ var p02 = renderView.Projection.M13;
+ var p12 = renderView.Projection.M23;
+
+ projInfo = new Vector4(
+ -2.0f / (screenSize.X * p00),
+ -2.0f / (screenSize.Y * p11),
+ (1.0f - p02) / p00,
+ (1.0f + p12) / p11);
+ }
+
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ProjInfo, ref projInfo);
+
+ //**********************************
+ // User parameters
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamProjScale, ParamProjScale);
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamIntensity, ParamIntensity);
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamBias, ParamBias);
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamRadius, ParamRadius);
+ aoRawImageEffect.Parameters.Set(AmbientOcclusionRawAOShaderKeys.ParamRadiusSquared, ParamRadius * ParamRadius);
+
+ aoRawImageEffect.SetInput(0, originalDepthBuffer);
+ aoRawImageEffect.SetOutput(aoTexture1);
+ aoRawImageEffect.Draw(context, "AmbientOcclusionRawAO");
+
+ for (int bounces = 0; bounces < NumberOfBounces; bounces++)
+ {
+ if (offsetsWeights == null)
+ {
+ offsetsWeights = new[]
+ {
+ // 0.356642f, 0.239400f, 0.072410f, 0.009869f,
+ // 0.398943f, 0.241971f, 0.053991f, 0.004432f, 0.000134f, // stddev = 1.0
+ 0.153170f, 0.144893f, 0.122649f, 0.092902f, 0.062970f, // stddev = 2.0
+ // 0.111220f, 0.107798f, 0.098151f, 0.083953f, 0.067458f, 0.050920f, 0.036108f, // stddev = 3.0
+ };
+
+ nameGaussianBlurH = string.Format("AmbientOcclusionBlurH{0}x{0}", offsetsWeights.Length);
+ nameGaussianBlurV = string.Format("AmbientOcclusionBlurV{0}x{0}", offsetsWeights.Length);
+ }
+
+ // Set Near/Far pre-calculated factors to speed up the linear depth reconstruction
+ blurH.Parameters.Set(CameraKeys.ZProjection, ref zProj);
+ blurV.Parameters.Set(CameraKeys.ZProjection, ref zProj);
+
+ // Update permutation parameters
+ blurH.Parameters.Set(AmbientOcclusionBlurKeys.Count, offsetsWeights.Length);
+ blurH.Parameters.Set(AmbientOcclusionBlurKeys.BlurScale, BlurScale);
+ blurH.Parameters.Set(AmbientOcclusionBlurKeys.EdgeSharpness, EdgeSharpness);
+ blurH.EffectInstance.UpdateEffect(context.GraphicsDevice);
+
+ blurV.Parameters.Set(AmbientOcclusionBlurKeys.Count, offsetsWeights.Length);
+ blurV.Parameters.Set(AmbientOcclusionBlurKeys.BlurScale, BlurScale);
+ blurV.Parameters.Set(AmbientOcclusionBlurKeys.EdgeSharpness, EdgeSharpness);
+ blurV.EffectInstance.UpdateEffect(context.GraphicsDevice);
+
+ // Update parameters
+ blurH.Parameters.Set(AmbientOcclusionBlurShaderKeys.Weights, offsetsWeights);
+ blurV.Parameters.Set(AmbientOcclusionBlurShaderKeys.Weights, offsetsWeights);
+
+ // Horizontal pass
+ blurH.SetInput(0, aoTexture1);
+ blurH.SetInput(1, originalDepthBuffer);
+ blurH.SetOutput(aoTexture2);
+ blurH.Draw(context, nameGaussianBlurH);
+
+ // Vertical pass
+ blurV.SetInput(0, aoTexture2);
+ blurV.SetInput(1, originalDepthBuffer);
+ blurV.SetOutput(aoTexture1);
+ blurV.Draw(context, nameGaussianBlurV);
+ }
+
+ aoApplyImageEffect.SetInput(0, originalColorBuffer);
+ aoApplyImageEffect.SetInput(1, aoTexture1);
+ aoApplyImageEffect.SetOutput(outputTexture);
+ aoApplyImageEffect.Draw(context, "AmbientOcclusionApply");
+ }
+
+ public enum TemporaryBufferSize
+ {
+ [Display("Full size")]
+ SizeFull = 12,
+
+ [Display("5/6 size")]
+ Size1012 = 10,
+
+ [Display("3/4 size")]
+ Size0912 = 9,
+
+ [Display("2/3 size")]
+ Size0812 = 8,
+
+ [Display("1/2 size")]
+ Size0612 = 6,
+ }
+ }
+}
diff --git a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs
index 948e3a0052..9b0d5e21bf 100644
--- a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs
+++ b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs
@@ -100,12 +100,22 @@ public PostProcessingEffects(RenderContext context)
[Category]
public AmbientOcclusion AmbientOcclusion { get; private set; }
+ ///
+ /// Gets the ground truth ambient occlusion effect. Ported from XeGTAO of Intel.
+ ///
+ ///
+ /// Darkens areas where light is occluded by opaque objects, such as corners and crevices
+ ///
+ [DataMember(9)]
+ [Category]
+ public GroundTruthAmbientOcclusion GroundTruthAmbientOcclusion { get; private set; }
+
///
/// Gets the local reflections effect.
///
/// The local reflection technique.
/// Reflect the scene in glossy materials
- [DataMember(9)]
+ [DataMember(10)]
[Category]
public LocalReflections LocalReflections { get; private set; }
@@ -114,7 +124,7 @@ public PostProcessingEffects(RenderContext context)
///
/// The depth of field.
/// Accentuate regions of the image by blurring objects in the foreground or background
- [DataMember(10)]
+ [DataMember(11)]
[Category]
public DepthOfField DepthOfField { get; private set; }
@@ -269,7 +279,7 @@ public void Draw(RenderDrawContext drawContext, RenderOutputValidator outputVali
public bool RequiresVelocityBuffer => Antialiasing?.RequiresVelocityBuffer ?? false;
- public bool RequiresNormalBuffer => LocalReflections.Enabled;
+ public bool RequiresNormalBuffer => LocalReflections.Enabled || GroundTruthAmbientOcclusion.Enabled;
public bool RequiresSpecularRoughnessBuffer => LocalReflections.Enabled;
@@ -386,6 +396,21 @@ protected override void DrawCore(RenderDrawContext context)
currentInput = aoOutput;
}
+ if (GroundTruthAmbientOcclusion.Enabled && inputDepthTexture != null)
+ {
+ var normalsBuffer = GetInput(2);
+
+ if (normalsBuffer != null)
+ {
+ // Ground Truth Ambient Occlusion
+ var gtaoOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format);
+ GroundTruthAmbientOcclusion.SetColorDepthNormalsInput(currentInput, inputDepthTexture, normalsBuffer);
+ GroundTruthAmbientOcclusion.SetOutput(gtaoOutput);
+ GroundTruthAmbientOcclusion.Draw(context);
+ currentInput = gtaoOutput;
+ }
+ }
+
if (LocalReflections.Enabled && inputDepthTexture != null)
{
var normalsBuffer = GetInput(2);