**Add Box Projection to Environment Probe** for better indoor areas
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
#define MAX_LOCAL_LIGHTS 4
|
||||
@1// Forward Shading: Includes
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
#if USE_REFLECTIONS
|
||||
#include "./Flax/ReflectionsCommon.hlsl"
|
||||
#if USE_REFLECTIONS
|
||||
#define MATERIAL_REFLECTIONS_SSR 1
|
||||
#if MATERIAL_REFLECTIONS == MATERIAL_REFLECTIONS_SSR
|
||||
#include "./Flax/SSR.hlsl"
|
||||
@@ -17,7 +17,7 @@
|
||||
@2// Forward Shading: Constants
|
||||
LightData DirectionalLight;
|
||||
LightData SkyLight;
|
||||
ProbeData EnvironmentProbe;
|
||||
EnvProbeData EnvironmentProbe;
|
||||
ExponentialHeightFogData ExponentialHeightFog;
|
||||
float3 Dummy2;
|
||||
uint LocalLightsCount;
|
||||
@@ -32,7 +32,7 @@ Texture3D VolumetricFogTexture : register(t__SRV__);
|
||||
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
|
||||
LightData GetDirectionalLight() { return DirectionalLight; }
|
||||
LightData GetSkyLight() { return SkyLight; }
|
||||
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
|
||||
EnvProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
|
||||
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
|
||||
uint GetLocalLightsCount() { return LocalLightsCount; }
|
||||
LightData GetLocalLight(uint i) { return LocalLights[i]; }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 179
|
||||
#define MATERIAL_GRAPH_VERSION 180
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Packed.h"
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
|
||||
const Char* ToString(RendererType value)
|
||||
@@ -620,6 +621,15 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo
|
||||
resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias
|
||||
}
|
||||
|
||||
void RenderTools::ComputeBoxModelDrawMatrix(const RenderView& view, const OrientedBoundingBox& box, Matrix& resultWorld, bool& resultIsViewInside)
|
||||
{
|
||||
// Construct world matrix
|
||||
Matrix::Transformation(box.Transformation.Scale * box.Extents * 2.0f, box.Transformation.Orientation, box.Transformation.Translation, resultWorld);
|
||||
|
||||
// Check if view is inside the sphere
|
||||
resultIsViewInside = box.Contains(view.Position) == ContainmentType::Contains;
|
||||
}
|
||||
|
||||
Float3 RenderTools::GetColorQuantizationError(PixelFormat format)
|
||||
{
|
||||
Float3 mantissaBits;
|
||||
|
||||
@@ -140,6 +140,7 @@ public:
|
||||
static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal, const Float3& tangent);
|
||||
|
||||
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
static void ComputeBoxModelDrawMatrix(const RenderView& view, const OrientedBoundingBox& box, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
|
||||
// Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl).
|
||||
static Float3 GetColorQuantizationError(PixelFormat format);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/ContentExporters/AssetExporters.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
@@ -22,6 +23,7 @@ EnvironmentProbe::EnvironmentProbe(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
, _radius(3000.0f)
|
||||
, _isUsingCustomProbe(false)
|
||||
, BoxProjection(false)
|
||||
{
|
||||
_drawCategory = SceneRendering::PreRender;
|
||||
_sphere = BoundingSphere(Vector3::Zero, _radius);
|
||||
@@ -48,11 +50,6 @@ void EnvironmentProbe::SetRadius(float value)
|
||||
UpdateBounds();
|
||||
}
|
||||
|
||||
float EnvironmentProbe::GetScaledRadius() const
|
||||
{
|
||||
return _radius * _transform.Scale.MaxValue();
|
||||
}
|
||||
|
||||
GPUTexture* EnvironmentProbe::GetProbe() const
|
||||
{
|
||||
return _probe ? _probe->GetTexture() : _probeTexture;
|
||||
@@ -169,8 +166,16 @@ void EnvironmentProbe::SetProbeData(TextureData& data)
|
||||
|
||||
void EnvironmentProbe::UpdateBounds()
|
||||
{
|
||||
_sphere = BoundingSphere(GetPosition(), GetScaledRadius());
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
if (BoxProjection)
|
||||
{
|
||||
OrientedBoundingBox(_radius, _transform).GetBoundingBox(_box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sphere = BoundingSphere(_transform.Translation, _radius * _transform.Scale.MaxValue());
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
}
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds);
|
||||
}
|
||||
@@ -183,7 +188,7 @@ void EnvironmentProbe::Draw(RenderContext& renderContext)
|
||||
{
|
||||
// Size culling
|
||||
const Float3 position = _sphere.Center - renderContext.View.Origin;
|
||||
const float radius = GetScaledRadius();
|
||||
const float radius = _sphere.Radius;
|
||||
const float drawMinScreenSize = 0.02f;
|
||||
const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
|
||||
const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(position, radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt;
|
||||
@@ -200,10 +205,17 @@ void EnvironmentProbe::Draw(RenderContext& renderContext)
|
||||
RenderEnvironmentProbeData data;
|
||||
data.Texture = texture;
|
||||
data.Position = position;
|
||||
data.Radius = radius;
|
||||
data.Radius = _radius;
|
||||
data.Brightness = Brightness;
|
||||
data.SortOrder = SortOrder;
|
||||
data.HashID = GetHash(_id);
|
||||
data.BlendDistance = Math::Clamp(BlendDistance, 0.001f, _radius * _transform.Scale.MinValue());
|
||||
data.BoxProjection = BoxProjection;
|
||||
if (data.BoxProjection)
|
||||
{
|
||||
data.Orientation = _transform.Orientation;
|
||||
data.Scale = _transform.Scale;
|
||||
}
|
||||
renderContext.List->EnvironmentProbes.Add(data);
|
||||
}
|
||||
}
|
||||
@@ -216,7 +228,19 @@ void EnvironmentProbe::Draw(RenderContext& renderContext)
|
||||
void EnvironmentProbe::OnDebugDrawSelected()
|
||||
{
|
||||
// Draw influence range
|
||||
DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::CornflowerBlue, 0, true);
|
||||
auto rangeColor = Color::CornflowerBlue;
|
||||
auto rangeColorAlpha = rangeColor.AlphaMultiplied(0.2f);
|
||||
if (BoxProjection)
|
||||
{
|
||||
OrientedBoundingBox box(_radius, _transform);
|
||||
DEBUG_DRAW_WIRE_BOX(box, rangeColorAlpha, 0, false);
|
||||
DEBUG_DRAW_WIRE_BOX(box, rangeColor, 0, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_DRAW_WIRE_SPHERE(_sphere, rangeColorAlpha, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(_sphere, rangeColor, 0, true);
|
||||
}
|
||||
|
||||
// Draw capture point (if offset)
|
||||
if (!CaptureOffset.IsZero())
|
||||
@@ -245,7 +269,9 @@ void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
SERIALIZE_MEMBER(Radius, _radius);
|
||||
SERIALIZE(CubemapResolution);
|
||||
SERIALIZE(BoxProjection);
|
||||
SERIALIZE(Brightness);
|
||||
SERIALIZE(BlendDistance);
|
||||
SERIALIZE(SortOrder);
|
||||
SERIALIZE(UpdateMode);
|
||||
SERIALIZE(CaptureNearPlane);
|
||||
@@ -261,7 +287,9 @@ void EnvironmentProbe::Deserialize(DeserializeStream& stream, ISerializeModifier
|
||||
|
||||
DESERIALIZE_MEMBER(Radius, _radius);
|
||||
DESERIALIZE(CubemapResolution);
|
||||
DESERIALIZE(BoxProjection);
|
||||
DESERIALIZE(Brightness);
|
||||
DESERIALIZE(BlendDistance);
|
||||
DESERIALIZE(SortOrder);
|
||||
DESERIALIZE(UpdateMode);
|
||||
DESERIALIZE(CaptureNearPlane);
|
||||
|
||||
@@ -42,21 +42,33 @@ public:
|
||||
/// <summary>
|
||||
/// The reflections texture resolution.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Quality\")")
|
||||
API_FIELD(Attributes="EditorOrder(110), EditorDisplay(\"Quality\")")
|
||||
ProbeCubemapResolution CubemapResolution = ProbeCubemapResolution::UseGraphicsSettings;
|
||||
|
||||
/// <summary>
|
||||
/// The probe update mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Quality\")")
|
||||
API_FIELD(Attributes = "EditorOrder(100), EditorDisplay(\"Quality\")")
|
||||
ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, probe will use box-projection for mapping reelections onto the object surfaces. This method suits better for indoor areas.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Probe\")")
|
||||
bool BoxProjection;
|
||||
|
||||
/// <summary>
|
||||
/// The reflections brightness.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")")
|
||||
float Brightness = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The probe shape inner margin over which blend happens to smooth borders.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(15), Limit(0, 1000, 0.1f), EditorDisplay(\"Probe\"), VisibleIf(nameof(BoxProjection))")
|
||||
float BlendDistance = 100.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The probe rendering order. The higher values are render later (on top).
|
||||
/// </summary>
|
||||
@@ -87,11 +99,6 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetRadius(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets probe scaled radius.
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetScaledRadius() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the probe texture used during rendering (baked or custom one).
|
||||
/// </summary>
|
||||
|
||||
@@ -88,8 +88,9 @@ GPU_CB_STRUCT(ShaderLightData {
|
||||
/// Packed env probe data
|
||||
/// </summary>
|
||||
GPU_CB_STRUCT(ShaderEnvProbeData {
|
||||
Float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused
|
||||
Float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused
|
||||
Float4 Data0;
|
||||
Float4 Data1;
|
||||
Float4 Data2;
|
||||
});
|
||||
|
||||
// Minimum roughness value used for shading (prevent 0 roughness which causes NaNs in Vis_SmithJointApprox)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Assets/Shader.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Engine/Units.h"
|
||||
#include "Engine/Graphics/PixelFormat.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
@@ -449,11 +450,11 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
{
|
||||
auto envProbe = (EnvironmentProbe*)_current.Actor.Get();
|
||||
Vector3 position = envProbe->GetTransform().LocalToWorld(envProbe->CaptureOffset);
|
||||
float radius = envProbe->GetScaledRadius();
|
||||
float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane);
|
||||
float radius = envProbe->GetSphere().Radius;
|
||||
float nearPlane = Math::Max(METERS_TO_UNITS(0.001f), envProbe->CaptureNearPlane);
|
||||
|
||||
// Adjust far plane distance
|
||||
float farPlane = Math::Max(radius, nearPlane + 100.0f);
|
||||
float farPlane = Math::Max(radius, nearPlane + METERS_TO_UNITS(1.0f));
|
||||
farPlane *= farPlane < 10000 ? 10 : 4;
|
||||
Function<bool(Actor*, const Vector3&, float&)> f(&FixFarPlane);
|
||||
SceneQuery::TreeExecute<const Vector3&, float&>(f, position, farPlane);
|
||||
@@ -467,7 +468,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
auto skyLight = (SkyLight*)_current.Actor.Get();
|
||||
Vector3 position = skyLight->GetPosition();
|
||||
float nearPlane = 10.0f;
|
||||
float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f);
|
||||
float farPlane = Math::Max(nearPlane + METERS_TO_UNITS(10.0f), skyLight->SkyDistanceThreshold * 2.0f);
|
||||
_customCullingNear = skyLight->SkyDistanceThreshold;
|
||||
|
||||
// Setup view
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "ScreenSpaceReflectionsPass.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
@@ -169,8 +170,9 @@ bool ReflectionsPass::Init()
|
||||
// Load assets
|
||||
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Reflections"));
|
||||
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Sphere"));
|
||||
_boxModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Box"));
|
||||
_preIntegratedGF = Content::LoadAsyncInternal<Texture>(PRE_INTEGRATED_GF_ASSET_NAME);
|
||||
if (_shader == nullptr || _sphereModel == nullptr || _preIntegratedGF == nullptr)
|
||||
if (_shader == nullptr || _sphereModel == nullptr || _boxModel == nullptr || _preIntegratedGF == nullptr)
|
||||
return true;
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
_shader.Get()->OnReloading.Bind<ReflectionsPass, &ReflectionsPass::OnShaderReloading>(this);
|
||||
@@ -182,7 +184,7 @@ bool ReflectionsPass::Init()
|
||||
bool ReflectionsPass::setupResources()
|
||||
{
|
||||
// Wait for the assets
|
||||
if (!_sphereModel->CanBeRendered() || !_preIntegratedGF->IsLoaded() || !_shader->IsLoaded())
|
||||
if (!_sphereModel->CanBeRendered() || !_boxModel->CanBeRendered() || !_preIntegratedGF->IsLoaded() || !_shader->IsLoaded())
|
||||
return true;
|
||||
const auto shader = _shader->GetShader();
|
||||
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
|
||||
@@ -219,7 +221,6 @@ bool ReflectionsPass::setupResources()
|
||||
|
||||
void ReflectionsPass::Dispose()
|
||||
{
|
||||
// Base
|
||||
RendererPass::Dispose();
|
||||
|
||||
// Cleanup
|
||||
@@ -227,6 +228,7 @@ void ReflectionsPass::Dispose()
|
||||
SAFE_DELETE_GPU_RESOURCE(_psProbeInside);
|
||||
SAFE_DELETE_GPU_RESOURCE(_psCombinePass);
|
||||
_shader = nullptr;
|
||||
_boxModel = nullptr;
|
||||
_sphereModel = nullptr;
|
||||
_preIntegratedGF = nullptr;
|
||||
}
|
||||
@@ -305,14 +307,18 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
|
||||
// Render all env probes
|
||||
auto& sphereMesh = _sphereModel->LODs.Get()[0].Meshes.Get()[0];
|
||||
auto& boxMesh = _boxModel->LODs.Get()[0].Meshes.Get()[0];
|
||||
for (int32 i = 0; i < probesCount; i++)
|
||||
{
|
||||
const RenderEnvironmentProbeData& probe = renderContext.List->EnvironmentProbes.Get()[i];
|
||||
|
||||
// Calculate world view projection matrix for the light sphere
|
||||
// Calculate world*view*projection matrix
|
||||
Matrix world, wvp;
|
||||
bool isViewInside;
|
||||
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, probe.Position, probe.Radius, world, isViewInside);
|
||||
if (probe.BoxProjection)
|
||||
RenderTools::ComputeBoxModelDrawMatrix(renderContext.View, OrientedBoundingBox(probe.Radius, Transform(probe.Position, probe.Orientation, probe.Scale)), world, isViewInside);
|
||||
else
|
||||
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, probe.Position, probe.Radius, world, isViewInside);
|
||||
Matrix::Multiply(world, view.ViewProjection(), wvp);
|
||||
|
||||
// Pack probe properties buffer
|
||||
@@ -324,7 +330,10 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
context->BindCB(0, cb);
|
||||
context->BindSR(4, probe.Texture);
|
||||
context->SetState(isViewInside ? _psProbeInside : _psProbe);
|
||||
sphereMesh.Render(context);
|
||||
if (probe.BoxProjection)
|
||||
boxMesh.Render(context);
|
||||
else
|
||||
sphereMesh.Render(context);
|
||||
}
|
||||
|
||||
context->UnBindSR(4);
|
||||
|
||||
@@ -22,6 +22,7 @@ private:
|
||||
GPUPipelineState* _psCombinePass = nullptr;
|
||||
|
||||
AssetReference<Model> _sphereModel;
|
||||
AssetReference<Model> _boxModel;
|
||||
AssetReference<Texture> _preIntegratedGF;
|
||||
|
||||
public:
|
||||
|
||||
@@ -163,8 +163,20 @@ void RenderSkyLightData::SetShaderData(ShaderLightData& data, bool useShadow) co
|
||||
|
||||
void RenderEnvironmentProbeData::SetShaderData(ShaderEnvProbeData& data) const
|
||||
{
|
||||
data.Data0 = Float4(Position, 0);
|
||||
data.Data1 = Float4(Radius, 1.0f / Radius, Brightness, 0);
|
||||
data.Data0 = Float4(Position, Brightness);
|
||||
if (BoxProjection)
|
||||
{
|
||||
data.Data0.W *= -1;
|
||||
data.Data1 = Float4(Scale * Radius, BlendDistance);
|
||||
Quaternion invQuat;
|
||||
Quaternion::Invert(Orientation, invQuat);
|
||||
data.Data2 = *(Float4*)&invQuat;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Data1 = Float4(Radius, 0, 0, 0);
|
||||
data.Data2 = Float4::Zero;
|
||||
}
|
||||
}
|
||||
|
||||
void* RendererAllocation::Allocate(uintptr size)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Memory/ArenaAllocation.h"
|
||||
#include "Engine/Core/Math/Half.h"
|
||||
#include "Engine/Core/Math/Quaternion.h"
|
||||
#include "Engine/Graphics/PostProcessSettings.h"
|
||||
#include "Engine/Graphics/DynamicBuffer.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
@@ -154,10 +155,14 @@ struct RenderEnvironmentProbeData
|
||||
{
|
||||
GPUTexture* Texture;
|
||||
Float3 Position;
|
||||
float Radius;
|
||||
float Radius; // unscaled for box
|
||||
float Brightness;
|
||||
int32 SortOrder;
|
||||
uint32 HashID;
|
||||
bool BoxProjection;
|
||||
Float3 Scale; // box-only
|
||||
float BlendDistance;
|
||||
Quaternion Orientation; // box-only
|
||||
|
||||
void SetShaderData(ShaderEnvProbeData& data) const;
|
||||
};
|
||||
|
||||
@@ -170,18 +170,6 @@ struct AtmosphericFogData
|
||||
float AtmosphericFogDensityOffset;
|
||||
};
|
||||
|
||||
// Packed env probe data
|
||||
struct ProbeData
|
||||
{
|
||||
float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused
|
||||
float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused
|
||||
};
|
||||
|
||||
#define ProbePos Data0.xyz
|
||||
#define ProbeRadius Data1.x
|
||||
#define ProbeInvRadius Data1.y
|
||||
#define ProbeBrightness Data1.z
|
||||
|
||||
struct Quad_VS2PS
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/MaterialCommon.hlsl"
|
||||
#include "./Flax/BRDF.hlsl"
|
||||
#include "./Flax/Random.hlsl"
|
||||
#include "./Flax/Noise.hlsl"
|
||||
#include "./Flax/MonteCarlo.hlsl"
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
#include "./Flax/GBuffer.hlsl"
|
||||
#include "./Flax/MaterialCommon.hlsl"
|
||||
#include "./Flax/ReflectionsCommon.hlsl"
|
||||
#include "./Flax/BRDF.hlsl"
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
|
||||
ProbeData PData;
|
||||
EnvProbeData PData;
|
||||
float4x4 WVP;
|
||||
GBufferData GBuffer;
|
||||
|
||||
META_CB_END
|
||||
|
||||
DECLARE_GBUFFERDATA_ACCESS(GBuffer)
|
||||
@@ -25,7 +19,7 @@ TextureCube Probe : register(t4);
|
||||
Texture2D Reflections : register(t5);
|
||||
Texture2D PreIntegratedGF : register(t6);
|
||||
|
||||
// Vertex Shader for models rendering
|
||||
// Vertex Shader for probe shape rendering
|
||||
META_VS(true, FEATURE_LEVEL_ES2)
|
||||
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||
Model_VS2PS VS_Model(ModelInput_PosOnly input)
|
||||
|
||||
@@ -4,45 +4,86 @@
|
||||
#define __REFLECTIONS_COMMON__
|
||||
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
#include "./Flax/Quaternion.hlsl"
|
||||
|
||||
// Hit depth (view space) threshold to detect if sky was hit (value above it where 1.0f is default)
|
||||
#define REFLECTIONS_HIT_THRESHOLD 0.9f
|
||||
|
||||
// Packed env probe data
|
||||
struct EnvProbeData
|
||||
{
|
||||
float4 Data0; // x - Position.x | y - Position.y | z - Position.z | w - Brightness (negative for BoxProjection)
|
||||
float4 Data1; // x - Radius/BoxExtent.x | y - BoxExtent.y | z - BoxExtent.z | w - BlendDistance
|
||||
float4 Data2; // x - BoxInvQuat.x | y - BoxInvQuat.y | z - BoxInvQuat.z | w - BoxInvQuat.w
|
||||
};
|
||||
|
||||
#define EnvProbePosition(data) data.Data0.xyz
|
||||
#define EnvProbeBrightness(data) abs(data.Data0.w)
|
||||
#define EnvProbeBoxProjection(data) (data.Data0.w < 0.0f)
|
||||
#define EnvProbeBoxExtent(data) data.Data1.xyz
|
||||
#define EnvProbeBoxInvQuat(data) data.Data2
|
||||
#define EnvProbeSphereRadius(data) data.Data1.x
|
||||
#define EnvProbeBlendDistance(data) data.Data1.w
|
||||
|
||||
float GetSpecularOcclusion(float NoV, float roughnessSq, float ao)
|
||||
{
|
||||
return saturate(pow(NoV + ao, roughnessSq) - 1 + ao);
|
||||
}
|
||||
|
||||
float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, ProbeData data, float3 positionWS, float3 normal, float roughness)
|
||||
float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, EnvProbeData data, float3 positionWS, float3 normal, float roughness)
|
||||
{
|
||||
// Calculate distance from probe to the pixel
|
||||
float3 captureVector = positionWS - data.ProbePos;
|
||||
float captureVectorLength = length(captureVector);
|
||||
|
||||
// Check if cannot light pixel
|
||||
// TODO: maybe remove this check?? - test it out with dozens of probes
|
||||
BRANCH
|
||||
if (captureVectorLength >= data.ProbeRadius)
|
||||
// Calculate fade based on distance to the probe
|
||||
float3 captureVector = positionWS - EnvProbePosition(data);
|
||||
float distanceAlpha;
|
||||
if (EnvProbeBoxProjection(data))
|
||||
{
|
||||
// End
|
||||
return 0;
|
||||
// Box shape
|
||||
float3 boxExtent = EnvProbeBoxExtent(data);
|
||||
float blendDistance = EnvProbeBlendDistance(data);
|
||||
float3 pos = QuaternionRotate(EnvProbeBoxInvQuat(data), captureVector);
|
||||
float3 clampedPos = clamp(pos, -boxExtent + blendDistance, boxExtent - blendDistance);
|
||||
float distanceToBox = length(clampedPos - pos);
|
||||
distanceAlpha = saturate(1 - distanceToBox / blendDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sphere shape
|
||||
float normalizedDistanceToCapture = saturate(length(captureVector) / EnvProbeSphereRadius(data));
|
||||
distanceAlpha = 1.0 - smoothstep(0.7f, 1, normalizedDistanceToCapture);
|
||||
}
|
||||
|
||||
// Fade out based on distance to capture
|
||||
float normalizedDistanceToCapture = saturate(captureVectorLength * data.ProbeInvRadius);
|
||||
float distanceAlpha = 1.0 - smoothstep(0.7, 1, normalizedDistanceToCapture);
|
||||
float fade = distanceAlpha * data.ProbeBrightness;
|
||||
// Early out without sampling texture if out of the bounds
|
||||
BRANCH
|
||||
if (distanceAlpha <= 0.0f)
|
||||
return float4(0, 0, 0, 0);
|
||||
|
||||
// Calculate reflection vector
|
||||
// Calculate probe sampling coordinates
|
||||
float3 sampleVector;
|
||||
float3 V = normalize(positionWS - viewPos);
|
||||
float3 R = reflect(V, normal);
|
||||
float3 D = data.ProbeInvRadius * captureVector + R;
|
||||
if (EnvProbeBoxProjection(data))
|
||||
{
|
||||
// Box projection
|
||||
float3 rotatedReflection = QuaternionRotate(EnvProbeBoxInvQuat(data), R);
|
||||
float3 boxExtent = EnvProbeBoxExtent(data);
|
||||
float3 boxMinMax = select(rotatedReflection > 0.0f, boxExtent, -boxExtent);
|
||||
float3 pos = QuaternionRotate(EnvProbeBoxInvQuat(data), captureVector);
|
||||
float3 rotatedPos = float3(boxMinMax - pos) / rotatedReflection;
|
||||
float minDir = min(min(rotatedPos.x, rotatedPos.y), rotatedPos.z);
|
||||
float3 dir = pos + rotatedReflection * minDir;
|
||||
sampleVector = QuaternionRotate(float4(-EnvProbeBoxInvQuat(data).xyz, EnvProbeBoxInvQuat(data).w), dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sphere projection
|
||||
sampleVector = captureVector / EnvProbeSphereRadius(data) + R;
|
||||
}
|
||||
|
||||
// Sample probe at valid mip level based on surface roughness value
|
||||
half mip = ProbeMipFromRoughness(roughness);
|
||||
float4 probeSample = probe.SampleLevel(SamplerLinearClamp, D, mip);
|
||||
float mip = ProbeMipFromRoughness(roughness);
|
||||
float4 probeSample = probe.SampleLevel(SamplerLinearClamp, sampleVector, mip);
|
||||
|
||||
return probeSample * fade;
|
||||
return probeSample * (distanceAlpha * EnvProbeBrightness(data));
|
||||
}
|
||||
|
||||
// Calculates the reflective environment lighting to multiply the raw reflection color for the specular light (eg. from Env Probe or SSR).
|
||||
|
||||
Reference in New Issue
Block a user