// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VisualizeSparseVolumeTexture.usf: Used to visualize and debug a sparse texture asset. =============================================================================*/ #include "/Engine/Private/Common.ush" #ifdef VERTEX_SHADER float DepthAsDeviceZ; void VisualizeSparseVolumeTextureVS( in uint VertexId : SV_VertexID, out float4 Position : SV_POSITION) { float2 UV = -1.0f; UV = VertexId == 1 ? float2(-1.0f, 3.0f) : UV; UV = VertexId == 2 ? float2(3.0f, -1.0f) : UV; Position = float4(UV, DepthAsDeviceZ, 1.0f); } #endif // VERTEX_SHADER #ifdef PIXEL_SHADER // Updated from http://jcgt.org/published/0007/03/04/ bool Slabs(float3 p0, float3 p1, float3 rayOrigin, float3 invRaydir, out float outTMin, out float outTMax) { float3 t0 = (p0 - rayOrigin) * invRaydir; float3 t1 = (p1 - rayOrigin) * invRaydir; float3 tmin = min(t0, t1), tmax = max(t0, t1); float maxtmin = max(max(tmin.x, tmin.y), tmin.z); float mintmax = min(min(tmax.x, tmax.y), tmax.z); outTMin = maxtmin; outTMax = mintmax; return maxtmin <= mintmax; } float3 VolumeBoundMinWorld; float3 VolumeBoundMaxWorld; Texture3D SparseVolumeTexture; float3 SparseVolumeTextureResolution; Texture3D SparseVolumeTexturePageTable; float3 SparseVolumeTexturePageTableResolution; #define SPARSE_VOLUME_TILE_RES 16 void VisualizeSparseVolumeTexturePS( in float4 SVPos : SV_POSITION, out float4 OutLuminanceAlpha : SV_Target0 ) { ResolvedView = ResolveView(); const uint2 PixelCoord = uint2(SVPos.xy); float2 UvBuffer = PixelCoord * View.BufferSizeAndInvSize.zw; // Uv for depth buffer read (size can be larger than viewport) float2 ScreenPosition = SvPositionToScreenPosition(SVPos).xy; const float Depth = 10000.0f; float4 TranslatedWorldPos = mul(float4(ScreenPosition * Depth, Depth, 1), View.ScreenToTranslatedWorld); const float3 RayDir = normalize(TranslatedWorldPos.xyz - View.TranslatedWorldCameraOrigin); const float3 RayOrig = LWCHackToFloat(ResolvedView.WorldCameraOrigin); OutLuminanceAlpha = float4(0.0, 0.0, 0.0, 1.0); float TMin = 0.0f; float TMax = 0.0f; float Transmittance = 1.0f; if (Slabs(VolumeBoundMinWorld, VolumeBoundMaxWorld, RayOrig, 1.0 / RayDir, TMin, TMax) && TMax > 0.0f) { Transmittance = 0.5; // show the bounding box // Amanatides 3D DDA marching implementation - Paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf // See https://www.shadertoy.com/view/3sKXDK float3 StartPos = RayOrig + RayDir * max(0.0, TMin); float3 VolumeResolution = SparseVolumeTexturePageTableResolution * SPARSE_VOLUME_TILE_RES; const float3 StartUVs = (StartPos - VolumeBoundMinWorld) / (VolumeBoundMaxWorld - VolumeBoundMinWorld); float3 P = StartUVs * VolumeResolution; // Force round to volume if (P.x < 0) P.x = 0; if (P.y < 0) P.y = 0; if (P.z < 0) P.z = 0; if (P.x > (VolumeResolution.x)) P.x = (VolumeResolution.x); if (P.y > (VolumeResolution.y)) P.y = (VolumeResolution.y); if (P.z > (VolumeResolution.z)) P.z = (VolumeResolution.z); // Amanatides 3D-DDA data preparation float3 stepSign = sign(RayDir); float3 tDelta = abs(1.0 / RayDir); float3 tMax = float3(0.0, 0.0, 0.0); float3 refPoint = floor(P); tMax.x = stepSign.x > 0.0 ? refPoint.x + 1.0 - P.x : P.x - refPoint.x; tMax.y = stepSign.y > 0.0 ? refPoint.y + 1.0 - P.y : P.y - refPoint.y; tMax.z = stepSign.z > 0.0 ? refPoint.z + 1.0 - P.z : P.z - refPoint.z; tMax.x *= tDelta.x; tMax.y *= tDelta.y; tMax.z *= tDelta.z; LOOP while (P.x >= 0 && P.y >= 0 && P.z >= 0 && P.x <= VolumeResolution.x && P.y <= VolumeResolution.y && P.z <= VolumeResolution.z) { float StepLength = 0.0; #if 0 // Slow reference P += RayDir * 0.005; StepLength = 0.005; #else // Amanatides 3D-DDA if (tMax.x < tMax.y) { if (tMax.x < tMax.z) { P.x += stepSign.x; tMax.x += tDelta.x; StepLength += 1; } else { P.z += stepSign.z; tMax.z += tDelta.z; StepLength += 1; } } else { if (tMax.y < tMax.z) { P.y += stepSign.y; tMax.y += tDelta.y; StepLength += 1; } else { P.z += stepSign.z; tMax.z += tDelta.z; StepLength += 1; } } #endif float3 PageCoordF = floor(P * (1.0 / SPARSE_VOLUME_TILE_RES)); const uint EncodedPage = SparseVolumeTexturePageTable.Load(int4(int3(PageCoordF), 0)); if (EncodedPage) { const int3 TileCoord = int3( EncodedPage & 0x7FF, (EncodedPage >> 11 ) & 0x7FF, (EncodedPage >> 22 ) & 0x3FF); int3 VoxelCoord = TileCoord * SPARSE_VOLUME_TILE_RES + int3(P - PageCoordF * SPARSE_VOLUME_TILE_RES); const float4 VoxelData = SparseVolumeTexture.Load(int4(VoxelCoord, 0)); const float Density = VoxelData.x; if (Density > 0.0f) { #if 0 //OutLuminanceAlpha = float4(Rand3DPCG16(PageCoordF).x / 65535.0f, Rand3DPCG16(VoxelCoord).y / 65535.0f, 0.0, 0.0); //OutLuminanceAlpha = float4(Rand3DPCG16(PageCoordF) / 65535.0f, 0.0); OutLuminanceAlpha = float4(Rand3DPCG16(VoxelCoord) / 65535.0f, 0.0); break; #else Transmittance *= exp(-StepLength * Density * 0.025); #endif } } } } OutLuminanceAlpha.a = Transmittance; } #endif // PIXEL_SHADER