Back to shaders

Shader test bench

Disco Spinner.fs

misc-isf-shaders utility glsl runnable fragment MIT
Source
runnable fragment

Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.

Code

uniform sampler2D inputImage;
uniform float distance;
uniform float spinX;
uniform float spinY;
uniform float spinZ;
uniform float cropX;
uniform float cropY;
uniform float squeezeX;
uniform float squeezeY;
/*{
  "CREDIT": "ProjectileObjects",
  "DESCRIPTION": "4x Disco Spinner FX: Creates 4 copies of the input image, positioned at cardinal directions with cropping and aspect controls.",
  "ISFVSN": "2",
  "INPUTS": [
    {
      "NAME": "inputImage",
      "TYPE": "image"
    },
    {
      "NAME": "distance",
      "TYPE": "float",
      "DEFAULT": 0.5,
      "MIN": 0.0,
      "MAX": 1.0
    },
    {
      "NAME": "spinX",
      "TYPE": "float",
      "DEFAULT": 0.0,
      "MIN": -3.14,
      "MAX": 3.14
    },
    {
      "NAME": "spinY",
      "TYPE": "float",
      "DEFAULT": 0.0,
      "MIN": -3.14,
      "MAX": 3.14
    },
    {
      "NAME": "spinZ",
      "TYPE": "float",
      "DEFAULT": 0.0,
      "MIN": -3.14,
      "MAX": 3.14
    },
    {
      "NAME": "cropX",
      "TYPE": "float",
      "DEFAULT": 0.0,
      "MIN": 0.0,
      "MAX": 0.5
    },
    {
      "NAME": "cropY",
      "TYPE": "float",
      "DEFAULT": 0.0,
      "MIN": 0.0,
      "MAX": 0.5
    },
    {
      "NAME": "squeezeX",
      "TYPE": "float",
      "DEFAULT": 1.0,
      "MIN": 0.5,
      "MAX": 2.0
    },
    {
      "NAME": "squeezeY",
      "TYPE": "float",
      "DEFAULT": 1.0,
      "MIN": 0.5,
      "MAX": 2.0
    }
  ]
}*/

#ifdef GL_ES
precision mediump float;
#endif

// Rotation matrix for X, Y, and Z axes
mat3 rotationMatrix(float angleX, float angleY, float angleZ) {
  mat3 rotX = mat3(
    1.0, 0.0, 0.0,
    0.0, cos(angleX), -sin(angleX),
    0.0, sin(angleX), cos(angleX)
  );

  mat3 rotY = mat3(
    cos(angleY), 0.0, sin(angleY),
    0.0, 1.0, 0.0,
    -sin(angleY), 0.0, cos(angleY)
  );

  mat3 rotZ = mat3(
    cos(angleZ), -sin(angleZ), 0.0,
    sin(angleZ), cos(angleZ), 0.0,
    0.0, 0.0, 1.0
  );

  return rotZ * rotY * rotX;
}

// Adjust UV coordinates for aspect ratio scaling, cropping, and squeezing
vec2 adjustUV(vec2 uv) {
  // Apply cropping
  uv = uv * (1.0 - vec2(cropX, cropY)) + vec2(cropX, cropY) * 0.5;

  // Apply squeezing
  uv.x *= squeezeX;
  uv.y *= squeezeY;

  // Clamp to [0, 1] to avoid sampling outside the image bounds
  uv = clamp(uv, 0.0, 1.0);
  return uv;
}

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution.xy;
  uv = uv * 2.0 - 1.0; // Normalize to [-1, 1]

  // Aspect ratio correction
  uv.x *= u_resolution.x / u_resolution.y;

  // Rotation matrix
  mat3 rotation = rotationMatrix(spinX, spinY, spinZ);

  // Center positions for the four spinners
  vec3 north = vec3(0.0, 1.0, 0.0);
  vec3 south = vec3(0.0, -1.0, 0.0);
  vec3 east = vec3(1.0, 0.0, 0.0);
  vec3 west = vec3(-1.0, 0.0, 0.0);

  // Apply rotation to the spinner positions
  north = rotation * north;
  south = rotation * south;
  east = rotation * east;
  west = rotation * west;

  // Calculate UV coordinates for each spinner
  vec2 northUV = uv - north.xy * distance;
  vec2 southUV = uv - south.xy * distance;
  vec2 eastUV = uv - east.xy * distance;
  vec2 westUV = uv - west.xy * distance;

  // Adjust UV coordinates for cropping, clamping, and squeezing
  northUV = adjustUV(0.5 + 0.5 * northUV);
  southUV = adjustUV(0.5 + 0.5 * southUV);
  eastUV = adjustUV(0.5 + 0.5 * eastUV);
  westUV = adjustUV(0.5 + 0.5 * westUV);

  // Fetch the texture colors for each spinner
  vec4 colorNorth = texture2D(inputImage, northUV);
  vec4 colorSouth = texture2D(inputImage, southUV);
  vec4 colorEast = texture2D(inputImage, eastUV);
  vec4 colorWest = texture2D(inputImage, westUV);

  // Combine the colors (use max to layer the images without blending)
  vec4 finalColor = max(max(colorNorth, colorSouth), max(colorEast, colorWest));

  gl_FragColor = finalColor;
}