Skip to content

Commit

Permalink
Added outline post fx.
Browse files Browse the repository at this point in the history
  • Loading branch information
herocrab committed Mar 31, 2021
1 parent 0b3fc81 commit bf7f771
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Xenko contributors (https://xenko.com) 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;
using Stride.Core;
using Stride.Core.Mathematics;
using Stride.Graphics;

namespace Stride.Rendering.Images {
/// <summary>
/// A fog filter.
/// </summary>
[DataContract("Outline")]
public class Outline : ImageEffect {
private readonly ImageEffectShader outlineFilter;
private Texture depthTexture;
private float zMin, zMax;

/// <summary>
/// Initializes a new instance of the <see cref="Outline"/> class.
/// </summary>
public Outline()
: this("OutlineEffect") {
}

[DataMember(10)]
public float NormalWeight { get; set; } = 2f;

[DataMember(20)]
public float DepthWeight { get; set; } = 0.2f;

[DataMember(30)]
public float NormalNearCutoff { get; set; } = 0.1f;

/// <summary>
/// Initializes a new instance of the <see cref="Outline"/> class.
/// </summary>
/// <param name="shaderName">Name of the outline shader.</param>
public Outline(string shaderName) : base(shaderName) {
if (shaderName == null) throw new ArgumentNullException("outlineFilterName");
outlineFilter = new ImageEffectShader(shaderName);
}

protected override void InitializeCore() {
base.InitializeCore();
ToLoadAndUnload(outlineFilter);
}

/// <summary>
/// Provides a color buffer and a depth buffer to apply the fog to.
/// </summary>
/// <param name="colorBuffer">A color buffer to process.</param>
/// <param name="depthBuffer">The depth buffer corresponding to the color buffer provided.</param>
public void SetColorDepthInput(Texture colorBuffer, Texture depthBuffer, float zMin, float zMax) {
SetInput(0, colorBuffer);
depthTexture = depthBuffer;
this.zMin = zMin;
this.zMax = zMax;
}

protected override void SetDefaultParameters() {
NormalWeight = 2f;
DepthWeight = 0.2f;
NormalNearCutoff = 0.1f;
base.SetDefaultParameters();
}

protected override void DrawCore(RenderDrawContext context) {
Texture color = GetInput(0);
Texture output = GetOutput(0);
if (color == null || output == null || depthTexture == null) {
return;
}

outlineFilter.Parameters.Set(OutlineEffectKeys.ScreenDiffs, new Vector2(0.5f / color.Width, 0.5f / color.Height));
outlineFilter.Parameters.Set(OutlineEffectKeys.DepthTexture, depthTexture);
outlineFilter.Parameters.Set(OutlineEffectKeys.zFar, zMax);
outlineFilter.Parameters.Set(OutlineEffectKeys.zNear, zMin);

outlineFilter.Parameters.Set(OutlineEffectKeys.NormalWeight, NormalWeight);
outlineFilter.Parameters.Set(OutlineEffectKeys.DepthWeight, DepthWeight);
outlineFilter.Parameters.Set(OutlineEffectKeys.NormalNearCutoff, NormalNearCutoff);

outlineFilter.SetInput(0, color);
outlineFilter.SetOutput(output);
((RendererBase)outlineFilter).Draw(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <auto-generated>
// Do not edit this file yourself!
//
// This code was generated by Xenko Shader Mixin Code Generator.
// To generate it yourself, please install Xenko.VisualStudio.Package .vsix
// and re-save the associated .xkfx.
// </auto-generated>

using Stride.Graphics;
using Stride.Core.Mathematics;

namespace Stride.Rendering
{
internal static partial class OutlineEffectKeys
{
public static readonly ValueParameterKey<Vector2> ScreenDiffs = ParameterKeys.NewValue<Vector2>();
public static readonly ValueParameterKey<float> zFar = ParameterKeys.NewValue<float>(1000f);
public static readonly ValueParameterKey<float> zNear = ParameterKeys.NewValue<float>(0.1f);
public static readonly ValueParameterKey<float> NormalWeight = ParameterKeys.NewValue<float>(2f);
public static readonly ValueParameterKey<float> DepthWeight = ParameterKeys.NewValue<float>(0.2f);
public static readonly ValueParameterKey<float> NormalNearCutoff = ParameterKeys.NewValue<float>(0.1f);
public static readonly ObjectParameterKey<Texture> DepthTexture = ParameterKeys.NewObject<Texture>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
namespace Stride.Rendering.Images
{
/// <summary>
/// Simple fog
/// </summary>
internal shader OutlineEffect : ImageEffectShader
{
stage float2 ScreenDiffs; // .x = Width, .y = Height

stage float zFar;
stage float zNear;

stage float NormalWeight;
stage float DepthWeight;
stage float NormalNearCutoff;

stage Texture2D DepthTexture;

float3 normal_from_depth(float depth, float2 texcoords) {
const float2 offset1 = float2(0.0,ScreenDiffs.y);
const float2 offset2 = float2(ScreenDiffs.x,0.0);

float depth1 = DepthTexture.SampleLevel(PointSampler, texcoords + offset1, 0.0).x;
float depth2 = DepthTexture.SampleLevel(PointSampler, texcoords + offset2, 0.0).x;

float3 p1 = float3(offset1, depth1 - depth);
float3 p2 = float3(offset2, depth2 - depth);

float3 normal = cross(p1, p2);
normal.z = -normal.z;

return normalize(normal);
}

float4 fetchNormalDepth(float2 tc){
float4 nd; // return value

// get depth
float z_b = DepthTexture.SampleLevel(PointSampler, tc, 0.0).x;
float z_n = 2.0 * z_b - 1.0;
float linearDepth = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));

// linear depth
nd.w = DepthWeight * linearDepth;

// normal, but skip stuff really close
nd.xyz = step(NormalNearCutoff, linearDepth) * normal_from_depth(z_b, tc) * NormalWeight;

return nd;
}

stage override float4 Shading() {
float4 color = Texture0.Sample(PointSampler, streams.TexCoord);

float4 n1 = fetchNormalDepth(streams.TexCoord + float2(-ScreenDiffs.x, -ScreenDiffs.y));
float4 n2 = fetchNormalDepth(streams.TexCoord + float2( ScreenDiffs.x, ScreenDiffs.y));
float4 n3 = fetchNormalDepth(streams.TexCoord + float2(-ScreenDiffs.x, ScreenDiffs.y));
float4 n4 = fetchNormalDepth(streams.TexCoord + float2( ScreenDiffs.x, -ScreenDiffs.y));

// Work out how much the normal and depth values are changing.
float4 diagonalDelta = abs(n1 - n2) + abs(n3 - n4);

float normalDelta = dot(diagonalDelta.xyz, float3(1.0, 1.0, 1.0));
float totalDelta = diagonalDelta.w + normalDelta * 0.4;

return float4(color.xyz * (1.0 - clamp(totalDelta, 0.0, 1.0)), 1.0);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public PostProcessingEffects(IServiceRegistry services)
/// </summary>
public PostProcessingEffects()
{
Outline = new Outline {Enabled = false};
AmbientOcclusion = new AmbientOcclusion();
LocalReflections = new LocalReflections();
DepthOfField = new DepthOfField();
Expand Down Expand Up @@ -68,6 +69,12 @@ public PostProcessingEffects(RenderContext context)
[NonOverridable]
public Guid Id { get; set; } = Guid.NewGuid();

/// <summary>
/// Gets the outline effect.
/// </summary>
[DataMember(6)]
[Category]
public Outline Outline { get; private set; }
/// <summary>
/// Gets the ambient occlusion effect.
/// </summary>
Expand Down Expand Up @@ -156,6 +163,7 @@ public PostProcessingEffects(RenderContext context)
/// </summary>
public void DisableAll()
{
Outline.Enabled = false;
AmbientOcclusion.Enabled = false;
LocalReflections.Enabled = false;
DepthOfField.Enabled = false;
Expand All @@ -181,6 +189,7 @@ protected override void InitializeCore()
{
base.InitializeCore();

Outline = ToLoadAndUnload(Outline);
AmbientOcclusion = ToLoadAndUnload(AmbientOcclusion);
LocalReflections = ToLoadAndUnload(LocalReflections);
DepthOfField = ToLoadAndUnload(DepthOfField);
Expand Down Expand Up @@ -280,6 +289,17 @@ protected override void DrawCore(RenderDrawContext context)

var currentInput = input;

// Draw outline before AA
if (Outline.Enabled && inputDepthTexture != null)
{
// Outline
var outlineOutput = NewScopedRenderTarget2D(input.Width, input.Height, input.Format);
Outline.SetColorDepthInput(currentInput, inputDepthTexture, context.RenderContext.RenderView.NearClipPlane, context.RenderContext.RenderView.FarClipPlane);
Outline.SetOutput(outlineOutput);
Outline.Draw(context);
currentInput = outlineOutput;
}

var fxaa = Antialiasing as FXAAEffect;
bool aaFirst = Bloom != null && Bloom.StableConvolution;
bool needAA = Antialiasing != null && Antialiasing.Enabled;
Expand Down

0 comments on commit bf7f771

Please sign in to comment.