float2x2 rotation2(float angle)
{
	float c = cos(angle);
	float s = sin(angle);
	return float2x2(c, -s, s, c);
}

float etherMap(float3 p, float time)
{
	p.xz = mul(rotation2(time * 0.4), p.xz);
	p.xy = mul(rotation2(time * 0.3), p.xy);

	float3 q = p * 2.0 + time;
	float wave = sin(q.x + sin(q.z + sin(q.y))) * 0.5;
	return length(p + float3(sin(time * 0.7), sin(time * 0.7), sin(time * 0.7))) * log(length(p) + 1.0) + wave - 1.0;
}

float4 shadeVideo(ShaderContext context)
{
	float2 resolution = max(context.outputResolution, float2(1.0, 1.0));
	float2 fragCoord = context.uv * resolution;
	float2 p = fragCoord / resolution.y - offset;
	float seed = context.startupRandom;
	float time = context.time * speed + seed * 41.0;

	float3 color = float3(0.0, 0.0, 0.0);
	float d = depth;

	for (int i = 0; i <= 5; ++i)
	{
		float3 rayPosition = float3(seed * 0.7 - 0.35, 0.23 - seed * 0.46, 5.0) + normalize(float3(p, -1.0)) * d;
		float rz = etherMap(rayPosition, time);
		float f = clamp((rz - etherMap(rayPosition + float3(0.1, 0.1, 0.1), time)) * 0.5, -0.1, 1.0);
		float3 light = baseColor.rgb + energyColor.rgb * 5.0 * f;
		color = color * light + smoothstep(2.5, 0.0, rz) * density * light;
		d += min(rz, 1.0);
	}

	color = pow(max(color * brightness, float3(0.0, 0.0, 0.0)), float3(1.0 / max(contrast, 0.001)));
	return saturate(lerp(float4(color, 1.0), context.sourceColor, sourceMix));
}
