Search code examples
glslshaderwebglfragment-shader

Apply transformation in a fragment shader


I know that I can perform a transformation(rotation,scale) in the vertex shader. However I wonder if I can do the similar thing in the fragment shader.

shadertoy.com - transformation_in_fragment_shade

I tried to apply transformation in fragment shader by multiply the uv but being confused by the result.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;
    uv = fract(uv * 2.);
    vec4 img = texture(iChannel0, uv);
    
    // Output to screen
    fragColor = img;
}

result

I expected the image will be scaled by 2x instead of zoom out, and I'm also confused by the meaning of fract here, why apply this function can split image into 4 chunks.

So the question is, can I apply rotation or scaling in the fragment shader here? Should I use different approach to do the rotation, scaling here?


Solution

  • Texture coordinates are in range [0.0, 1.0]. (0, 0) is the bottom left and (1, 1) is the top right. The texture looks up the texture at a particular coordinate and returns the color from that coordinate. If you multiply the texture coordinates by 2, the coordinates will be in the range [0.0, 2.0]. The coordinates from 1.0 to 2.0 are not on the texture and clamped to 1.0.
    The fract function skips the integral part. For example, 1.5 becomes 0.5. So uv = fract(uv * 2.) maps the coordinates from the interval [0.0, 1.0] to 2 intervals [0.0, 1.0], [0.0, 1.0].
    If you want to zoom in (2x) to the center you need to project the texture between the coordinates (0.25, 0.25) and (0.75, 0.75) in the view:

    uv = uv / 2.0 + 0.25; // maps [0.0, 1.0] to [0.25, 0.75]
    

    You can do the same with a 3x3 matrix:

    mat3 zoom = mat3(
        vec3(0.5, 0.0, 0.0),
        vec3(0.0, 0.5, 0.0),
        vec3(0.25, 0.25, 1.0));
    
    uv = (zoom * vec3(uv, 1.0)).xy;