// End Portal — One Giant Rotating Black Hole (single piece, fills the whole portal)
// Event horizon + photon ring + spinning accretion disk. Disk rotates around its axis.
color.rgb = vec3(0.0);

// Stable 2D coordinate from view direction projected onto the portal plane.
vec3 wpos = normalize((gbufferModelViewInverse * vec4(viewPos, 1.0)).xyz);
vec2 pCoord;
if (abs(NdotU) > 0.9) {
    pCoord = (wpos.xz / max(abs(wpos.y), 0.0001)) * (abs(playerPos.y) + 1.0);
    pCoord -= cameraPosition.xz;
} else if (abs(dot(normal, eastVec)) > 0.9) {
    pCoord = (wpos.yz / max(abs(wpos.x), 0.0001)) * (abs(playerPos.x) + 1.0);
    pCoord -= cameraPosition.yz;
} else {
    pCoord = (wpos.yx / max(abs(wpos.z), 0.0001)) * (abs(playerPos.z) + 1.0);
    pCoord -= cameraPosition.yx;
}

// Single black hole filling the whole portal — no per-block tiling.
vec2 bhUV = pCoord * 0.045;
float dist = length(bhUV);
float ang  = atan(bhUV.y, bhUV.x);

// Differential rotation: inner disk spins faster than outer (Keplerian-style).
// Pure axial rotation only.
float baseSpin   = frameTimeCounter * 0.55;
float angRot     = ang + baseSpin / max(dist * 3.5, 0.25);

// Event horizon — pitch-black core with a sharp edge.
float horizonR   = 0.18;
float horizon    = smoothstep(horizonR + 0.01, horizonR - 0.01, dist);

// Photon ring — bright thin ring just outside the horizon.
float photonR    = 0.21;
float photonRing = exp(-pow((dist - photonR) * 35.0, 2.0));

// Accretion disk extent.
float diskInner  = 0.22;
float diskOuter  = 1.10;
float diskBody   = smoothstep(diskOuter, diskInner, dist) * smoothstep(diskInner - 0.02, diskInner + 0.04, dist);

// Spinning striations on the disk (using the rotated angle).
float striation  = 0.55 + 0.45 * cos(angRot * 6.0 + dist * 18.0);
// Hot spots that orbit with the disk.
float hotSpot    = pow(0.5 + 0.5 * cos(angRot * 2.0 + dist * 4.0), 6.0);

// Doppler-like brightening on one side of the disk.
float doppler    = 0.6 + 0.5 * cos(ang + 1.2);

// Outer faint glow halo.
float outerGlow  = exp(-dist * 2.6) * 0.35;

// Color palette — classic black hole look.
vec3 voidColor   = vec3(0.0);
vec3 ringColor   = vec3(2.8, 1.9, 1.0);   // photon ring (white-hot)
vec3 diskHot     = vec3(2.4, 1.0, 0.30);  // inner disk (orange)
vec3 diskCool    = vec3(0.85, 0.30, 0.05); // outer disk (deep red-orange)
vec3 hotSpotCol  = vec3(2.6, 1.5, 0.55);   // bright orbiting hot spots
vec3 glowColor   = vec3(0.40, 0.15, 0.05);

// Disk gradient: hot near the center, cooler far out.
float diskT      = smoothstep(diskInner, diskOuter, dist);
vec3 diskColor   = mix(diskHot, diskCool, diskT);

// Compose.
color.rgb  = glowColor * outerGlow;
color.rgb += diskColor   * diskBody * striation * doppler * 1.4;
color.rgb += hotSpotCol  * diskBody * hotSpot * 0.6;
color.rgb += ringColor   * photonRing * 1.6;
color.rgb  = mix(color.rgb, voidColor, horizon);
color.rgb *= 1.3;
emission = 12.0;

float dither = Bayer64(gl_FragCoord.xy);

noDirectionalShading = true;

#ifdef LU_COATED_TEXTURES
    noiseFactor = 0.0;
#endif

#ifdef LU_PORTAL_EDGE_EFFECT
    //vec3 voxelPos = SceneToVoxel(mix(playerPos, vec3(0.0), -0.02)); // Fixes weird parallax offset
    vec3 voxelPos = SceneToVoxel(playerPos);

    if (CheckInsideVoxelVolume(voxelPos)) {
        float portalOffset = 0.08333 * dither;
        vec3[4] portalOffsets = vec3[](
            vec3( portalOffset, 0, portalOffset),
            vec3( portalOffset, 0,-portalOffset),
            vec3(-portalOffset, 0, portalOffset),
            vec3(-portalOffset, 0,-portalOffset)
        );

        float edge = 0.0;
        for (int i = 0; i < 4; i++) {
            int voxel = int(texelFetch(voxel_sampler, ivec3(voxelPos + portalOffsets[i]), 0).r);
            if (voxel == 58 || voxel == 255) { // End Portal Frame or Bedrock
                edge = 1.0; break;
            }
        }

        #ifdef END
            // No edge effect in the middle of the return fountain
            vec2 var1 = abs(playerPos.xz + cameraPosition.xz - 0.5);
            float var2 = max(var1.x, var1.y);
            if (var2 > 1.0)
        #endif
        {
            vec4 edgeColor = vec4(vec3(2.0, 0.85, 0.20), 1.0);
            color = mix(color, edgeColor, edge);
            emission = mix(emission, 7.0, edge);
        }
    }
#endif
