Refactor Global SDF to use a single texture for all cascades

This commit is contained in:
Wojciech Figat
2022-06-17 10:41:04 +02:00
parent bc8cc75ad8
commit 2e06d1ce2a
14 changed files with 204 additions and 194 deletions

View File

@@ -59,20 +59,27 @@ struct GlobalSDFHit
}
};
void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeMaxDistance, out float3 cascadeUV, out float3 textureUV)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float3 posInCascade = worldPosition - cascadePosDistance.xyz;
cascadeMaxDistance = cascadePosDistance.w * 2;
cascadeUV = saturate(posInCascade / cascadeMaxDistance + 0.5f);
textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // cascades are placed next to each other on X axis
}
// Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4], float3 worldPosition)
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition)
{
float distance = data.CascadePosDistance[3].w * 2.0f;
if (distance <= 0.0f)
return GLOBAL_SDF_WORLD_SIZE;
UNROLL
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float cascadeMaxDistance = cascadePosDistance.w * 2;
float3 posInCascade = worldPosition - cascadePosDistance.xyz;
float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f;
float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
distance = cascadeDistance * cascadeMaxDistance;
@@ -83,29 +90,27 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4], float3
}
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex[4], float3 worldPosition, out float distance)
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition, out float distance)
{
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f)
return gradient;
UNROLL
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float cascadeMaxDistance = cascadePosDistance.w * 2;
float3 posInCascade = worldPosition - cascadePosDistance.xyz;
float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f;
float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float texelOffset = 1.0f / data.Resolution;
float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x;
float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x;
float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x;
float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
@@ -115,7 +120,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex[4]
}
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex[4], Texture3D<float> mips[4], float3 worldPosition, out float distance)
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, float3 worldPosition, out float distance)
{
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
@@ -123,28 +128,26 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex[4]
return gradient;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
UNROLL
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float cascadeMaxDistance = cascadePosDistance.w * 2;
float3 posInCascade = worldPosition - cascadePosDistance.xyz;
float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f;
float cascadeDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float cascadeDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistanceTex < chunkMarginDistance * 2)
{
cascadeDistance = cascadeDistanceTex;
}
float texelOffset = 1.0f / data.Resolution;
float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x;
float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x;
float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x;
float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
@@ -154,7 +157,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex[4]
}
// Ray traces the Global SDF.
GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4], Texture3D<float> mips[4], const GlobalSDFTrace trace)
GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, const GlobalSDFTrace trace)
{
GlobalSDFHit hit = (GlobalSDFHit)0;
hit.HitTime = -1.0f;
@@ -163,7 +166,6 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4]
float nextIntersectionStart = 0.0f;
float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2);
float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance;
UNROLL
for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
@@ -195,13 +197,14 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4]
{
float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime;
// Sample SDF
float3 posInCascade = stepPosition - cascadePosDistance.xyz;
float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f;
float stepDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
// Sample SDF
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV);
float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistance < chunkSizeDistance)
{
float stepDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0);
float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistanceTex < chunkMarginDistance * 2)
{
stepDistance = stepDistanceTex;
@@ -226,12 +229,12 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4]
{
// Calculate hit normal from SDF gradient
float texelOffset = 1.0f / data.Resolution;
float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x;
float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x;
float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x;
float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x;
float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn));
}
break;