Luxe polar warp shader

April 5, 2015 ยท View on GitHub

//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);
	return;
}

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);

}