Last active
January 21, 2026 17:49
-
-
Save Enichan/4077aa05cc558402491d7b9ce6576910 to your computer and use it in GitHub Desktop.
HLSL Spherize shader function identical to Photoshop's spherize distortion filter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef PI | |
| #define PI 3.14159265359 | |
| #endif | |
| // adapted from https://www.shadertoy.com/view/WstfzH | |
| float2 SpherizeOriginal(float2 uv, float2 center, float radius) { | |
| float2 delta = uv - center; | |
| if (delta.x == 0 && delta.y == 0) return uv; | |
| float dist = length(delta); | |
| if (dist > radius) return uv; | |
| float theta = atan2(delta.y, delta.x); | |
| float convexRad = asin(dist / radius) / (PI / 2) * radius; | |
| return center + float2(convexRad * cos(theta), convexRad * sin(theta)); | |
| } | |
| // almost identical to asin https://www.desmos.com/calculator/xxovxrssyc | |
| float FastApproxAsin(float x) { | |
| // c is from circle ease formula sqrt(1-x*x) | |
| float c = 1.0 - sqrt(1.0 - x * x); | |
| // using pow(c, 1.9296) is even more accurate than c*c, but pow is slower | |
| // using sign(x)*PI/2 makes this work for -1 to 1 instead of only 0 to 1 | |
| // (thanks Greg Egan @gregeganSF@mathstodon.xyz for the sign suggestion) | |
| return lerp(x, PI/2, c * c); | |
| } | |
| // optimized version of SpherizeOriginal without atan2 | |
| float2 Spherize(float2 uv, float2 center, float radius, float strength) { | |
| float2 delta = uv - center; | |
| if (delta.x == 0 && delta.y == 0) return uv; | |
| float dist = length(delta); | |
| if (dist > radius) return uv; | |
| // originally before simplification: | |
| // percent = dist / radius | |
| // convexRad = FastApproxAsin(percent) / (PI / 2) * radius | |
| // center + (delta / dist) * convexRad | |
| // | |
| // convexRad is the same term as in the original code | |
| // delta / dist just normalizes the delta vector to turn | |
| // convexRad into a 2D offset | |
| // | |
| // move radius: | |
| // convexPct = FastApproxAsin(percent) / (PI / 2) | |
| // center + (delta / dist) * convexPct * radius | |
| // | |
| // rearrange to: | |
| // center + delta * radius / dist * convexPct | |
| // | |
| // radius / dist is the inverse of percent's dist / radius | |
| // so that term can be replaced with 1/percent: | |
| // center + delta * 1/percent * convexPct | |
| // | |
| // simplifies to center + delta * convexPct / percent | |
| float percent = dist / radius; | |
| float convexPct = FastApproxAsin(percent) / (PI / 2); | |
| return center + delta * (convexPct / percent) * strength; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment