#ifdef GL_ES
precision highp float;
#endif
// Uniform variables provided by the rendering environment
uniform vec2 u_resolution; // Canvas resolution (width, height) in pixels
uniform vec2 u_mouse; // Mouse position (x, y) in pixels
uniform float u_time; // Time in seconds since the shader started running
uniform sampler2D u_texture; // Ascii sprite sheet
uniform vec2 u_texture_resolution; // Ascii sprite sheet size
/**
* Function to adjust UV coordinates to maintain aspect ratio when displaying an image
* Uses the "cover" approach (similar to CSS background-size: cover)
*
* @param imageAR The aspect ratio of the image (width/height)
* @param containerAR The aspect ratio of the container (width/height)
* @param uv The original UV coordinates (0-1 range)
* @return Adjusted UV coordinates that maintain the image's aspect ratio
*/
vec2 cropImage(float imageAR, float containerAR, vec2 uv) {
// Determine if the image is wider (landscape) than the container
bool isLandscape = containerAR < imageAR;
// Calculate the scale factor needed to maintain aspect ratio
float scale = isLandscape ? containerAR / imageAR : imageAR / containerAR;
// Adjust the X or Y coordinate based on the orientation
// We only scale the dimension that needs to be adjusted to maintain ratio
float x = !isLandscape ? uv.x : (uv.x - 0.5) * scale + 0.5; // Center and scale X for landscape
float y = isLandscape ? uv.y : (uv.y - 0.5) * scale + 0.5; // Center and scale Y for portrait
return vec2(x, y);
}
// Constants used for the grid effect
const float renderGridSize = 60.; // Number of cells in the display grid
const float spriteGridSize = 10.; // Number of cells in the ascii sprite sheet (u_texture)
const float speedMultiplier = 8.; // Controls the animation speed
void main() {
// Convert fragment coordinates to normalized [0, 1] space
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
// Calculate aspect ratios for the input texture and the canvas
float videoAR = u_texture_resolution.x / u_texture_resolution.y;
float canvasAR = u_resolution.x / u_resolution.y;
// Adjust UVs to crop the image while maintaining aspect ratio
vec2 adjustedUV = cropImage(videoAR, canvasAR, uv);
// Compute horizontal cell index within the render grid, then normalize to sprite grid
float xIndex = fract(floor(adjustedUV.x * renderGridSize) / spriteGridSize);
float texelX = fract(adjustedUV.x * renderGridSize) / spriteGridSize + xIndex;
// Introduce vertical motion over time (scrolling effect)
float posYOffset = floor(u_time * speedMultiplier) / spriteGridSize;
// Compute vertical cell index (with offset) normalized to sprite the grid
float yIndex = fract(floor(adjustedUV.y * renderGridSize) / spriteGridSize + posYOffset + xIndex);
float texelY = fract(adjustedUV.y * renderGridSize) / spriteGridSize + yIndex;
// Sample the texture at the calculated coordinates to get the color
vec4 texel = texture2D(u_texture, vec2(texelX, texelY));
// Output the final color
gl_FragColor = texel;
}