#include "/Lib/Settings.glsl" #include "/Lib/Utilities.glsl" // MinecraftPT — Specular Tracer // Traces GGX-sampled reflection/refraction rays through the voxel grid and // accumulates the reflected radiance (incl. light sphere contributions). #ifndef SPECULAR_TRACER_GLSL #define SPECULAR_TRACER_GLSL #include "/Lib/PathTracing/Tracer/TracingUtilities.glsl" #include "/Lib/BasicFunctions/LightingConstants.glsl" #include "/Lib/PathTracing/Tracer/ShadowTracing.glsl" #include "/Lib/PathTracing/Tracer/SampleIRC.glsl" // Panorama: 3:2 cubemap cross. Map direction to face UV. float absX = abs(dir.x), absY = abs(dir.y), absZ = abs(dir.z); float maxAxis = max(absX, max(absY, absZ)); vec2 uv; vec3 tdir = dir / maxAxis; if (maxAxis == absX){ uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(0.0, 0.5) * vec2(1.0/3.0, 1.0/2.0); if (tdir.x > 0.0){ uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 0.5/2.0); } }else if (maxAxis == absY){ uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(2.0/3.0, 0.5/2.0); if (tdir.y > 0.0){ uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 0.5/2.0); } }else{ uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(2.0/3.0, 1.5/2.0); if (tdir.z > 0.0){ uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 1.5/2.0); } } return textureLod(skyBox2D, uv, 0.0).rgb; } // Trace a single specular ray and accumulate radiance. // origin: world pos (voxel space handled inside), dir: normalized direction vec3 SpecularTrace(vec3 voxelPos, vec3 dir, float maxDist, vec2 noise){ vec3 result = vec3(0.0); Ray ray = PackRay(voxelPos, dir); vec3 voxelCoord = floor(ray.ori); vec3 totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir); float rayLength = 0.0; vec3 tracingNext; bool hit = false; for (int i = 0; i < 128; i++){ if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord){ // Escaped the voxel grid — sample sky result = SampleSkyBox(dir); break; } if (rayLength > maxDist){ result = SampleSkyBox(dir); break; } vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0); float voxelID = DecodeVoxelID(voxelData.z); // Light source: accumulate sphere light if (IsLightSphere(voxelID)){ result += HitLightShpereReflection(ray, voxelCoord, voxelID, rayLength); rayLength = minVec3(totalStep); tracingNext = step(totalStep, vec3(rayLength)); voxelCoord += tracingNext * ray.sdir; totalStep += tracingNext * abs(ray.rdir); continue; } if (voxelID >= 999.0){ // Full block // Shade the hit point: sample albedo from atlas, apply lighting vec3 hitPos = ray.ori + ray.dir * rayLength; vec3 hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir; vec2 midCoord = voxelData.xy; vec3 albedo = SampleVoxelAlbedo(midCoord, hitPos, hitNormal); // Direct light from sun vec3 sunColor = GetSunIrradiance(); float sunShadow = SimpleShadowTracing(hitPos + hitNormal * 0.01, GetSunDirWorld()); vec3 direct = albedo * sunColor * sunShadow * max(dot(hitNormal, GetSunDirWorld()), 0.0); // Sky ambient from IRC vec3 irc = SampleIRC(hitPos); result += direct + irc * albedo * 0.5; hit = true; break; }else if (voxelID < 1000.0 && voxelID > 1.0){ // Cutout shape float rawID = 1000.0 - voxelID; rayLength = minVec3(totalStep); vec3 n; if (HitShape(ray, voxelCoord, rawID, rayLength, n)){ vec3 hitPos = ray.ori + ray.dir * rayLength; vec2 midCoord = voxelData.xy; vec3 albedo = SampleVoxelAlbedo(midCoord, hitPos, n); vec3 sunColor = GetSunIrradiance(); float sunShadow = SimpleShadowTracing(hitPos + n * 0.01, GetSunDirWorld()); vec3 direct = albedo * sunColor * sunShadow * max(dot(n, GetSunDirWorld()), 0.0); vec3 irc = SampleIRC(hitPos); result += direct + irc * albedo * 0.5; hit = true; break; } } // Sparse skip float marker = voxelData.z; if (marker > 0.60 && marker < 0.92){ float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0); 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; ray.ori += stepVec; voxelCoord = floor(ray.ori); totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir); continue; } rayLength = minVec3(totalStep); tracingNext = step(totalStep, vec3(rayLength)); voxelCoord += tracingNext * ray.sdir; totalStep += tracingNext * abs(ray.rdir); } if (!hit){ result = SampleSkyBox(dir); } return result; } #endif