Add **Screen Space Reflections for transparent materials**
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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] });
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
154
Source/Shaders/SSR.hlsl
Normal 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 = true, 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user