physics-glass-effects
Version:
Physics-based glass effects using WebGL with real optical science - works with React, Vue, Next.js, and vanilla JS
2 lines • 9.94 kB
TypeScript
export declare const fragmentShaderSource = "\nprecision highp float;\n\nuniform float u_time;\nuniform vec2 u_resolution;\nuniform vec2 u_mousePos;\nuniform float u_refractionIndex;\nuniform float u_dispersion;\nuniform float u_thickness;\nuniform float u_glassSize;\nuniform float u_glassShape;\nuniform float u_backgroundPattern;\nuniform sampler2D u_backgroundTexture;\n\nvarying vec2 v_uv;\n\n#define PI 3.14159265359\n\n// Noise function for surface perturbations\nfloat noise(vec2 p) {\n return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nfloat fbm(vec2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n \n for(int i = 0; i < 4; i++) {\n value += amplitude * noise(p * frequency);\n amplitude *= 0.5;\n frequency *= 2.0;\n }\n return value;\n}\n\n// Ray-sphere intersection\nvec2 intersectSphere(vec3 rayOrigin, vec3 rayDir, vec3 sphereCenter, float radius) {\n vec3 oc = rayOrigin - sphereCenter;\n float b = dot(oc, rayDir);\n float c = dot(oc, oc) - radius * radius;\n float discriminant = b * b - c;\n \n if (discriminant < 0.0) return vec2(-1.0);\n \n float sqrt_discriminant = sqrt(discriminant);\n float t1 = -b - sqrt_discriminant;\n float t2 = -b + sqrt_discriminant;\n \n return vec2(t1, t2);\n}\n\n// Ray-cylinder intersection (infinite height)\nvec2 intersectCylinder(vec3 rayOrigin, vec3 rayDir, vec3 cylinderCenter, float radius) {\n vec2 ro = rayOrigin.xy - cylinderCenter.xy;\n vec2 rd = rayDir.xy;\n \n float a = dot(rd, rd);\n float b = 2.0 * dot(ro, rd);\n float c = dot(ro, ro) - radius * radius;\n \n float discriminant = b * b - 4.0 * a * c;\n if (discriminant < 0.0) return vec2(-1.0);\n \n float sqrt_discriminant = sqrt(discriminant);\n float t1 = (-b - sqrt_discriminant) / (2.0 * a);\n float t2 = (-b + sqrt_discriminant) / (2.0 * a);\n \n return vec2(t1, t2);\n}\n\n// Get sphere normal at point\nvec3 getSphereNormal(vec3 point, vec3 center) {\n return normalize(point - center);\n}\n\n// Get cylinder normal at point\nvec3 getCylinderNormal(vec3 point, vec3 center) {\n vec3 toPoint = point - center;\n return normalize(vec3(toPoint.xy, 0.0));\n}\n\n// 3D refraction using Snell's law\nvec3 refract3D(vec3 incident, vec3 normal, float eta) {\n float cosI = dot(-incident, normal);\n float sinT2 = eta * eta * (1.0 - cosI * cosI);\n \n if(sinT2 >= 1.0) {\n // Total internal reflection\n return reflect(incident, normal);\n }\n \n float cosT = sqrt(1.0 - sinT2);\n return eta * incident + (eta * cosI - cosT) * normal;\n}\n\n// Fresnel reflection coefficient\nfloat fresnel(float cosTheta, float n1, float n2) {\n float r0 = pow((n1 - n2) / (n1 + n2), 2.0);\n return r0 + (1.0 - r0) * pow(1.0 - cosTheta, 5.0);\n}\n\n// Generate different background patterns\nvec3 getBackgroundPattern(vec2 uv, float patternType) {\n if (patternType < 0.5) {\n // Black and white stripes\n float stripeWidth = 0.1;\n float stripe = step(0.5, mod(uv.x / stripeWidth, 1.0));\n return vec3(stripe);\n } else if (patternType < 1.5) {\n // Grid pattern\n float gridSize = 0.05;\n vec2 grid = step(0.5, mod(uv / gridSize, 1.0));\n float pattern = max(grid.x, grid.y);\n return vec3(pattern);\n } else if (patternType < 2.5) {\n // Concentric circles\n float dist = length(uv - 0.5);\n float rings = sin(dist * 50.0) * 0.5 + 0.5;\n return vec3(rings);\n } else {\n // Texture pattern - sample from background texture\n return texture2D(u_backgroundTexture, uv).rgb;\n }\n}\n\n// Ray trace through different glass shapes\nvec4 traceGlassShape(vec2 uv, vec2 mouseUV, float shapeType, float glassSize) {\n vec3 rayOrigin = vec3(uv, 2.0);\n vec3 rayDir = vec3(0.0, 0.0, -1.0);\n vec3 glassCenter = vec3(mouseUV, 0.0);\n \n vec2 intersections;\n vec3 normal1, normal2;\n vec3 enterPoint, exitPoint;\n \n // Different glass shapes\n if (shapeType < 0.5) {\n // Sphere\n intersections = intersectSphere(rayOrigin, rayDir, glassCenter, glassSize);\n if (intersections.x < 0.0) return vec4(0.0);\n \n enterPoint = rayOrigin + rayDir * intersections.x;\n exitPoint = rayOrigin + rayDir * intersections.y;\n normal1 = getSphereNormal(enterPoint, glassCenter);\n normal2 = -getSphereNormal(exitPoint, glassCenter);\n \n } else if (shapeType < 1.5) {\n // Cylinder\n intersections = intersectCylinder(rayOrigin, rayDir, glassCenter, glassSize);\n if (intersections.x < 0.0) return vec4(0.0);\n \n enterPoint = rayOrigin + rayDir * intersections.x;\n exitPoint = rayOrigin + rayDir * intersections.y;\n normal1 = getCylinderNormal(enterPoint, glassCenter);\n normal2 = -getCylinderNormal(exitPoint, glassCenter);\n \n } else if (shapeType < 2.5) {\n // Convex lens (approximated as flattened sphere)\n float lensRatio = 1.5;\n vec3 lensCenter = vec3(mouseUV, 0.0);\n intersections = intersectSphere(rayOrigin, rayDir, lensCenter, glassSize * lensRatio);\n if (intersections.x < 0.0) return vec4(0.0);\n \n enterPoint = rayOrigin + rayDir * intersections.x;\n exitPoint = rayOrigin + rayDir * intersections.y;\n normal1 = getSphereNormal(enterPoint, lensCenter);\n normal2 = -getSphereNormal(exitPoint, lensCenter);\n \n // Flatten the lens effect\n normal1.z *= 0.3;\n normal2.z *= 0.3;\n normal1 = normalize(normal1);\n normal2 = normalize(normal2);\n \n } else if (shapeType < 3.5) {\n // Triangular prism (simplified)\n float dist = length(uv - mouseUV);\n if (dist > glassSize) return vec4(0.0);\n \n // Create triangular shape effect\n vec2 centered = uv - mouseUV;\n float angle = atan(centered.y, centered.x);\n float prismEffect = sin(angle * 3.0) * 0.3 + 0.7;\n \n if (dist > glassSize * prismEffect) return vec4(0.0);\n \n // Simplified prism normals\n normal1 = normalize(vec3(sin(angle * 3.0), cos(angle * 3.0), 1.0));\n normal2 = -normal1;\n \n } else {\n // Flat glass\n float dist = length(uv - mouseUV);\n if (dist > glassSize) return vec4(0.0);\n \n normal1 = vec3(0.0, 0.0, 1.0);\n normal2 = vec3(0.0, 0.0, -1.0);\n }\n \n // Add surface perturbations\n vec3 perturbation = vec3(\n fbm(uv * 20.0 + u_time * 0.1) * 0.02,\n fbm(uv * 20.0 + u_time * 0.13) * 0.02,\n 0.0\n );\n normal1 = normalize(normal1 + perturbation);\n normal2 = normalize(normal2 + perturbation);\n \n return vec4(normal1, 1.0); // Return first normal for now\n}\n\nvoid main() {\n vec2 uv = v_uv;\n vec2 mouseUV = u_mousePos;\n \n vec4 glassInfo = traceGlassShape(uv, mouseUV, u_glassShape, u_glassSize);\n \n if (glassInfo.w < 0.5) {\n gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n return;\n }\n \n vec3 normal = glassInfo.xyz;\n \n // Calculate view direction\n vec3 viewDir = vec3(0.0, 0.0, 1.0);\n float cosTheta = abs(dot(viewDir, normal));\n \n // Fresnel effect\n float fresnelTerm = fresnel(cosTheta, 1.0, u_refractionIndex);\n \n // Chromatic dispersion - different wavelengths refract differently\n float redEta = 1.0 / (u_refractionIndex - u_dispersion);\n float greenEta = 1.0 / u_refractionIndex;\n float blueEta = 1.0 / (u_refractionIndex + u_dispersion);\n \n // Calculate refracted rays for each color channel\n vec3 refractedR = refract3D(-viewDir, normal, redEta);\n vec3 refractedG = refract3D(-viewDir, normal, greenEta);\n vec3 refractedB = refract3D(-viewDir, normal, blueEta);\n \n // Calculate distortion based on glass geometry and thickness\n float distortionScale = u_thickness * 0.3;\n \n // For sphere and lens, create strong curvature distortion\n if (u_glassShape < 2.5) {\n vec2 centerOffset = uv - mouseUV;\n float distFromCenter = length(centerOffset);\n float curvatureEffect = 1.0 + distFromCenter * distFromCenter * 2.0;\n distortionScale *= curvatureEffect;\n }\n \n vec2 offsetR = refractedR.xy * distortionScale;\n vec2 offsetG = refractedG.xy * distortionScale;\n vec2 offsetB = refractedB.xy * distortionScale;\n \n // Sample background with strong geometric distortion\n vec3 bgColorR = getBackgroundPattern(uv + offsetR, u_backgroundPattern);\n vec3 bgColorG = getBackgroundPattern(uv + offsetG, u_backgroundPattern);\n vec3 bgColorB = getBackgroundPattern(uv + offsetB, u_backgroundPattern);\n \n vec3 refractedColor = vec3(bgColorR.r, bgColorG.g, bgColorB.b);\n \n // Reflection color\n vec3 reflectionColor = vec3(0.9, 0.95, 1.0);\n \n // Combine reflection and refraction\n vec3 finalColor = mix(refractedColor, reflectionColor, fresnelTerm);\n \n // Add caustic effects\n float caustic = pow(max(0.0, dot(normal, vec3(0.5, 0.5, 1.0))), 3.0);\n finalColor += caustic * 0.4 * vec3(1.0, 1.0, 0.9);\n \n // Glass transparency\n float dist = length(uv - mouseUV);\n float glassMask = smoothstep(u_glassSize + 0.02, u_glassSize - 0.02, dist);\n \n // Make glass more transparent to show background properly\n float alpha = glassMask * 0.4;\n \n // Edge enhancement for glass rim effect\n float edge = 1.0 - smoothstep(u_glassSize - 0.005, u_glassSize, dist);\n alpha += edge * 0.6;\n \n // Ensure the glass effect blends properly with background\n gl_FragColor = vec4(finalColor, alpha);\n}\n";
//# sourceMappingURL=fragment.glsl.d.ts.map