220 lines
11 KiB
HLSL
220 lines
11 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "/Engine/Private/Common.ush"
|
|
|
|
#define NEEDS_SCENE_TEXTURES 1
|
|
#define SCENE_TEXTURES_DISABLED 0
|
|
#define SubstratePublicStruct SubstratePublic
|
|
#include "/Engine/Public/SubstratePublic.ush"
|
|
|
|
#define NDICOLLISIONQUERY_USE_GBUFFER_NORMAL (FEATURE_LEVEL >= FEATURE_LEVEL_SM5)
|
|
|
|
float NDICollisionQuery_GetCustomSceneDepth(float2 ScreenUV)
|
|
{
|
|
#if (FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1)
|
|
return ConvertFromDeviceZ(Texture2DSampleLevel(MobileSceneTextures.CustomDepthTexture, MobileSceneTextures.CustomDepthTextureSampler, ScreenUV, 0).r);
|
|
#else
|
|
return ConvertFromDeviceZ(Texture2DSampleLevel(SceneTexturesStruct.CustomDepthTexture, SceneTexturesStruct_CustomDepthTextureSampler, ScreenUV, 0).r);
|
|
#endif
|
|
}
|
|
|
|
float NDICollisionQuery_GetScenePartialDepth(float2 ScreenUV)
|
|
{
|
|
#if (FEATURE_LEVEL <= FEATURE_LEVEL_ES3_1)
|
|
return ConvertFromDeviceZ(Texture2DSampleLevel(MobileSceneTextures.ScenePartialDepthTexture, MobileSceneTextures.ScenePartialDepthTextureSampler, ScreenUV, 0).r);
|
|
#else
|
|
return ConvertFromDeviceZ(Texture2DSampleLevel(SceneTexturesStruct.ScenePartialDepthTexture, SceneTexturesStruct_ScenePartialDepthTextureSampler, ScreenUV, 0).r);
|
|
#endif
|
|
}
|
|
|
|
void NDICollisionQuery_QuerySceneDepthGPU(in float3 In_SamplePos, in float3 In_LWCTile, out float Out_SceneDepth, out float3 Out_CameraPosWorld, out bool Out_IsInsideView, out float3 Out_WorldPos, out float3 Out_WorldNormal)
|
|
{
|
|
Out_SceneDepth = -1;
|
|
Out_WorldPos = float3(0.0, 0.0, 0.0);
|
|
Out_WorldNormal = float3(0.0, 0.0, 1.0);
|
|
Out_IsInsideView = true;
|
|
FLWCVector3 CameraPos = PrimaryView.TileOffset.WorldCameraOrigin;
|
|
LWCSetTile(CameraPos, LWCGetTile(CameraPos) - In_LWCTile); // convert to simulation space
|
|
Out_CameraPosWorld = LWCToFloat(CameraPos);
|
|
|
|
FLWCVector3 LwcSamplePos = MakeLWCVector3(In_LWCTile, In_SamplePos);
|
|
float4 SamplePosition = float4(LWCToFloat(LWCAdd(LwcSamplePos, PrimaryView.TileOffset.PreViewTranslation)), 1); // TODO[mg]: LWCToFloat here?
|
|
float4 ClipPosition = mul(SamplePosition, View.TranslatedWorldToClip);
|
|
float2 ScreenPosition = ClipPosition.xy / ClipPosition.w;
|
|
|
|
// Check if the sample is inside the view.
|
|
if (all(abs(ScreenPosition.xy) <= float2(1, 1)))
|
|
{
|
|
// Sample the depth buffer to get a world position near the sample position.
|
|
float2 ScreenUV = ScreenPosition * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
|
|
float SceneDepth = CalcSceneDepth(ScreenUV);
|
|
FLWCVector3 WorldPosition = WorldPositionFromSceneDepth(ScreenPosition.xy, SceneDepth);
|
|
|
|
#if NDICOLLISIONQUERY_USE_GBUFFER_NORMAL
|
|
#if SUBTRATE_GBUFFER_FORMAT==1
|
|
uint2 ScreenPos = uint2(ScreenUV * View.BufferSizeAndInvSize.xy);
|
|
float3 WorldNormal = SubstratePublic_GetWorldNormal(ScreenPos);
|
|
#else
|
|
float3 WorldNormal = Texture2DSampleLevel(SceneTexturesStruct.GBufferATexture, SceneTexturesStruct_GBufferATextureSampler, ScreenUV, 0).xyz * 2.0 - 1.0;
|
|
#endif
|
|
#else
|
|
float CollisionDepthBounds = 500.0f;
|
|
float SceneDepth0 = CalcSceneDepth(ScreenUV + float2(View.BufferSizeAndInvSize.z, 0.0));
|
|
float SceneDepth1 = CalcSceneDepth(ScreenUV + float2(0.0, View.BufferSizeAndInvSize.w));
|
|
// When using the forward shading, the normal of the pixel is approximated by the derivative of the world position
|
|
// of the pixel. But in on the visible edge this derivative can become very high, making CollisionPlane almost
|
|
// perpendicular to the view plane. In these case the particle may collide the visible edges of the diferent meshes
|
|
// in the view frustum. To avoid this, we disable the collision test if one of the derivate is above a threshold.
|
|
if (max(abs(SceneDepth - SceneDepth0), abs(SceneDepth - SceneDepth1)) > CollisionDepthBounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FLWCVector3 WorldPosition0 = WorldPositionFromSceneDepth(ScreenPosition.xy + float2(2 * View.ViewSizeAndInvSize.z, 0.0), SceneDepth0);
|
|
FLWCVector3 WorldPosition1 = WorldPositionFromSceneDepth(ScreenPosition.xy - float2(0.0, 2 * View.ViewSizeAndInvSize.w), SceneDepth1);
|
|
float3 WorldNormal = normalize(cross(LWCSubtract(WorldPosition0, WorldPosition).Offset, LWCSubtract(WorldPosition1, WorldPosition).Offset));
|
|
#endif
|
|
|
|
// convert LWC position back to simulation space
|
|
LWCSetTile(WorldPosition, LWCGetTile(WorldPosition) - In_LWCTile);
|
|
|
|
// Set outputs
|
|
Out_SceneDepth = SceneDepth;
|
|
Out_WorldPos = LWCToFloat(WorldPosition);
|
|
Out_WorldNormal = WorldNormal;
|
|
}
|
|
else
|
|
{
|
|
Out_IsInsideView = false;
|
|
}
|
|
}
|
|
|
|
void NDICollisionQuery_QueryScenePartialDepthGPU(in float3 In_SamplePos, in float3 In_LWCTile, out float Out_SceneDepth, out float3 Out_CameraPosWorld, out bool Out_IsInsideView, out float3 Out_WorldPos, out float3 Out_WorldNormal)
|
|
{
|
|
Out_SceneDepth = -1;
|
|
Out_WorldPos = float3(0.0, 0.0, 0.0);
|
|
Out_WorldNormal = float3(0.0, 0.0, 1.0);
|
|
Out_IsInsideView = true;
|
|
FLWCVector3 CameraPos = PrimaryView.TileOffset.WorldCameraOrigin;
|
|
LWCSetTile(CameraPos, LWCGetTile(CameraPos) - In_LWCTile); // convert to simulation space
|
|
Out_CameraPosWorld = LWCToFloat(CameraPos);
|
|
|
|
FLWCVector3 LwcSamplePos = MakeLWCVector3(In_LWCTile, In_SamplePos);
|
|
float4 SamplePosition = float4(LWCToFloat(LWCAdd(LwcSamplePos, PrimaryView.TileOffset.PreViewTranslation)), 1); // TODO[mg]: LWCToFloat here?
|
|
float4 ClipPosition = mul(SamplePosition, View.TranslatedWorldToClip);
|
|
float2 ScreenPosition = ClipPosition.xy / ClipPosition.w;
|
|
|
|
// Check if the sample is inside the view.
|
|
if (all(abs(ScreenPosition.xy) <= float2(1, 1)))
|
|
{
|
|
// Sample the depth buffer to get a world position near the sample position.
|
|
float2 ScreenUV = ScreenPosition * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
|
|
|
|
float SceneDepth = NDICollisionQuery_GetScenePartialDepth(ScreenUV);
|
|
|
|
FLWCVector3 WorldPosition = WorldPositionFromSceneDepth(ScreenPosition.xy, SceneDepth);
|
|
|
|
// We cannot sample the normal from the GBuffer when using partial depth collision queries.
|
|
// => It must be derived form the partial depth buffer itself to get a match.
|
|
|
|
float CollisionDepthBounds = 500.0f;
|
|
float SceneDepth0 = NDICollisionQuery_GetScenePartialDepth(ScreenUV + float2(View.BufferSizeAndInvSize.z, 0.0));
|
|
float SceneDepth1 = NDICollisionQuery_GetScenePartialDepth(ScreenUV + float2(0.0, View.BufferSizeAndInvSize.w));
|
|
// When using the forward shading, the normal of the pixel is approximated by the derivative of the world position
|
|
// of the pixel. But in on the visible edge this derivative can become very high, making CollisionPlane almost
|
|
// perpendicular to the view plane. In these case the particle may collide the visible edges of the diferent meshes
|
|
// in the view frustum. To avoid this, we disable the collision test if one of the derivate is above a threshold.
|
|
if (max(abs(SceneDepth - SceneDepth0), abs(SceneDepth - SceneDepth1)) > CollisionDepthBounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FLWCVector3 WorldPosition0 = WorldPositionFromSceneDepth(ScreenPosition.xy + float2(2 * View.ViewSizeAndInvSize.z, 0.0), SceneDepth0);
|
|
FLWCVector3 WorldPosition1 = WorldPositionFromSceneDepth(ScreenPosition.xy - float2(0.0, 2 * View.ViewSizeAndInvSize.w), SceneDepth1);
|
|
float3 WorldNormal = normalize(cross(LWCSubtract(WorldPosition0, WorldPosition).Offset, LWCSubtract(WorldPosition1, WorldPosition).Offset));
|
|
|
|
// convert LWC position back to simulation space
|
|
LWCSetTile(WorldPosition, LWCGetTile(WorldPosition) - In_LWCTile);
|
|
|
|
// Set outputs
|
|
Out_SceneDepth = SceneDepth;
|
|
Out_WorldPos = LWCToFloat(WorldPosition);
|
|
Out_WorldNormal = WorldNormal;
|
|
}
|
|
else
|
|
{
|
|
Out_IsInsideView = false;
|
|
}
|
|
}
|
|
|
|
void NDICollisionQuery_QueryCustomDepthGPU(in float3 In_SamplePos, in float3 In_LWCTile, out float Out_SceneDepth, out float3 Out_CameraPosWorld, out bool Out_IsInsideView, out float3 Out_WorldPos, out float3 Out_WorldNormal)
|
|
{
|
|
Out_SceneDepth = -1;
|
|
Out_WorldPos = float3(0.0, 0.0, 0.0);
|
|
Out_WorldNormal = float3(0.0, 0.0, 1.0);
|
|
Out_IsInsideView = true;
|
|
FLWCVector3 CameraPos = PrimaryView.TileOffset.WorldCameraOrigin;
|
|
LWCSetTile(CameraPos, LWCGetTile(CameraPos) - In_LWCTile); // convert to simulation space
|
|
Out_CameraPosWorld = LWCToFloat(CameraPos);
|
|
|
|
FLWCVector3 LwcSamplePos = MakeLWCVector3(In_LWCTile, In_SamplePos);
|
|
float4 SamplePosition = float4(LWCToFloat(LWCAdd(LwcSamplePos, PrimaryView.TileOffset.PreViewTranslation)), 1); // TODO[mg]: LWCToFloat here?
|
|
float4 ClipPosition = mul(SamplePosition, View.TranslatedWorldToClip);
|
|
float2 ScreenPosition = ClipPosition.xy / ClipPosition.w;
|
|
|
|
// Check if the sample is inside the view.
|
|
if (all(abs(ScreenPosition.xy) <= float2(1, 1)))
|
|
{
|
|
// Sample the depth buffer to get a world position near the sample position.
|
|
float2 ScreenUV = ScreenPosition * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
|
|
float SceneDepth = NDICollisionQuery_GetCustomSceneDepth(ScreenUV);
|
|
FLWCVector3 WorldPosition = WorldPositionFromSceneDepth(ScreenPosition.xy, SceneDepth);
|
|
|
|
float CollisionDepthBounds = 500.0f;
|
|
float SceneDepth0 = NDICollisionQuery_GetCustomSceneDepth(ScreenUV + float2(View.BufferSizeAndInvSize.z, 0.0));
|
|
float SceneDepth1 = NDICollisionQuery_GetCustomSceneDepth(ScreenUV + float2(0.0, View.BufferSizeAndInvSize.w));
|
|
// When using the forward shading, the normal of the pixel is approximated by the derivative of the world position
|
|
// of the pixel. But in on the visible edge this derivative can become very high, making CollisionPlane almost
|
|
// perpendicular to the view plane. In these case the particle may collide the visible edges of the diferent meshes
|
|
// in the view frustum. To avoid this, we disable the collision test if one of the derivate is above a threshold.
|
|
if (max(abs(SceneDepth - SceneDepth0), abs(SceneDepth - SceneDepth1)) > CollisionDepthBounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FLWCVector3 WorldPosition0 = WorldPositionFromSceneDepth(ScreenPosition.xy + float2(2 * View.ViewSizeAndInvSize.z, 0.0), SceneDepth0);
|
|
FLWCVector3 WorldPosition1 = WorldPositionFromSceneDepth(ScreenPosition.xy - float2(0.0, 2 * View.ViewSizeAndInvSize.w), SceneDepth1);
|
|
float3 WorldNormal = normalize(cross(LWCSubtract(WorldPosition0, WorldPosition).Offset, LWCSubtract(WorldPosition1, WorldPosition).Offset));
|
|
|
|
// convert LWC position back to simulation space
|
|
LWCSetTile(WorldPosition, LWCGetTile(WorldPosition) - In_LWCTile);
|
|
|
|
// Set outputs
|
|
Out_SceneDepth = SceneDepth;
|
|
Out_WorldPos = LWCToFloat(WorldPosition);
|
|
Out_WorldNormal = WorldNormal;
|
|
}
|
|
else
|
|
{
|
|
Out_IsInsideView = false;
|
|
}
|
|
}
|
|
|
|
void NDICollisionQuery_QueryMeshDistanceFieldGPU(in float3 In_SamplePos, in float3 In_LWCTile, out float Out_DistanceToNearestSurface, out float3 Out_FieldGradient, out bool Out_IsDistanceFieldValid)
|
|
{
|
|
#if PLATFORM_SUPPORTS_DISTANCE_FIELDS
|
|
FLWCVector3 LwcSamplePos = MakeLWCVector3(In_LWCTile, In_SamplePos);
|
|
float3 SamplePosition = LWCToFloat(LWCAdd(LwcSamplePos, PrimaryView.TileOffset.PreViewTranslation));
|
|
|
|
Out_DistanceToNearestSurface = GetDistanceToNearestSurfaceGlobal(SamplePosition);
|
|
Out_FieldGradient = GetDistanceFieldGradientGlobal(SamplePosition);
|
|
Out_IsDistanceFieldValid = (MaxGlobalDFAOConeDistance > 0) && !(Out_DistanceToNearestSurface > 0 && all(Out_FieldGradient == float3(0,0,0.001f)));
|
|
#else
|
|
Out_DistanceToNearestSurface = 0;
|
|
Out_FieldGradient = (float3)0;
|
|
Out_IsDistanceFieldValid = false;
|
|
#endif
|
|
}
|