117 lines
3.2 KiB
Plaintext
117 lines
3.2 KiB
Plaintext
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 + context.startupRandom * 6.2831853;
|
|
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);
|
|
}
|