const float IOR_AIR = 1.0; const float IOR_WATER = 1.333; const vec3 abovewaterColor = vec3(0.25, 1.0, 1.25); const vec3 underwaterColor = vec3(0.4, 0.9, 1.0); uniform float poolHeight; uniform vec2 poolHalfSize; uniform float poolRadius; uniform vec3 light; uniform sampler2D tiles; uniform sampler2D causticTex; uniform sampler2D water; uniform float useTiles; uniform vec3 tilesColor; vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax) { vec3 tMin = (cubeMin - origin) / ray; vec3 tMax = (cubeMax - origin) / ray; vec3 t1 = min(tMin, tMax); vec3 t2 = max(tMin, tMax); float tNear = max(max(t1.x, t1.y), t1.z); float tFar = min(min(t2.x, t2.y), t2.z); return vec2(tNear, tFar); } vec3 getWallColor(vec3 point) { float scale = 0.5; vec3 wallColor = tilesColor; vec3 normal; float edgeX = poolHalfSize.x - 0.001; float edgeZ = poolHalfSize.y - 0.001; if (abs(point.x) > edgeX) { vec2 uv = vec2(point.y / poolHeight, point.z / poolHalfSize.y); if (useTiles > 0.5) { wallColor = texture(tiles, uv * 0.5 + vec2(1.0, 0.5)).rgb; } normal = vec3(-sign(point.x), 0.0, 0.0); } else if (abs(point.z) > edgeZ) { vec2 uv = vec2(point.y / poolHeight, point.x / poolHalfSize.x); if (useTiles > 0.5) { wallColor = texture(tiles, uv * 0.5 + vec2(1.0, 0.5)).rgb; } normal = vec3(0.0, 0.0, -sign(point.z)); } else { vec2 uv = vec2(point.x / poolHalfSize.x, point.z / poolHalfSize.y); if (useTiles > 0.5) { wallColor = texture(tiles, uv * 0.5 + 0.5).rgb; } normal = vec3(0.0, 1.0, 0.0); } scale /= length(point); /* pool ambient occlusion */ /* caustics */ 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 = vec2( point.x / (poolHalfSize.x * 2.0) + 0.5, point.z / (poolHalfSize.y * 2.0) + 0.5 ); vec4 info = texture(water, waterCoord); if (point.y < info.r) { vec2 poolScale = poolHalfSize * 2.0; vec2 causticCoord = (point.xz - point.y * refractedLight.xz / refractedLight.y) / poolScale; vec4 caustic = texture(causticTex, 0.75 * causticCoord + 0.5); scale += diffuse * caustic.r * 2.0 * caustic.g; } else { /* shadow for the rim of the pool */ vec2 t = intersectCube( point, refractedLight, vec3(-poolHalfSize.x, -poolHeight, -poolHalfSize.y), vec3(poolHalfSize.x, poolHeight * 2.0, poolHalfSize.y) ); 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; }