#include "/Lib/Settings.glsl" #include "/Lib/Utilities.glsl" // MinecraftPT — Tracing Utilities // Core voxel ray marching helpers used by all tracers. // Custom resource declarations (bound via shaders.properties) uniform sampler2D atlas2D; uniform sampler3D voxelData3D; uniform sampler2D skyBox2D; // Sample the sky box panorama for a direction (for reflections / sky miss). // Layout must match SkyImage_CS: 3x2 tiles: // row 0: +X -X +Y row 1: -Y +Z -Z vec3 SampleSkyBox(vec3 dir){ dir = normalize(dir); float absX = abs(dir.x), absY = abs(dir.y), absZ = abs(dir.z); float maxAxis = max(absX, max(absY, absZ)); vec3 tdir = dir / maxAxis; vec2 uv; vec2 tileOrigin; vec2 tile = vec2(1.0 / 3.0, 1.0 / 2.0); if (maxAxis == absX){ // +X (col 0) or -X (col 1), row 0 tileOrigin = vec2(dir.x > 0.0 ? 0.0 : 1.0, 0.0); uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5); }else if (maxAxis == absY){ // +Y (col 2, row 0) or -Y (col 0, row 1) tileOrigin = dir.y > 0.0 ? vec2(2.0, 0.0) : vec2(0.0, 1.0); uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5); }else{ // +Z (col 1, row 1) or -Z (col 2, row 1) tileOrigin = vec2(dir.z > 0.0 ? 1.0 : 2.0, 1.0); uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5); } uv = uv * tile + tileOrigin * tile; return textureLod(skyBox2D, uv, 0.0).rgb; } #ifndef TRACING_UTILITIES_GLSL #define TRACING_UTILITIES_GLSL #include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl" #include "/Lib/PathTracing/Voxelizer/BlockShape.glsl" // Convert world position to voxel grid coordinates vec3 WorldToVoxel(vec3 worldPos){ return worldPos + cameraPositionFract + (voxelResolution * 0.5); } // Sample the atlas for a hit voxel, returns albedo vec3 SampleVoxelAlbedo(vec2 midCoord, vec3 hitVoxelPos, vec3 hitNormal){ // Reconstruct the face UV from hit position within voxel vec3 local = fract(hitVoxelPos); vec2 uv; if (abs(hitNormal.x) > 0.5){ uv = vec2(local.z, local.y); }else if (abs(hitNormal.y) > 0.5){ uv = vec2(local.x, local.z); }else{ uv = vec2(local.x, local.y); } // midCoord is the tile-aligned center UV; reconstruct pixel within tile vec2 tileCoord = midCoord; vec2 atlasSizeF = vec2(atlasSize); float tileW = 1.0 / atlasSizeF.x; // approximate; resolved via textureResolution vec2 pixelUV = tileCoord + (uv - 0.5) * tileW * 4.0; return textureLod(atlas2D, pixelUV, 0.0).rgb; } // Read a voxel's data vec4 ReadVoxel(ivec3 voxelCoord){ return texelFetch(voxelData3D, voxelCoord, 0); } // Test whether a voxel is empty (air or empty marker) bool IsVoxelEmpty(vec4 voxelData){ float voxelID = DecodeVoxelID(voxelData.z); return voxelID <= 1.0; } // Test whether a voxel is a light source (sphere light) bool IsLightSphere(float voxelID){ return voxelID >= 239.0 && voxelID <= 290.0; } // Core 3D-DDA march. Calls the callback-style inline logic via return. // Returns: 0 = no hit, 1 = hit, 2 = hit light int TraceRay(Ray ray, float maxDist, out float rayLength, out ivec3 hitVoxel, out float voxelID, out vec3 hitNormal){ vec3 voxelPos = ray.ori; vec3 voxelCoord = floor(voxelPos); vec3 totalStep = (ray.sdir * (voxelCoord - voxelPos + 0.5) + 0.5) * abs(ray.rdir); rayLength = 0.0; vec3 tracingNext; hitNormal = vec3(0.0); int result = 0; vec3 prevCoord = voxelCoord; for (int i = 0; i < 128; i++){ if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord) break; vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0); float id = DecodeVoxelID(voxelData.z); if (id <= 1.0){ // empty or full-block marker if (id >= 0.99 && id <= 1.0){ // Full block if (rayLength > 0.0){ hitVoxel = ivec3(voxelCoord); voxelID = 1.0; hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir; result = 1; break; } } }else if (id >= 1000.0){ // Encoded as full block: id = rawID + 1000 float rawID = id - 1000.0; if (rawID <= 1.0){ hitVoxel = ivec3(voxelCoord); voxelID = rawID; hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir; result = 1; break; } }else if (id > 1.0 && id < 1000.0){ // Cutout shape: id = 1000 - rawID float rawID = 1000.0 - id; float t = minVec3(totalStep); rayLength = t; vec3 n; if (HitShape(ray, voxelCoord, rawID, rayLength, n)){ hitVoxel = ivec3(voxelCoord); voxelID = rawID; hitNormal = n; result = 1; break; } }else if (id >= 200.0 && id <= 290.0){ // Light source — contribute and continue // (handled by caller; here just note it) hitVoxel = ivec3(voxelCoord); voxelID = id; result = 2; break; } // Sparse tracing: skip empty markers float marker = voxelData.z; if (marker > 0.60 && marker < 0.92){ // Hierarchical skip — approximate by stepping over the block size float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0); // find next boundary vec3 nextBoundary = floor((voxelCoord + 1.0) / skipSize) * skipSize; vec3 distToBoundary = (nextBoundary - voxelCoord) * abs(ray.rdir); float tSkip = minVec3(distToBoundary) + 1e-4; rayLength += tSkip; vec3 stepVec = ray.sdir * abs(ray.rdir) * tSkip; voxelPos += stepVec; voxelCoord = floor(voxelPos); totalStep = (ray.sdir * (voxelCoord - voxelPos + 0.5) + 0.5) * abs(ray.rdir); if (rayLength > maxDist) break; continue; } rayLength = minVec3(totalStep); tracingNext = step(totalStep, vec3(rayLength)); voxelCoord += tracingNext * ray.sdir; totalStep += tracingNext * abs(ray.rdir); if (rayLength > maxDist) break; } return result; } #endif