Skip to content

Instantly share code, notes, and snippets.

Last active November 20, 2019 15:00
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save KeyMaster-/2bb5e20f824241f3caef to your computer and use it in GitHub Desktop.
Luxe polar warp shader
//The source texture that will be transformed
uniform sampler2D tex0;
//The texture coordinate passed in from the vertex shader (the default luxe vertex shader is suffice)
varying vec2 tcoord;
//x: width of the texture divided by the radius that represents the top of the image (normally screen width / radius)
//y: height of the texture divided by the radius representing the top of the image (normally screen height / radius)
uniform vec2 sizeOverRadius;
//This is a constant to determine how far left/right on the source image we look for additional samples
//to counteract thin lines disappearing towards the center (a somewhat adjusted texel width in uv-coordinates)
//This value is 1 / (texture width * 2 PI)
uniform float sampleOffset;
//Linear blend factor that swaps the direction the y-axis of the source is mapped onto the radius
//if the value is 1, y = 0 is at the outer edge of the circle,
//if the value is 0, y = 0 is the center of the circle
uniform float polarFactor;
void main() {
//Make relative to center
vec2 relPos = tcoord - vec2(0.5,0.5);
//Adjust for screen ratio
relPos *= sizeOverRadius;
//Normalised polar coordinates.
//y: radius from center
//x: angle
vec2 polar;
polar.y = sqrt(relPos.x * relPos.x + relPos.y * relPos.y);
//Any radius over 1 would go beyond the source texture size, this simply outputs black for those fragments
if(polar.y > 1.0){
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
polar.x = atan(relPos.y, relPos.x);
//Normally, the angle starts from the axis going right and is counter-clockwise
//I want the left edge of the screen image to be the line upwards,
//so I rotate the angle half pi clockwise
polar.x -= 1.57079632679;
//Normalise from angle to 0-1 range
polar.x /= 6.28318530718;
//Make clockwise
polar.x = -polar.x;
polar.x = mod(polar.x, 1.0);
//The xOffset fixes lines disappearing towards the center of the coordinate system
//This happens because there's only a few pixels trying to display the whole width of the source image
//so they 'miss' the lines. To fix this, we sample at the transformed position
//and a bit to the left and right of it to catch anything we might miss.
//Using 1 / radius gives us minimal offset far out from the circle,
//and a wide offset for pixels close to the center
float xOffset = 0.0;
if(polar.y != 0.0){
xOffset = 1.0 / polar.y;
//Adjusts for texture resolution
xOffset *= sampleOffset;
//This inverts the radius variable depending on the polarFactor
polar.y = polar.y * polarFactor + (1.0 - polar.y) * (1.0 - polarFactor);
//Sample at positions with a slight offset
vec4 one = texture2D(tex0, vec2(polar.x - xOffset, polar.y));
vec4 two = texture2D(tex0, polar);
vec4 three = texture2D(tex0, vec2(polar.x + xOffset, polar.y));
//Take the maximum of the three samples. This is not ideal, but the quickest way to choose a coloured sample over the background colour.
gl_FragColor = max(max(one, two), three);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment