precision mediump float; uniform float u_time; uniform vec2 u_resolution; uniform sampler2D u_charTexture; uniform vec2 u_charPos; // Normalized 0-1 (Bottom-Left) uniform vec2 u_charSize; // Normalized 0-1 (Width, Height) varying vec2 vUv; // reliable pseudo-random hash float random(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } void main() { // Correct UV for aspect ratio if needed, but here we work in screen space mostly // Background Noise // Use floor to ensure we are sampling integer grid, making it purely pixel-noise vec2 gridPos = floor(gl_FragCoord.xy); float bgNoise = random(gridPos); float val = step(0.5, bgNoise); // Character Composition vec2 charEnd = u_charPos + u_charSize; bool inBox = (vUv.x >= u_charPos.x && vUv.x <= charEnd.x && vUv.y >= u_charPos.y && vUv.y <= charEnd.y); if (inBox) { vec2 texUv = (vUv - u_charPos) / u_charSize; texUv.y = 1.0 - texUv.y; vec4 texColor = texture2D(u_charTexture, texUv); // Hard threshold to avoid any smooth edges revealing the shape if (texColor.a > 0.5) { // Inner Character Noise - Scrolls Down float scrollSpeed = 200.0; // Calculate scrolled position vec2 scrollCoords = gl_FragCoord.xy; scrollCoords.y += u_time * scrollSpeed; // IMPORTANT: Floor the scrolled coordinates! // This ensures the noise "pixels" look exactly like the background "pixels" in size and sharpness. // If we don't floor, we get sub-pixel sampling which might look different (smooth/shimmering). vec2 charGridPos = floor(scrollCoords); // Use different seed offset to distinguish content, but same distribution float charNoise = random(charGridPos + vec2(42.0, 99.0)); val = step(0.5, charNoise); } } gl_FragColor = vec4(vec3(1.0 - val), 1.0); }