vec2 intersectCylinder(vec3 origin, vec3 ray, float radius, float yMin, float yMax) { float a = ray.x * ray.x + ray.z * ray.z; float b = 2.0 * (origin.x * ray.x + origin.z * ray.z); float c = origin.x * origin.x + origin.z * origin.z - radius * radius; float tNear = -1e10; float tFar = 1e10; if (a > 1e-6) { float discriminant = b * b - 4.0 * a * c; if (discriminant < 0.0) return vec2(1e10, -1e10); float sqrtDisc = sqrt(discriminant); tNear = (-b - sqrtDisc) / (2.0 * a); tFar = (-b + sqrtDisc) / (2.0 * a); } if (abs(ray.y) > 1e-6) { float tBottom = (yMin - origin.y) / ray.y; float tTop = (yMax - origin.y) / ray.y; float tCapNear = min(tBottom, tTop); float tCapFar = max(tBottom, tTop); tNear = max(tNear, tCapNear); tFar = min(tFar, tCapFar); } else { if (origin.y < yMin || origin.y > yMax) return vec2(1e10, -1e10); } return vec2(tNear, tFar); } vec3 getCylinderWallColor(vec3 point) { float scale = 0.5; vec3 wallColor = tilesColor; vec3 normal; float r = length(point.xz); float edge = poolRadius - 0.001; if (r > edge) { float angle = atan(point.z, point.x); vec2 uv = vec2(angle / 3.14159 * 0.5 + 0.5, point.y / poolHeight * 0.5 + 0.5); if (useTiles > 0.5) { wallColor = texture(tiles, uv).rgb; } normal = vec3(-point.x / r, 0.0, -point.z / r); } else { vec2 uv = point.xz / (poolRadius * 2.0) + 0.5; if (useTiles > 0.5) { wallColor = texture(tiles, uv).rgb; } normal = vec3(0.0, 1.0, 0.0); } scale /= length(point); vec3 refractedLight = -refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER); float diffuse = max(0.0, dot(refractedLight, normal)); vec2 waterCoord = point.xz / (poolRadius * 2.0) + 0.5; vec4 info = texture(water, waterCoord); if (point.y < info.r) { vec2 causticCoord = (point.xz - point.y * refractedLight.xz / refractedLight.y) / (poolRadius * 2.0); vec4 caustic = texture(causticTex, 0.75 * causticCoord + 0.5); scale += diffuse * caustic.r * 2.0 * caustic.g; } else { vec2 t = intersectCylinder(point, refractedLight, poolRadius, -poolHeight, poolHeight * 2.0); diffuse *= 1.0 / (1.0 + exp(-200.0 / (1.0 + 10.0 * (t.y - t.x)) * (point.y + refractedLight.y * t.y - poolHeight * 2.0 / 12.0))); scale += diffuse * 0.5; } return wallColor * scale; }