Merge remote-tracking branch 'origin/master' into gi

This commit is contained in:
Wojciech Figat
2022-04-21 12:57:08 +02:00
78 changed files with 604 additions and 311 deletions

BIN
Content/Editor/Camera/M_Camera.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/DefaultFontMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Gizmo/Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialWire.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Highlight Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Icons/IconsMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@@ -6,6 +6,10 @@
#include "./Flax/LightingCommon.hlsl" #include "./Flax/LightingCommon.hlsl"
#if USE_REFLECTIONS #if USE_REFLECTIONS
#include "./Flax/ReflectionsCommon.hlsl" #include "./Flax/ReflectionsCommon.hlsl"
#define MATERIAL_REFLECTIONS_SSR 1
#if MATERIAL_REFLECTIONS == MATERIAL_REFLECTIONS_SSR
#include "./Flax/SSR.hlsl"
#endif
#endif #endif
#include "./Flax/Lighting.hlsl" #include "./Flax/Lighting.hlsl"
#include "./Flax/ShadowsSampling.hlsl" #include "./Flax/ShadowsSampling.hlsl"
@@ -93,9 +97,29 @@ float4 PS_Forward(PixelInput input) : SV_Target0
light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight); light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight);
} }
#if USE_REFLECTIONS
// Calculate reflections // Calculate reflections
light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a; #if USE_REFLECTIONS
float3 reflections = SampleReflectionProbe(ViewPos, EnvProbe, EnvironmentProbe, gBuffer.WorldPos, gBuffer.Normal, gBuffer.Roughness).rgb;
#if MATERIAL_REFLECTIONS == MATERIAL_REFLECTIONS_SSR
// Screen Space Reflections
Texture2D sceneDepthTexture = MATERIAL_REFLECTIONS_SSR_DEPTH; // Material Generator inserts depth and color buffers and plugs it via internal define
Texture2D sceneColorTexture = MATERIAL_REFLECTIONS_SSR_COLOR;
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
float stepSize = ScreenSize.z; // 1 / screenWidth
float maxSamples = 32;
float worldAntiSelfOcclusionBias = 0.1f;
float brdfBias = 0.82f;
float drawDistance = 5000.0f;
float3 hit = TraceSceenSpaceReflection(screenUV, gBuffer, sceneDepthTexture, ViewPos, ViewMatrix, ViewProjectionMatrix, stepSize, maxSamples, false, 0.0f, worldAntiSelfOcclusionBias, brdfBias, drawDistance);
if (hit.z > 0)
{
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
reflections = lerp(reflections, screenColor, hit.z);
}
#endif
light.rgb += reflections * GetReflectionSpecularLighting(ViewPos, gBuffer) * light.a;
#endif #endif
// Add lighting (apply ambient occlusion) // Add lighting (apply ambient occlusion)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/SpriteMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/TexturePreviewMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Wires Debug Material.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Engine/DefaultMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/DefaultTerrainMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SingleColorMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SkyboxMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/SSR.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -689,6 +689,30 @@ namespace FlaxEditor.Content.GUI
return base.OnKeyDown(key); return base.OnKeyDown(key);
} }
/// <inheritdoc />
public override bool OnCharInput(char c)
{
if (base.OnCharInput(c))
return true;
if (char.IsLetterOrDigit(c) && _items.Count != 0)
{
// Jump to the item starting with this character
c = char.ToLowerInvariant(c);
for (int i = 0; i < _items.Count; i++)
{
var name = _items[i].ShortName;
if (!string.IsNullOrEmpty(name) && char.ToLowerInvariant(name[0]) == c)
{
Select(_items[i]);
break;
}
}
}
return false;
}
/// <inheritdoc /> /// <inheritdoc />
protected override void PerformLayoutBeforeChildren() protected override void PerformLayoutBeforeChildren()
{ {

View File

@@ -29,7 +29,7 @@ namespace FlaxEditor.CustomEditors
/// Enables using prefab-related features of the properties editor (eg. revert to prefab option). /// Enables using prefab-related features of the properties editor (eg. revert to prefab option).
/// </summary> /// </summary>
UsePrefab = 1 << 1, UsePrefab = 1 << 1,
/// <summary> /// <summary>
/// Enables using default-value-related features of the properties editor (eg. revert to default option). /// Enables using default-value-related features of the properties editor (eg. revert to default option).
/// </summary> /// </summary>
@@ -88,7 +88,6 @@ namespace FlaxEditor.CustomEditors
/// <seealso cref="FlaxEditor.CustomEditors.SyncPointEditor" /> /// <seealso cref="FlaxEditor.CustomEditors.SyncPointEditor" />
protected class RootEditor : SyncPointEditor protected class RootEditor : SyncPointEditor
{ {
private readonly string _noSelectionText;
private CustomEditor _overrideEditor; private CustomEditor _overrideEditor;
/// <summary> /// <summary>
@@ -109,13 +108,18 @@ namespace FlaxEditor.CustomEditors
} }
} }
/// <summary>
/// The text to show when no object is selected.
/// </summary>
public string NoSelectionText;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RootEditor"/> class. /// Initializes a new instance of the <see cref="RootEditor"/> class.
/// </summary> /// </summary>
/// <param name="noSelectionText">The text to show when no item is selected.</param> /// <param name="noSelectionText">The text to show when no item is selected.</param>
public RootEditor(string noSelectionText) public RootEditor(string noSelectionText)
{ {
_noSelectionText = noSelectionText ?? "No selection"; NoSelectionText = noSelectionText ?? "No selection";
} }
/// <summary> /// <summary>
@@ -154,7 +158,7 @@ namespace FlaxEditor.CustomEditors
} }
else else
{ {
var label = layout.Label(_noSelectionText, TextAlignment.Center); var label = layout.Label(NoSelectionText, TextAlignment.Center);
label.Label.Height = 20.0f; label.Label.Height = 20.0f;
} }
@@ -251,6 +255,20 @@ namespace FlaxEditor.CustomEditors
/// </summary> /// </summary>
public object Owner; public object Owner;
/// <summary>
/// Gets or sets the text to show when no object is selected.
/// </summary>
public string NoSelectionText
{
get => Editor.NoSelectionText;
set
{
Editor.NoSelectionText = value;
if (SelectionCount == 0)
BuildLayoutOnUpdate();
}
}
private bool _buildOnUpdate; private bool _buildOnUpdate;
/// <summary> /// <summary>

View File

@@ -49,10 +49,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = this, Parent = this,
Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, 18), Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, 18),
}; };
addScriptButton.ButtonClicked += AddScriptButtonOnClicked; addScriptButton.ButtonClicked += OnAddScriptButtonClicked;
} }
private void AddScriptButtonOnClicked(Button button) private void OnAddScriptButtonClicked(Button button)
{ {
var scripts = Editor.Instance.CodeEditing.Scripts.Get(); var scripts = Editor.Instance.CodeEditing.Scripts.Get();
if (scripts.Count == 0) if (scripts.Count == 0)

View File

@@ -716,7 +716,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Header // Header
if (item.Header != null) if (item.Header != null)
itemLayout.Header(item.Header.Text); itemLayout.Header(item.Header);
try try
{ {

View File

@@ -279,6 +279,16 @@ namespace FlaxEditor.CustomEditors
return element; return element;
} }
internal LabelElement Header(HeaderAttribute header)
{
var element = Header(header.Text);
if (header.FontSize != -1)
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
if (header.Color != 0)
element.Label.TextColor = Color.FromRGBA(header.Color);
return element;
}
/// <summary> /// <summary>
/// Adds new text box element. /// Adds new text box element.
/// </summary> /// </summary>

View File

@@ -258,6 +258,24 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1) NodeElementArchetype.Factory.Output(0, string.Empty, null, 1)
} }
}, },
new NodeArchetype
{
TypeID = 14,
Title = "Array Add Unique",
Description = "Adds the unique item to the array (to the end). Does nothing it specified item was already added.",
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
Size = new Vector2(170, 40),
ConnectionsHints = ConnectionsHint.Array,
IndependentBoxes = new int[] { 0 },
DependentBoxes = new int[] { 1, 2 },
DependentBoxFilter = GetArrayItemType,
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Array", true, null, 0),
NodeElementArchetype.Factory.Input(1, "Item", true, typeof(object), 1),
NodeElementArchetype.Factory.Output(0, string.Empty, null, 2)
}
},
// first 100 IDs reserved for arrays // first 100 IDs reserved for arrays
}; };
} }

View File

@@ -192,7 +192,7 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.SubsurfaceColor).Enabled = false; GetBox(MaterialNodeBoxes.SubsurfaceColor).Enabled = false;
break; break;
} }
default: throw new ArgumentOutOfRangeException(); default: throw new ArgumentOutOfRangeException();
} }
} }
@@ -807,7 +807,26 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(0, "RGB", true, typeof(Vector3), 0, 0), NodeElementArchetype.Factory.Input(0, "RGB", true, typeof(Vector3), 0, 0),
NodeElementArchetype.Factory.Output(0, "HSV", typeof(Vector3), 1), NodeElementArchetype.Factory.Output(0, "HSV", typeof(Vector3), 1),
} }
} },
new NodeArchetype
{
TypeID = 38,
Title = "Custom Global Code",
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(300, 220),
DefaultValues = new object[]
{
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
true,
},
Elements = new[]
{
NodeElementArchetype.Factory.Bool(0, 0, 1),
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
NodeElementArchetype.Factory.TextBox(0, 20, 300, 200, 0),
}
},
}; };
} }
} }

View File

@@ -317,7 +317,7 @@ namespace FlaxEditor.Surface
// Header // Header
if (e.Header != null) if (e.Header != null)
itemLayout.Header(e.Header.Text); itemLayout.Header(e.Header);
// Values container // Values container
var valueType = new ScriptType(e.Type); var valueType = new ScriptType(e.Type);

View File

@@ -410,7 +410,7 @@ namespace FlaxEditor.Surface
// Header // Header
var header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute); var header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute);
if (header != null) if (header != null)
itemLayout.Header(header.Text); itemLayout.Header(header);
var propertyValue = new CustomValueContainer var propertyValue = new CustomValueContainer
( (

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors;
using FlaxEditor.GUI; using FlaxEditor.GUI;
@@ -106,13 +107,36 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc /> /// <inheritdoc />
protected override void OnAssetLoaded() protected override void OnAssetLoaded()
{ {
_object = Asset.CreateInstance(); _object = Asset.Instance;
if (_object == null)
{
// Hint developer about cause of failure
var dataTypeName = Asset.DataTypeName;
var type = Type.GetType(dataTypeName);
if (type != null)
{
try
{
var obj = Activator.CreateInstance(type);
var data = Asset.Data;
FlaxEngine.Json.JsonSerializer.Deserialize(obj, data);
}
catch (Exception ex)
{
_presenter.NoSelectionText = "Failed to load asset. See log for more. " + ex.Message.Replace('\n', ' ');
}
}
else
{
_presenter.NoSelectionText = string.Format("Missing type '{0}'.", dataTypeName);
}
}
_presenter.Select(_object); _presenter.Select(_object);
_undo.Clear(); _undo.Clear();
ClearEditedFlag(); ClearEditedFlag();
// Auto-close on scripting reload if json asset is from game scripts (it might be reloaded) // Auto-close on scripting reload if json asset is from game scripts (it might be reloaded)
if (_object != null && FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType()) && !_isRegisteredForScriptsReload) if ((_object == null || FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType())) && !_isRegisteredForScriptsReload)
{ {
_isRegisteredForScriptsReload = true; _isRegisteredForScriptsReload = true;
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
@@ -121,6 +145,14 @@ namespace FlaxEditor.Windows.Assets
base.OnAssetLoaded(); base.OnAssetLoaded();
} }
/// <inheritdoc />
protected override void OnAssetLoadFailed()
{
_presenter.NoSelectionText = "Failed to load the asset.";
base.OnAssetLoadFailed();
}
/// <inheritdoc /> /// <inheritdoc />
public override void OnItemReimported(ContentItem item) public override void OnItemReimported(ContentItem item)
{ {

View File

@@ -77,6 +77,10 @@ namespace FlaxEditor.Windows.Assets
[EditorOrder(200), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")] [EditorOrder(200), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
public bool EnableReflections; public bool EnableReflections;
[VisibleIf(nameof(EnableReflections))]
[EditorOrder(210), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
public bool EnableScreenSpaceReflections;
[EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")] [EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
public bool EnableFog; public bool EnableFog;
@@ -142,6 +146,7 @@ namespace FlaxEditor.Windows.Assets
DepthTest = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthTest) == 0; DepthTest = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthTest) == 0;
DepthWrite = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthWrite) == 0; DepthWrite = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthWrite) == 0;
EnableReflections = (info.FeaturesFlags & MaterialFeaturesFlags.DisableReflections) == 0; EnableReflections = (info.FeaturesFlags & MaterialFeaturesFlags.DisableReflections) == 0;
EnableScreenSpaceReflections = (info.FeaturesFlags & MaterialFeaturesFlags.ScreenSpaceReflections) != 0;
EnableFog = (info.FeaturesFlags & MaterialFeaturesFlags.DisableFog) == 0; EnableFog = (info.FeaturesFlags & MaterialFeaturesFlags.DisableFog) == 0;
EnableDistortion = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDistortion) == 0; EnableDistortion = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDistortion) == 0;
PixelNormalOffsetRefraction = (info.FeaturesFlags & MaterialFeaturesFlags.PixelNormalOffsetRefraction) != 0; PixelNormalOffsetRefraction = (info.FeaturesFlags & MaterialFeaturesFlags.PixelNormalOffsetRefraction) != 0;
@@ -177,6 +182,8 @@ namespace FlaxEditor.Windows.Assets
info.FeaturesFlags |= MaterialFeaturesFlags.DisableDepthWrite; info.FeaturesFlags |= MaterialFeaturesFlags.DisableDepthWrite;
if (!EnableReflections) if (!EnableReflections)
info.FeaturesFlags |= MaterialFeaturesFlags.DisableReflections; info.FeaturesFlags |= MaterialFeaturesFlags.DisableReflections;
if (EnableScreenSpaceReflections)
info.FeaturesFlags |= MaterialFeaturesFlags.ScreenSpaceReflections;
if (!EnableFog) if (!EnableFog)
info.FeaturesFlags |= MaterialFeaturesFlags.DisableFog; info.FeaturesFlags |= MaterialFeaturesFlags.DisableFog;
if (!EnableDistortion) if (!EnableDistortion)

View File

@@ -111,6 +111,8 @@ void SlotBucketInit(AnimGraphInstanceData::Bucket& bucket)
bucket.Slot.TimePosition = 0.0f; bucket.Slot.TimePosition = 0.0f;
bucket.Slot.BlendInPosition = 0.0f; bucket.Slot.BlendInPosition = 0.0f;
bucket.Slot.BlendOutPosition = 0.0f; bucket.Slot.BlendOutPosition = 0.0f;
bucket.Slot.LoopsDone = 0;
bucket.Slot.LoopsLeft = 0;
} }
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n) bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)

View File

@@ -260,6 +260,7 @@ struct FLAXENGINE_API AnimGraphSlot
float Speed = 1.0f; float Speed = 1.0f;
float BlendInTime = 0.0f; float BlendInTime = 0.0f;
float BlendOutTime = 0.0f; float BlendOutTime = 0.0f;
int32 LoopCount = 0;
bool Pause = false; bool Pause = false;
}; };
@@ -314,6 +315,8 @@ public:
float TimePosition; float TimePosition;
float BlendInPosition; float BlendInPosition;
float BlendOutPosition; float BlendOutPosition;
int32 LoopsDone;
int32 LoopsLeft;
}; };
/// <summary> /// <summary>

View File

@@ -1924,6 +1924,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
bucket.TimePosition = 0.0f; bucket.TimePosition = 0.0f;
bucket.BlendInPosition = 0.0f; bucket.BlendInPosition = 0.0f;
bucket.BlendOutPosition = 0.0f; bucket.BlendOutPosition = 0.0f;
bucket.LoopsDone = 0;
bucket.LoopsLeft = slot.LoopCount;
break; break;
} }
} }
@@ -1940,18 +1942,27 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
ASSERT(slot.Animation && slot.Animation->IsLoaded()); ASSERT(slot.Animation && slot.Animation->IsLoaded());
const float deltaTime = slot.Pause ? 0.0f : context.DeltaTime * slot.Speed; const float deltaTime = slot.Pause ? 0.0f : context.DeltaTime * slot.Speed;
const float length = anim->GetLength(); const float length = anim->GetLength();
const bool loop = bucket.LoopsLeft != 0;
float newTimePos = bucket.TimePosition + deltaTime; float newTimePos = bucket.TimePosition + deltaTime;
if (newTimePos >= length) if (newTimePos >= length)
{ {
// End playing animation if (bucket.LoopsLeft == 0)
value = tryGetValue(node->GetBox(1), Value::Null); {
bucket.Index = -1; // End playing animation
slot.Animation = nullptr; value = tryGetValue(node->GetBox(1), Value::Null);
return; bucket.Index = -1;
slot.Animation = nullptr;
return;
}
// Loop animation
if (bucket.LoopsLeft > 0)
bucket.LoopsLeft--;
bucket.LoopsDone++;
} }
value = SampleAnimation(node, false, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed); value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed);
bucket.TimePosition = newTimePos; bucket.TimePosition = newTimePos;
if (slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition) if (bucket.LoopsLeft == 0 && slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition)
{ {
// Blend out // Blend out
auto input = tryGetValue(node->GetBox(1), Value::Null); auto input = tryGetValue(node->GetBox(1), Value::Null);
@@ -1959,7 +1970,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime); const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime);
value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic); value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic);
} }
else if (slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime) else if (bucket.LoopsDone == 0 && slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime)
{ {
// Blend in // Blend in
auto input = tryGetValue(node->GetBox(1), Value::Null); auto input = tryGetValue(node->GetBox(1), Value::Null);

View File

@@ -430,6 +430,8 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
options.Macros.Add({ "USE_DITHERED_LOD_TRANSITION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition ? 1 : 0] }); options.Macros.Add({ "USE_DITHERED_LOD_TRANSITION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition ? 1 : 0] });
options.Macros.Add({ "USE_GBUFFER_CUSTOM_DATA", Numbers[useCustomData ? 1 : 0] }); options.Macros.Add({ "USE_GBUFFER_CUSTOM_DATA", Numbers[useCustomData ? 1 : 0] });
options.Macros.Add({ "USE_REFLECTIONS", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections ? 0 : 1] }); options.Macros.Add({ "USE_REFLECTIONS", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections ? 0 : 1] });
if (!(info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections) && info.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections)
options.Macros.Add({ "MATERIAL_REFLECTIONS", Numbers[1]});
options.Macros.Add({ "USE_FOG", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableFog ? 0 : 1] }); options.Macros.Add({ "USE_FOG", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableFog ? 0 : 1] });
if (useForward) if (useForward)
options.Macros.Add({ "USE_PIXEL_NORMAL_OFFSET_REFRACTION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::PixelNormalOffsetRefraction ? 1 : 0] }); options.Macros.Add({ "USE_PIXEL_NORMAL_OFFSET_REFRACTION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::PixelNormalOffsetRefraction ? 1 : 0] });

View File

@@ -19,6 +19,8 @@
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MField.h"
#include "Engine/Utilities/StringConverter.h" #include "Engine/Utilities/StringConverter.h"
JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info) JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info)
@@ -212,13 +214,11 @@ Asset::LoadResult JsonAsset::loadAsset()
if (CreateInstance()) if (CreateInstance())
return LoadResult::Failed; return LoadResult::Failed;
#if USE_EDITOR #if USE_EDITOR
if (Instance) // Reload instance when module with this type gets reloaded
{ Level::ScriptsReloadStart.Bind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
// Reload instance when module with this type gets reloaded Level::ScriptsReloaded.Bind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
Level::ScriptsReloadStart.Bind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
Level::ScriptsReloaded.Bind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
}
#endif #endif
return LoadResult::Ok; return LoadResult::Ok;
@@ -226,14 +226,11 @@ Asset::LoadResult JsonAsset::loadAsset()
void JsonAsset::unload(bool isReloading) void JsonAsset::unload(bool isReloading)
{ {
if (Instance)
{
#if USE_EDITOR #if USE_EDITOR
Level::ScriptsReloadStart.Unbind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this); Level::ScriptsReloadStart.Unbind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
Level::ScriptsReloaded.Unbind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this); Level::ScriptsReloaded.Unbind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
#endif #endif
DeleteInstance(); DeleteInstance();
}
JsonAssetBase::unload(isReloading); JsonAssetBase::unload(isReloading);
} }
@@ -281,6 +278,13 @@ bool JsonAsset::CreateInstance()
void JsonAsset::DeleteInstance() void JsonAsset::DeleteInstance()
{ {
// C# instance
if (MObject* object = GetManagedInstance())
{
GetClass()->GetField("_instance")->SetValue(object, nullptr);
}
// C++ instance
if (!Instance || !_dtor) if (!Instance || !_dtor)
return; return;
_dtor(Instance); _dtor(Instance);

View File

@@ -7,27 +7,35 @@ namespace FlaxEngine
{ {
partial class JsonAsset partial class JsonAsset
{ {
private object _instance;
/// <summary> /// <summary>
/// Creates the serialized object instance from the json asset data. /// Gets the instance of the serialized object from the json asset data. Cached internally.
/// </summary> /// </summary>
/// <returns>The created object or null.</returns> public object Instance => _instance ?? (_instance = CreateInstance());
/// <summary>
/// Creates a new instance of the serialized object from the json asset data.
/// </summary>
/// <remarks>Use <see cref="Instance"/> to get cached object.</remarks>
/// <returns>The new object or null if failed.</returns>
public T CreateInstance<T>() public T CreateInstance<T>()
{ {
return (T)CreateInstance(); return (T)CreateInstance();
} }
/// <summary> /// <summary>
/// Creates the serialized object instance from the json asset data. /// Creates a new instance of the serialized object from the json asset data.
/// </summary> /// </summary>
/// <returns>The created object or null.</returns> /// <remarks>Use <see cref="Instance"/> to get cached object.</remarks>
/// <returns>The new object or null if failed.</returns>
public object CreateInstance() public object CreateInstance()
{ {
if (WaitForLoaded()) if (WaitForLoaded())
return null; return null;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var dataTypeName = DataTypeName; var dataTypeName = DataTypeName;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++) for (int i = 0; i < assemblies.Length; i++)
{ {
var assembly = assemblies[i]; var assembly = assemblies[i];
@@ -48,14 +56,14 @@ namespace FlaxEngine
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogException(ex); Debug.LogException(ex, this);
} }
return obj; return obj;
} }
} }
} }
Debug.LogError(string.Format("Missing type '{0}' to create Json Asset instance.", dataTypeName), this);
return null; return null;
} }
} }

View File

@@ -123,7 +123,7 @@ CreateAssetResult CreateMaterial::Create(CreateAssetContext& context)
if (context.AllocateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE)) if (context.AllocateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE))
return CreateAssetResult::CannotAllocateChunk; return CreateAssetResult::CannotAllocateChunk;
layer->Graph.Nodes.EnsureCapacity(32); layer->Graph.Nodes.EnsureCapacity(32);
layer->Root = (MaterialGraphNode*)&layer->Graph.Nodes[0]; layer->Root = &layer->Graph.Nodes[0];
for (auto& box : layer->Root->Boxes) for (auto& box : layer->Root->Boxes)
box.Parent = layer->Root; box.Parent = layer->Root;
Meta11 meta; Meta11 meta;

View File

@@ -202,7 +202,7 @@ namespace FlaxEditor.Content.Settings
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath); var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath);
if (asset && !asset.WaitForLoaded()) if (asset && !asset.WaitForLoaded())
{ {
if (asset.CreateInstance() is GameSettings result) if (asset.Instance is GameSettings result)
return result; return result;
} }
return new GameSettings(); return new GameSettings();
@@ -212,7 +212,7 @@ namespace FlaxEditor.Content.Settings
{ {
if (asset && !asset.WaitForLoaded()) if (asset && !asset.WaitForLoaded())
{ {
if (asset.CreateInstance() is T result) if (asset.Instance is T result)
return result; return result;
} }
return new T(); return new T();
@@ -222,7 +222,7 @@ namespace FlaxEditor.Content.Settings
{ {
if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename) if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename)
{ {
if (asset.CreateInstance() is SettingsBase result) if (asset.Instance is SettingsBase result)
return result; return result;
} }
return null; return null;
@@ -314,7 +314,7 @@ namespace FlaxEditor.Content.Settings
{ {
if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName) if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName)
{ {
var custom = e.Value.CreateInstance(); var custom = e.Value.Instance;
if (custom is T result) if (custom is T result)
return result; return result;
} }

View File

@@ -405,6 +405,59 @@ public:
} }
} }
/// <summary>
/// Binds a static function (if not binded yet).
/// </summary>
template<void(*Method)(Params ...)>
void BindUnique()
{
FunctionType f;
f.template Bind<Method>();
BindUnique(f);
}
/// <summary>
/// Binds a member function (if not binded yet).
/// </summary>
/// <param name="callee">The object instance.</param>
template<class T, void(T::*Method)(Params ...)>
void BindUnique(T* callee)
{
FunctionType f;
f.template Bind<T, Method>(callee);
BindUnique(f);
}
/// <summary>
/// Binds a function (if not binded yet).
/// </summary>
/// <param name="method">The method.</param>
void BindUnique(Signature method)
{
FunctionType f(method);
BindUnique(f);
}
/// <summary>
/// Binds a function (if not binded yet).
/// </summary>
/// <param name="f">The function to bind.</param>
void BindUnique(const FunctionType& f)
{
const intptr size = Platform::AtomicRead(&_size);
FunctionType* bindings = (FunctionType*)Platform::AtomicRead(&_ptr);
if (bindings)
{
// Skip if already binded
for (intptr i = 0; i < size; i++)
{
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._callee) == (intptr)f._callee && Platform::AtomicRead((intptr volatile*)&bindings[i]._function) == (intptr)f._function)
return;
}
}
Bind(f);
}
/// <summary> /// <summary>
/// Unbinds a static function. /// Unbinds a static function.
/// </summary> /// </summary>

View File

@@ -230,6 +230,20 @@ namespace FlaxEngine
a); a);
} }
/// <summary>
/// Creates <see cref="Color"/> from the RGBA value.
/// </summary>
/// <param name="rgb">The packed RGBA value.</param>
/// <returns>The color.</returns>
public static Color FromRGBA(uint rgb)
{
return new Color(
((rgb >> 16) & 0xff) / 255.0f,
((rgb >> 8) & 0xff) / 255.0f,
(rgb & 0xff) / 255.0f,
((rgb >> 24) & 0xff) / 255.0f);
}
/// <summary> /// <summary>
/// Gets the color value as the hexadecimal string. /// Gets the color value as the hexadecimal string.
/// </summary> /// </summary>

View File

@@ -70,7 +70,7 @@ void ForwardMaterialShader::Bind(BindParameters& params)
MaterialParameter::BindMeta bindMeta; MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context; bindMeta.Context = context;
bindMeta.Constants = cb; bindMeta.Constants = cb;
bindMeta.Input = nullptr; // forward pass materials cannot sample scene color for now bindMeta.Input = params.Input;
bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.Buffers = params.RenderContext.Buffers;
bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth;
bindMeta.CanSampleGBuffer = true; bindMeta.CanSampleGBuffer = true;

View File

@@ -272,6 +272,11 @@ API_ENUM(Attributes="Flags") enum class MaterialFeaturesFlags : uint32
/// The flag used to enable refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces. /// The flag used to enable refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.
/// </summary> /// </summary>
PixelNormalOffsetRefraction = 1 << 9, PixelNormalOffsetRefraction = 1 << 9,
/// <summary>
/// The flag used to enable high-quality reflections based on the screen space raytracing. Useful for large water-like surfaces. The Forward Pass materials option.
/// </summary>
ScreenSpaceReflections = 1 << 10,
}; };
DECLARE_ENUM_OPERATORS(MaterialFeaturesFlags); DECLARE_ENUM_OPERATORS(MaterialFeaturesFlags);

View File

@@ -406,11 +406,13 @@ void MaterialParameter::Bind(BindMeta& meta) const
{ {
case MaterialSceneTextures::SceneDepth: case MaterialSceneTextures::SceneDepth:
view = meta.CanSampleDepth view = meta.CanSampleDepth
? (GPUDevice::Instance->Limits.HasReadOnlyDepth ? meta.Buffers->DepthBuffer->ViewReadOnlyDepth() : meta.Buffers->DepthBuffer->View()) ? meta.Buffers->DepthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? meta.Buffers->DepthBuffer->ViewReadOnlyDepth() : meta.Buffers->DepthBuffer->View()
: GPUDevice::Instance->GetDefaultWhiteTexture()->View(); : GPUDevice::Instance->GetDefaultWhiteTexture()->View();
break; break;
case MaterialSceneTextures::AmbientOcclusion: case MaterialSceneTextures::AmbientOcclusion:
case MaterialSceneTextures::BaseColor: case MaterialSceneTextures::BaseColor:
case MaterialSceneTextures::DiffuseColor:
case MaterialSceneTextures::SpecularColor:
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer0->View() : nullptr; view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer0->View() : nullptr;
break; break;
case MaterialSceneTextures::WorldNormal: case MaterialSceneTextures::WorldNormal:

View File

@@ -10,7 +10,7 @@
/// <summary> /// <summary>
/// Current materials shader version. /// Current materials shader version.
/// </summary> /// </summary>
#define MATERIAL_GRAPH_VERSION 150 #define MATERIAL_GRAPH_VERSION 151
class Material; class Material;
class GPUShader; class GPUShader;

View File

@@ -419,6 +419,17 @@ Array<Actor*> Actor::GetChildren(const MClass* type) const
return result; return result;
} }
void Actor::DestroyChildren(float timeLeft)
{
Array<Actor*> children = Children;
const bool useGameTime = timeLeft > ZeroTolerance;
for (Actor* child : children)
{
child->SetParent(nullptr, false, false);
child->DeleteObject(timeLeft, useGameTime);
}
}
bool Actor::HasTag(const StringView& tag) const bool Actor::HasTag(const StringView& tag) const
{ {
return HasTag() && tag == Level::Tags[_tag]; return HasTag() && tag == Level::Tags[_tag];

View File

@@ -296,23 +296,6 @@ namespace FlaxEngine
return output; return output;
} }
/// <summary>
/// Destroys the children. Calls Object.Destroy on every child actor and unlink them for the parent.
/// </summary>
/// <param name="timeLeft">The time left to destroy object (in seconds).</param>
[NoAnimate]
public void DestroyChildren(float timeLeft = 0.0f)
{
if (ChildrenCount == 0)
return;
Actor[] children = Children;
for (var i = 0; i < children.Length; i++)
{
children[i].Parent = null;
Destroy(children[i], timeLeft);
}
}
/// <summary> /// <summary>
/// Gets the matrix that transforms a point from the world space to local space of the actor. /// Gets the matrix that transforms a point from the world space to local space of the actor.
/// </summary> /// </summary>

View File

@@ -244,6 +244,12 @@ public:
return result; return result;
} }
/// <summary>
/// Destroys the children. Calls Object.Destroy on every child actor and unlinks them for this actor.
/// </summary>
/// <param name="timeLeft">The time left to destroy object (in seconds).</param>
API_FUNCTION(Attributes="NoAnimate") void DestroyChildren(float timeLeft = 0.0f);
public: public:
/// <summary> /// <summary>

View File

@@ -325,7 +325,7 @@ void AnimatedModel::ClearBlendShapeWeights()
_blendShapes.Clear(); _blendShapes.Clear();
} }
void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed, float blendInTime, float blendOutTime) void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed, float blendInTime, float blendOutTime, int32 loopCount)
{ {
CHECK(anim); CHECK(anim);
for (auto& slot : GraphInstance.Slots) for (auto& slot : GraphInstance.Slots)
@@ -334,6 +334,7 @@ void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* ani
{ {
slot.Pause = false; slot.Pause = false;
slot.BlendInTime = blendInTime; slot.BlendInTime = blendInTime;
slot.LoopCount = loopCount;
return; return;
} }
} }
@@ -351,6 +352,7 @@ void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* ani
slot.Speed = speed; slot.Speed = speed;
slot.BlendInTime = blendInTime; slot.BlendInTime = blendInTime;
slot.BlendOutTime = blendOutTime; slot.BlendOutTime = blendOutTime;
slot.LoopCount = loopCount;
} }
void AnimatedModel::StopSlotAnimation() void AnimatedModel::StopSlotAnimation()

View File

@@ -313,7 +313,8 @@ public:
/// <param name="speed">The playback speed.</param> /// <param name="speed">The playback speed.</param>
/// <param name="blendInTime">The animation blending in time (in seconds). Cam be used to smooth the slot animation playback with the input pose when starting the animation.</param> /// <param name="blendInTime">The animation blending in time (in seconds). Cam be used to smooth the slot animation playback with the input pose when starting the animation.</param>
/// <param name="blendOutTime">The animation blending out time (in seconds). Cam be used to smooth the slot animation playback with the input pose when ending animation.</param> /// <param name="blendOutTime">The animation blending out time (in seconds). Cam be used to smooth the slot animation playback with the input pose when ending animation.</param>
API_FUNCTION() void PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed = 1.0f, float blendInTime = 0.2f, float blendOutTime = 0.2f); /// <param name="loopCount">The amount of loops to play the animation: 0 to play once, -1 to play infinite, 1 or higher to loop once or more.</param>
API_FUNCTION() void PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed = 1.0f, float blendInTime = 0.2f, float blendOutTime = 0.2f, int32 loopCount = 0);
/// <summary> /// <summary>
/// Stops all the animations playback on the all slots in Anim Graph. /// Stops all the animations playback on the all slots in Anim Graph.

View File

@@ -96,7 +96,7 @@ namespace FlaxEngine
/// <summary> /// <summary>
/// Gets the font object described by the structure. /// Gets the font object described by the structure.
/// </summary> /// </summary>
/// <returns>Th font or null if descriptor is invalid.</returns> /// <returns>The font or null if descriptor is invalid.</returns>
public Font GetFont() public Font GetFont()
{ {
if (_cachedFont) if (_cachedFont)

View File

@@ -74,10 +74,7 @@ void ForwardPass::Dispose()
void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
{ {
PROFILE_GPU_CPU("Forward"); PROFILE_GPU_CPU("Forward");
auto context = GPUDevice::Instance->GetMainContext();
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View; auto& view = renderContext.View;
auto mainCache = renderContext.List; auto mainCache = renderContext.List;
@@ -141,6 +138,6 @@ void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTex
// Run forward pass // Run forward pass
view.Pass = DrawPass::Forward; view.Pass = DrawPass::Forward;
context->SetRenderTarget(depthBufferHandle, output->View()); context->SetRenderTarget(depthBufferHandle, output->View());
mainCache->ExecuteDrawCalls(renderContext, forwardList); mainCache->ExecuteDrawCalls(renderContext, forwardList, input->View());
} }
} }

View File

@@ -554,7 +554,7 @@ bool CanUseInstancing(DrawPass pass)
return pass == DrawPass::GBuffer || pass == DrawPass::Depth; return pass == DrawPass::GBuffer || pass == DrawPass::Depth;
} }
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list) void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input)
{ {
if (list.IsEmpty()) if (list.IsEmpty())
return; return;
@@ -626,6 +626,7 @@ DRAW:
// Execute draw calls // Execute draw calls
MaterialBase::BindParameters bindParams(context, renderContext); MaterialBase::BindParameters bindParams(context, renderContext);
bindParams.Input = input;
if (useInstancing) if (useInstancing)
{ {
int32 instanceBufferOffset = 0; int32 instanceBufferOffset = 0;

View File

@@ -570,9 +570,10 @@ public:
/// </summary> /// </summary>
/// <param name="renderContext">The rendering context.</param> /// <param name="renderContext">The rendering context.</param>
/// <param name="listType">The collected draw calls list type.</param> /// <param name="listType">The collected draw calls list type.</param>
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType) /// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType, GPUTextureView* input = nullptr)
{ {
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType]); ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType], input);
} }
/// <summary> /// <summary>
@@ -580,7 +581,8 @@ public:
/// </summary> /// </summary>
/// <param name="renderContext">The rendering context.</param> /// <param name="renderContext">The rendering context.</param>
/// <param name="list">The collected draw calls list.</param> /// <param name="list">The collected draw calls list.</param>
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list); /// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input = nullptr);
}; };
/// <summary> /// <summary>

View File

@@ -448,8 +448,9 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
} }
// Run forward pass // Run forward pass
auto forwardPassResult = renderContext.Buffers->RT1_FloatRGB; GPUTexture* frameBuffer = renderContext.Buffers->RT1_FloatRGB;
ForwardPass::Instance()->Render(renderContext, lightBuffer, forwardPassResult); GPUTexture* tempBuffer = renderContext.Buffers->RT2_FloatRGB;
ForwardPass::Instance()->Render(renderContext, lightBuffer, frameBuffer);
// Cleanup // Cleanup
context->ResetRenderTarget(); context->ResetRenderTarget();
@@ -462,16 +463,10 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
{ {
context->SetRenderTarget(task->GetOutputView()); context->SetRenderTarget(task->GetOutputView());
context->SetViewportAndScissors(task->GetOutputViewport()); context->SetViewportAndScissors(task->GetOutputViewport());
context->Draw(forwardPassResult); context->Draw(frameBuffer);
return; return;
} }
// Prepare buffers for post processing frame
GPUTexture* frameBuffer = renderContext.Buffers->RT1_FloatRGB;
GPUTexture* tempBuffer = renderContext.Buffers->RT2_FloatRGB;
if (forwardPassResult == tempBuffer)
Swap(frameBuffer, tempBuffer);
// Material and Custom PostFx // Material and Custom PostFx
renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::BeforePostProcessingPass, frameBuffer, tempBuffer); renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);
renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::BeforePostProcessingPass, frameBuffer, tempBuffer); renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);

View File

@@ -42,13 +42,10 @@ PACK_STRUCT(struct Data
float TemporalScale; float TemporalScale;
float RayTraceStep; float RayTraceStep;
float NoTemporalEffect; float TemporalEffect;
float Intensity; float Intensity;
float FadeOutDistance; float FadeOutDistance;
Vector3 Dummy0;
float InvFadeDistance;
Matrix ViewMatrix; Matrix ViewMatrix;
Matrix ViewProjectionMatrix; Matrix ViewProjectionMatrix;
}); });
@@ -248,11 +245,10 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
data.MaxColorMiplevel = settings.UseColorBufferMips ? (float)colorBufferMips - 2.0f : 0.0f; data.MaxColorMiplevel = settings.UseColorBufferMips ? (float)colorBufferMips - 2.0f : 0.0f;
data.RayTraceStep = static_cast<float>(settings.DepthResolution) / (float)width; data.RayTraceStep = static_cast<float>(settings.DepthResolution) / (float)width;
data.Intensity = settings.Intensity; data.Intensity = settings.Intensity;
data.FadeOutDistance = settings.FadeOutDistance; data.FadeOutDistance = Math::Max(settings.FadeOutDistance, 100.0f);
data.InvFadeDistance = 1.0f / settings.FadeDistance;
data.TemporalScale = settings.TemporalScale; data.TemporalScale = settings.TemporalScale;
data.TemporalResponse = settings.TemporalResponse; data.TemporalResponse = settings.TemporalResponse;
data.NoTemporalEffect = useTemporal ? 0.0f : 1.0f; data.TemporalEffect = useTemporal ? 1.0f : 0.0f;
if (useTemporal) if (useTemporal)
{ {
const float time = Time::Draw.UnscaledTime.GetTotalSeconds(); const float time = Time::Draw.UnscaledTime.GetTotalSeconds();

View File

@@ -17,6 +17,16 @@ namespace FlaxEngine
/// </summary> /// </summary>
public string Text; public string Text;
/// <summary>
/// The custom header font size.
/// </summary>
public int FontSize;
/// <summary>
/// The custom header color (as 32-bit uint).
/// </summary>
public uint Color;
private HeaderAttribute() private HeaderAttribute()
{ {
} }
@@ -25,9 +35,13 @@ namespace FlaxEngine
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class. /// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
/// </summary> /// </summary>
/// <param name="text">The header text.</param> /// <param name="text">The header text.</param>
public HeaderAttribute(string text) /// <param name="fontSize">The header text font size (-1 to use default which is 14).</param>
/// <param name="color">The header color (as 32-bit uint, 0 to use default).</param>
public HeaderAttribute(string text, int fontSize = -1, uint color = 0)
{ {
Text = text; Text = text;
FontSize = fontSize;
Color = color;
} }
} }
} }

View File

@@ -27,7 +27,7 @@ namespace FlaxEngine
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class. /// Initializes a new instance of the <see cref="VisibleIfAttribute"/> class.
/// </summary> /// </summary>
/// <param name="memberName">The name of the field or property of the object. Must be a bool type.</param> /// <param name="memberName">The name of the field or property of the object. Must be a bool type.</param>
/// <param name="invert">True if invert member value when computing the visibility value.</param> /// <param name="invert">True if invert member value when computing the visibility value.</param>

View File

@@ -9,7 +9,7 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B
ASSERT(texture && box); ASSERT(texture && box);
// Cache data // Cache data
const auto parent = box->GetParent<MaterialGraphNode>(); const auto parent = box->GetParent<ShaderGraphNode<>>();
const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture; const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture;
const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray; const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray;
const bool isVolume = texture->Type == MaterialParameterType::GPUTextureVolume; const bool isVolume = texture->Type == MaterialParameterType::GPUTextureVolume;

View File

@@ -163,7 +163,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
// Cache data // Cache data
MaterialLayer* baseLayer = GetRootLayer(); MaterialLayer* baseLayer = GetRootLayer();
MaterialGraphNode* baseNode = baseLayer->Root; auto* baseNode = baseLayer->Root;
_treeLayerVarName = baseLayer->GetVariableName(nullptr); _treeLayerVarName = baseLayer->GetVariableName(nullptr);
_treeLayer = baseLayer; _treeLayer = baseLayer;
_graphStack.Add(&_treeLayer->Graph); _graphStack.Add(&_treeLayer->Graph);
@@ -398,6 +398,14 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
_writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold); _writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold);
_writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count()); _writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count());
_writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold); _writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && !(materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableReflections) && materialInfo.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections)
{
// Inject depth and color buffers for Screen Space Reflections used by transparent material
auto sceneDepthTexture = findOrAddSceneTexture(MaterialSceneTextures::SceneDepth);
auto sceneColorTexture = findOrAddSceneTexture(MaterialSceneTextures::SceneColor);
_writer.Write(TEXT("#define MATERIAL_REFLECTIONS_SSR_DEPTH ({0})\n"), sceneDepthTexture.ShaderName);
_writer.Write(TEXT("#define MATERIAL_REFLECTIONS_SSR_COLOR ({0})\n"), sceneColorTexture.ShaderName);
}
WRITE_FEATURES(Defines); WRITE_FEATURES(Defines);
inputs[In_Defines] = _writer.ToString(); inputs[In_Defines] = _writer.ToString();
_writer.Clear(); _writer.Clear();
@@ -447,6 +455,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
} }
for (auto f : features) for (auto f : features)
{ {
// Process SRV slots used in template
const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources]; const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources];
const Char* str = text.Get(); const Char* str = text.Get();
int32 prevIdx = 0, idx = 0; int32 prevIdx = 0, idx = 0;
@@ -483,6 +492,21 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
// Utilities // Utilities
{ {
WRITE_FEATURES(Utilities); WRITE_FEATURES(Utilities);
Array<Graph*, InlinedAllocation<8>> graphs;
_functions.GetValues(graphs);
for (MaterialLayer* layer : _layers)
graphs.Add(&layer->Graph);
for (Graph* graph : graphs)
{
for (const MaterialGraph::Node& node : graph->Nodes)
{
if (node.Type == GRAPH_NODE_MAKE_TYPE(1, 38) && (bool)node.Values[1])
{
// Custom Global Code
_writer.Write((StringView)node.Values[0]);
}
}
}
inputs[In_Utilities] = _writer.ToString(); inputs[In_Utilities] = _writer.ToString();
_writer.Clear(); _writer.Clear();
} }

View File

@@ -50,7 +50,7 @@ void MaterialLayer::Prepare()
const auto node = &Graph.Nodes[i]; const auto node = &Graph.Nodes[i];
if (node->Type == ROOT_NODE_TYPE) if (node->Type == ROOT_NODE_TYPE)
{ {
Root = (MaterialGraphNode*)node; Root = node;
break; break;
} }
} }
@@ -159,7 +159,7 @@ MaterialLayer* MaterialLayer::Load(const Guid& id, ReadStream* graphData, const
{ {
if (layer->Graph.Nodes[i].Type == ROOT_NODE_TYPE) if (layer->Graph.Nodes[i].Type == ROOT_NODE_TYPE)
{ {
layer->Root = (MaterialGraphNode*)&layer->Graph.Nodes[i]; layer->Root = &layer->Graph.Nodes[i];
break; break;
} }
} }
@@ -233,7 +233,7 @@ void MaterialLayer::createRootNode()
#undef INIT_BOX #undef INIT_BOX
// Mark as root // Mark as root
Root = (MaterialGraphNode*)&rootNode; Root = &rootNode;
} }
#endif #endif

View File

@@ -50,7 +50,7 @@ public:
/// <summary> /// <summary>
/// Root node /// Root node
/// </summary> /// </summary>
MaterialGraphNode* Root; MaterialGraph::Node* Root;
/// <summary> /// <summary>
/// Material structure variable name (different for every layer sampling with different UVs, default UVs are a first index) /// Material structure variable name (different for every layer sampling with different UVs, default UVs are a first index)

View File

@@ -7,10 +7,6 @@
#include "Engine/Core/Enums.h" #include "Engine/Core/Enums.h"
#include "Engine/Visject/ShaderGraph.h" #include "Engine/Visject/ShaderGraph.h"
class MaterialGraphNode : public ShaderGraphNode<>
{
};
class MaterialGraph : public ShaderGraph<> class MaterialGraph : public ShaderGraph<>
{ {
}; };

View File

@@ -47,7 +47,7 @@ namespace FlaxEngine.Utilities
/// Requests the mesh data. /// Requests the mesh data.
/// </summary> /// </summary>
/// <param name="model">The model to get it's data.</param> /// <param name="model">The model to get it's data.</param>
/// <returns>True if ahs valid data to access, otherwise false if it's during downloading.</returns> /// <returns>True if has valid data to access, otherwise false if it's during downloading.</returns>
public bool RequestMeshData(Model model) public bool RequestMeshData(Model model)
{ {
if (model == null) if (model == null)

View File

@@ -1350,6 +1350,13 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
array.Reverse(); array.Reverse();
value = MoveTemp(v); value = MoveTemp(v);
break; break;
// Add Unique
case 14:
b = node->GetBox(1);
ENSURE(b->HasConnection(), TEXT("Missing value to add."));
array.AddUnique(eatBox(b->GetParent<Node>(), b->FirstConnection()));
value = MoveTemp(v);
break;
} }
} }
} }

View File

@@ -42,18 +42,13 @@ float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, ProbeData data,
return probeSample * fade; return probeSample * fade;
} }
float3 GetEnvProbeLighting(float3 viewPos, TextureCube probe, ProbeData data, GBufferSample gBuffer) // Calculates the reflective environment lighting to multiply the raw reflection color for the specular light (eg. from Env Probe or SSR).
float3 GetReflectionSpecularLighting(float3 viewPos, GBufferSample gBuffer)
{ {
// Calculate reflections
float3 reflections = SampleReflectionProbe(viewPos, probe, data, gBuffer.WorldPos, gBuffer.Normal, gBuffer.Roughness).rgb;
// Calculate specular color
float3 specularColor = GetSpecularColor(gBuffer); float3 specularColor = GetSpecularColor(gBuffer);
// Calculate reflecion color
float3 V = normalize(viewPos - gBuffer.WorldPos); float3 V = normalize(viewPos - gBuffer.WorldPos);
float NoV = saturate(dot(gBuffer.Normal, V)); float NoV = saturate(dot(gBuffer.Normal, V));
return reflections * EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV); return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV);
} }
#endif #endif

154
Source/Shaders/SSR.hlsl Normal file
View File

@@ -0,0 +1,154 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
#include "./Flax/BRDF.hlsl"
#include "./Flax/Random.hlsl"
#include "./Flax/MonteCarlo.hlsl"
#include "./Flax/GBufferCommon.hlsl"
float max2(float2 v)
{
return max(v.x, v.y);
}
float2 RandN2(float2 pos, float2 random)
{
return frac(sin(dot(pos.xy + random, float2(12.9898, 78.233))) * float2(43758.5453, 28001.8384));
}
// 1:-1 to 0:1
float2 ClipToUv(float2 clipPos)
{
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
}
// go into clip space (-1:1 from bottom/left to up/right)
float3 ProjectWorldToClip(float3 wsPos, float4x4 viewProjectionMatrix)
{
float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix);
return clipPos.xyz / clipPos.w;
}
// go into UV space. (0:1 from top/left to bottom/right)
float3 ProjectWorldToUv(float3 wsPos, float4x4 viewProjectionMatrix)
{
float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix);
return float3(ClipToUv(clipPos.xy), clipPos.z);
}
float3 TangentToWorld(float3 N, float4 H)
{
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
float3 T = normalize(cross(upVector, N));
float3 B = cross(N, T);
return float3((T * H.x) + (B * H.y) + (N * H.z));
}
float RayAttenBorder(float2 pos, float value)
{
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
return saturate(borderDist > value ? 1.0 : borderDist / value);
}
// Screen Space Reflection ray tracing utility.
// Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit).
float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f)
{
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance)
return 0;
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
// Randomize it a little
float2 jitter = RandN2(uv, temporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
// Calculate normalized view space reflection vector
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
return 0;
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
float3 reflectWS = reflect(viewWS, H.xyz);
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias;
float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix);
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);
float3 rayUV = endUV - startUV;
rayUV *= stepSize / max2(abs(rayUV.xy));
float3 startUv = startUV + rayUV * 2;
float3 currOffset = startUv;
float3 rayStep = rayUV * 2;
// Calculate number of samples
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
float numSamples = min(maxSamples, samplesToEdge.x);
rayStep *= samplesToEdge.x / numSamples;
// Calculate depth difference error
float depthDiffError = 1.3f * abs(rayStep.z);
// Ray trace
float currSampleIndex = 0;
float currSample, depthDiff;
LOOP
while (currSampleIndex < numSamples)
{
// Sample depth buffer and calculate depth difference
currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r;
depthDiff = currOffset.z - currSample;
// Check intersection
if (depthDiff >= 0)
{
if (depthDiff < depthDiffError)
{
break;
}
else
{
currOffset -= rayStep;
rayStep *= 0.5;
}
}
// Move forward
currOffset += rayStep;
currSampleIndex++;
}
// Check if has valid result after ray tracing
if (currSampleIndex >= numSamples)
{
// All samples done but no result
return 0;
}
float2 hitUV = currOffset.xy;
// Fade rays close to screen edge
const float fadeStart = 0.9f;
const float fadeEnd = 1.0f;
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
fadeOnBorder *= RayAttenBorder(hitUV, edgeFade);
// Fade rays on high roughness
float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20);
// Fade on distance
float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance);
// Output: xy: hitUV, z: hitMask
return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade);
}

View File

@@ -1,13 +1,10 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl" #include "./Flax/Common.hlsl"
#include "./Flax/BRDF.hlsl"
#include "./Flax/Random.hlsl"
#include "./Flax/MonteCarlo.hlsl"
#include "./Flax/LightingCommon.hlsl" #include "./Flax/LightingCommon.hlsl"
#include "./Flax/GBuffer.hlsl"
#include "./Flax/ReflectionsCommon.hlsl" #include "./Flax/ReflectionsCommon.hlsl"
#include "./Flax/BRDF.hlsl" #include "./Flax/SSR.hlsl"
#include "./Flax/GBuffer.hlsl"
// Enable/disable luminance filter to reduce reflections highlights // Enable/disable luminance filter to reduce reflections highlights
#define SSR_REDUCE_HIGHLIGHTS 1 #define SSR_REDUCE_HIGHLIGHTS 1
@@ -34,13 +31,10 @@ float TemporalResponse;
float TemporalScale; float TemporalScale;
float RayTraceStep; float RayTraceStep;
float NoTemporalEffect; float TemporalEffect;
float Intensity; float Intensity;
float FadeOutDistance; float FadeOutDistance;
float3 Dummy0;
float InvFadeDistance;
float4x4 ViewMatrix; float4x4 ViewMatrix;
float4x4 ViewProjectionMatrix; float4x4 ViewProjectionMatrix;
@@ -52,50 +46,6 @@ Texture2D Texture0 : register(t4);
Texture2D Texture1 : register(t5); Texture2D Texture1 : register(t5);
Texture2D Texture2 : register(t6); Texture2D Texture2 : register(t6);
float max2(float2 v)
{
return max(v.x, v.y);
}
float2 RandN2(float2 pos, float2 random)
{
return frac(sin(dot(pos.xy + random, float2(12.9898, 78.233))) * float2(43758.5453, 28001.8384));
}
// 1:-1 to 0:1
float2 ClipToUv(float2 clipPos)
{
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
}
// go into clip space (-1:1 from bottom/left to up/right)
float3 ProjectWorldToClip(float3 wsPos)
{
float4 clipPos = mul(float4(wsPos, 1), ViewProjectionMatrix);
return clipPos.xyz / clipPos.w;
}
// go into UV space. (0:1 from top/left to bottom/right)
float3 ProjectWorldToUv(float3 wsPos)
{
float3 clipPos = ProjectWorldToClip(wsPos);
return float3(ClipToUv(clipPos.xy), clipPos.z);
}
float4 TangentToWorld(float3 N, float4 H)
{
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
float3 T = normalize(cross(upVector, N));
float3 B = cross(N, T);
return float4((T * H.x) + (B * H.y) + (N * H.z), H.w);
}
float RayAttenBorder(float2 pos, float value)
{
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
return saturate(borderDist > value ? 1.0 : borderDist / value);
}
// Pixel Shader for screen space reflections rendering - combine pass // Pixel Shader for screen space reflections rendering - combine pass
META_PS(true, FEATURE_LEVEL_ES2) META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0 float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
@@ -135,112 +85,15 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
META_PS(true, FEATURE_LEVEL_ES2) META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0 float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
{ {
float2 uv = input.TexCoord;
// Sample GBuffer // Sample GBuffer
GBufferData gBufferData = GetGBufferData(); GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv); GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
// Reject invalid pixels // Trace depth buffer to find intersection
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > RoughnessFade || gBuffer.ViewPos.z > FadeOutDistance) float3 hit = TraceSceenSpaceReflection(input.TexCoord, gBuffer, Depth, gBufferData.ViewPos, ViewMatrix, ViewProjectionMatrix, RayTraceStep, MaxTraceSamples, TemporalEffect, TemporalTime, WorldAntiSelfOcclusionBias, BRDFBias, FadeOutDistance, RoughnessFade, EdgeFadeFactor);
return 0;
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)ViewMatrix);
// Randomize it a little
float2 jitter = RandN2(uv, TemporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, BRDFBias);
float4 H = TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness));
if (NoTemporalEffect)
H.xyz = gBuffer.Normal;
// Calculate normalized view space reflection vector
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
return 0;
float3 viewWS = normalize(gBuffer.WorldPos - GBuffer.ViewPos);
float3 reflectWS = reflect(viewWS, H.xyz);
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * WorldAntiSelfOcclusionBias;
float3 startUV = ProjectWorldToUv(startWS);
float3 endUV = ProjectWorldToUv(startWS + reflectWS);
float3 rayUV = endUV - startUV;
rayUV *= RayTraceStep / max2(abs(rayUV.xy));
float3 startUv = startUV + rayUV * 2;
float3 currOffset = startUv;
float3 rayStep = rayUV * 2;
// Calculate number of samples
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
float numSamples = min(MaxTraceSamples, samplesToEdge.x);
rayStep *= samplesToEdge.x / numSamples;
// Calculate depth difference error
float depthDiffError = 1.3f * abs(rayStep.z);
// Ray trace
float currSampleIndex = 0;
float currSample, depthDiff;
LOOP
while (currSampleIndex < numSamples)
{
// Sample depth buffer and calculate depth difference
currSample = SampleZ(currOffset.xy);
depthDiff = currOffset.z - currSample;
// Check intersection
if (depthDiff >= 0)
{
if (depthDiff < depthDiffError)
{
break;
}
else
{
currOffset -= rayStep;
rayStep *= 0.5;
}
}
// Move forward
currOffset += rayStep;
currSampleIndex++;
}
// Check if has valid result after ray tracing
if (currSampleIndex >= numSamples)
{
// All samples done but no result
return 0;
}
float2 hitUV = currOffset.xy;
// Fade rays close to screen edge
const float fadeStart = 0.9f;
const float fadeEnd = 1.0f;
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
fadeOnBorder *= RayAttenBorder(hitUV, EdgeFadeFactor);
// Fade rays on high roughness
float roughnessFade = saturate((RoughnessFade - gBuffer.Roughness) * 20);
// Fade on distance
float distanceFade = saturate((FadeOutDistance - gBuffer.ViewPos.z) * InvFadeDistance);
// Output: xy: hitUV, z: hitMask, w: unused // Output: xy: hitUV, z: hitMask, w: unused
return float4(hitUV, fadeOnBorder * roughnessFade * distanceFade * Intensity, 0); return float4(hit.xy, hit.z * Intensity, 0);
} }
#ifndef RESOLVE_SAMPLES #ifndef RESOLVE_SAMPLES