float3 rotateX(float3 p, float angle) { float s = sin(angle); float c = cos(angle); return float3(p.x, c * p.y - s * p.z, s * p.y + c * p.z); } float3 rotateY(float3 p, float angle) { float s = sin(angle); float c = cos(angle); return float3(c * p.x + s * p.z, p.y, -s * p.x + c * p.z); } bool intersectCube(float3 rayOrigin, float3 rayDirection, float halfExtent, out float hitDistance) { float3 boxMin = float3(-halfExtent, -halfExtent, -halfExtent); float3 boxMax = float3(halfExtent, halfExtent, halfExtent); float3 invDir = 1.0 / rayDirection; float3 t0 = (boxMin - rayOrigin) * invDir; float3 t1 = (boxMax - rayOrigin) * invDir; float3 tMin3 = min(t0, t1); float3 tMax3 = max(t0, t1); float tMin = max(max(tMin3.x, tMin3.y), tMin3.z); float tMax = min(min(tMax3.x, tMax3.y), tMax3.z); if (tMax < max(tMin, 0.0)) { hitDistance = 0.0; return false; } hitDistance = tMin > 0.0 ? tMin : tMax; return hitDistance > 0.0; } float2 cubeFaceUv(float3 hitPoint, float halfExtent, float zoom) { float3 face = abs(hitPoint); float2 uv = float2(0.5, 0.5); float safeZoom = max(zoom, 0.001); if (face.x >= face.y && face.x >= face.z) { uv = hitPoint.x > 0.0 ? float2(-hitPoint.z, hitPoint.y) : float2(hitPoint.z, hitPoint.y); } else if (face.y >= face.x && face.y >= face.z) { uv = hitPoint.y > 0.0 ? float2(hitPoint.x, -hitPoint.z) : float2(hitPoint.x, hitPoint.z); } else { uv = hitPoint.z > 0.0 ? float2(hitPoint.x, hitPoint.y) : float2(-hitPoint.x, hitPoint.y); } uv = uv / (halfExtent * 2.0 * safeZoom) + 0.5; return frac(uv); } float4 shadeVideo(ShaderContext context) { float2 centeredUv = context.uv * 2.0 - 1.0; float aspect = context.outputResolution.x / max(context.outputResolution.y, 1.0); centeredUv.x *= aspect; float3 rayOrigin = float3(0.0, 0.0, 2.7); float3 rayDirection = normalize(float3(centeredUv, -2.1)); float spin = context.time * spinSpeed; float yaw = spin; float pitch = spin * 0.61 + 0.35; float3 localOrigin = rotateY(rotateX(rayOrigin, -pitch), -yaw); float3 localDirection = rotateY(rotateX(rayDirection, -pitch), -yaw); float halfExtent = max(cubeScale, 0.05); float hitDistance = 0.0; float3 background = lerp(float3(0.02, 0.02, 0.03), context.sourceColor.rgb, saturate(backgroundMix)); if (!intersectCube(localOrigin, localDirection, halfExtent, hitDistance)) return float4(background, 1.0); float3 localHit = localOrigin + localDirection * hitDistance; float2 faceUv = cubeFaceUv(localHit, halfExtent, faceZoom); float4 faceColor = sampleVideo(faceUv); float3 normal; float3 face = abs(localHit); if (face.x >= face.y && face.x >= face.z) normal = float3(sign(localHit.x), 0.0, 0.0); else if (face.y >= face.x && face.y >= face.z) normal = float3(0.0, sign(localHit.y), 0.0); else normal = float3(0.0, 0.0, sign(localHit.z)); normal = rotateX(rotateY(normal, yaw), pitch); float3 lightDir = normalize(float3(0.5, 0.8, 0.6)); float diffuse = saturate(dot(normal, lightDir)) * 0.75 + 0.25; float edge = 1.0 - saturate(max(face.x, max(face.y, face.z)) - halfExtent + 0.03) / 0.03; float3 shaded = faceColor.rgb * diffuse; shaded = lerp(shaded, shaded + 0.12, edge * 0.35); return float4(saturate(shaded), 1.0); }