@ -63,22 +63,124 @@ out lowp float value;
#endif
#ifdef TEXELFETCH_USABLE
mediump ivec2 rotate ( const mediump ivec2 vec ) {
return ivec2 ( - vec . y , vec . x ) ;
}
bool hasValue ( const mediump ivec2 position , const mediump ivec2 offset ) {
return texelFetch ( textureData , position + offset , 0 ) . r > 0.5 ;
return texelFetch ( textureData , position + offset , 0 ) . r > 0.5 ;
}
#else
mediump vec2 rotate ( const mediump vec2 vec ) {
return vec2 ( - vec . y , vec . x ) ;
bool hasValue ( const mediump vec2 position , const mediump ivec2 offset ) {
return texture ( textureData , position + ( vec2 ( offset ) + vec2 ( 0.5 ) ) * imageSizeInverted ) . r > 0.5 ;
}
#endif
mediump int findMinDistanceSquared ( const mediump
# ifdef TEXELFETCH_USABLE
ivec2
# else
vec2
# endif
position , const bool isInside )
{
/* Initialize minimal distance to a value just outside the radius */
mediump int minDistanceSquared = ( RADIUS + 1 ) * ( RADIUS + 1 ) ;
/* Go in cocentric squares around the point */
for ( int i = 1 ; i <= RADIUS ; + + i ) {
/ * First check the nearest points , since that ' s only four combinations .
If any of the four values is opposite of what is on ` position ` , we
found the nearest value . If the distance is not less than what ' s
found already , we don ' t even check the texture .
i = 1 i = 2 i = 3
0
0
0
1 o3 1 o 3 1 o 3
2
2
2
Since everything else in the cocentric square and all others will be
further away , we can stop if we found something . * /
const mediump int centerDistanceSquared = i * i ;
if ( centerDistanceSquared >= minDistanceSquared )
return minDistanceSquared ;
if ( hasValue ( position , ivec2 ( 0 , i ) ) != isInside ||
hasValue ( position , ivec2 ( - i , 0 ) ) != isInside ||
hasValue ( position , ivec2 ( 0 , - i ) ) != isInside ||
hasValue ( position , ivec2 ( i , 0 ) ) != isInside ) {
return centerDistanceSquared ;
}
/ * Now check for points further away , except for the corner points .
Every iteration checks all eight rotations / reflections at the same
distance . Again , if the distance is not less than what ' s found
already , we don ' t even check the texture - - but can ' t return , since
next iteration can still have closer values .
i = 1 i = 2 i = 3
( none )
91 0 8
1 0 a f
2 7 2 7
o o o
3 6 3 6
4 5 b e
c4 5 d
Once we find something , it ' s the closest value possible in this
cycle , so we stop the cycle . But next iterations can still have
values that are closer , so can ' t return . * /
for ( int j = 1 ; j < RADIUS ; + + j ) {
/ * Don ' t go further than current radius - 1 ( i . e . , excluding the
corner ) . The loop needs to be compile - time bound otherwise some
drivers crash on it , so we ' re breaking inside instead of having
this directly in the loop condition . * /
if ( j >= i ) break ;
const mediump int sideDistanceSquared = i * i + j * j ;
if ( sideDistanceSquared >= minDistanceSquared )
break ;
if ( hasValue ( position , ivec2 ( j , i ) ) != isInside ||
hasValue ( position , ivec2 ( - j , i ) ) != isInside ||
hasValue ( position , ivec2 ( - i , j ) ) != isInside ||
hasValue ( position , ivec2 ( - i , - j ) ) != isInside ||
hasValue ( position , ivec2 ( - j , - i ) ) != isInside ||
hasValue ( position , ivec2 ( j , - i ) ) != isInside ||
hasValue ( position , ivec2 ( i , - j ) ) != isInside ||
hasValue ( position , ivec2 ( i , j ) ) != isInside ) {
minDistanceSquared = sideDistanceSquared ;
break ;
}
}
/ * Finally , check for the corners , which is again just four cases :
i = 1 i = 2 i = 3
1 0
1 0
1 0
o o o
2 3
2 3
2 3
If we find something , it ' s most probably not the nearest distance ,
since the following iterations can be much closer . * /
const mediump int cornerDistanceSquared = 2 * i * i ;
if ( cornerDistanceSquared >= minDistanceSquared )
continue ;
if ( hasValue ( position , ivec2 ( i , i ) ) != isInside ||
hasValue ( position , ivec2 ( - i , i ) ) != isInside ||
hasValue ( position , ivec2 ( - i , - i ) ) != isInside ||
hasValue ( position , ivec2 ( i , - i ) ) != isInside ) {
minDistanceSquared = cornerDistanceSquared ;
}
}
bool hasValue ( const mediump vec2 position , const mediump vec2 offset ) {
return texture ( textureData , position + offset ) . r > 0.5 ;
return minDistanceSquared ;
}
#endif
void main ( ) {
# ifdef TEXELFETCH_USABLE
@ -91,65 +193,14 @@ void main() {
const mediump vec2 position = ( gl_FragCoord . xy - vec2 ( 0.5 ) ) * scaling * imageSizeInverted ;
# endif
/ * If pixel at the position is inside ( 1 ) , we are looking for nearest pixel
outside and the value will be positive ( > 0.5 ) . If it is outside ( 0 ) , we
are looking for nearest pixel inside and the value will be negative
( < 0.5 ) . * /
# ifdef TEXELFETCH_USABLE
/ * If pixel at the position is inside ( its value > 0.5 ) , we are looking for
nearest pixel outside and the value will be positive ( or > 0.5 after
normalization ) . If it is outside ( its value < 0 ) , we are looking for
nearest pixel inside and the value will be negative ( or < 0.5 ) . * /
const bool isInside = hasValue ( position , ivec2 ( 0 , 0 ) ) ;
# else
const bool isInside = hasValue ( position , vec2 ( 0.0 , 0.0 ) ) ;
# endif
const highp float sign = isInside ? 1.0 : - 1.0 ;
/* Minimal found distance is just out of the radius (i.e. infinity) */
highp float minDistanceSquared = float ( ( RADIUS + 1 ) * ( RADIUS + 1 ) ) ;
/* Go in circles around the point and find nearest value */
int radiusLimit = RADIUS ;
for ( int i = 1 ; i <= RADIUS ; + + i ) {
int jmax = i * 2 ;
for ( int j = 0 ; j < RADIUS * 2 ; + + j ) {
# ifdef TEXELFETCH_USABLE
const lowp ivec2 offset = ivec2 ( - i + j , i ) ;
# else
const lowp vec2 pixelOffset = vec2 ( float ( - i + j ) , float ( i ) ) + vec2 ( 0.5 ) ;
const lowp vec2 offset = pixelOffset * imageSizeInverted ;
# endif
/ * If any of the four values is opposite of what is on the pixel ,
we found nearest value * /
if ( hasValue ( position , offset ) == ! isInside ||
hasValue ( position , rotate ( offset ) ) == ! isInside ||
hasValue ( position , rotate ( rotate ( offset ) ) ) == ! isInside ||
hasValue ( position , rotate ( rotate ( rotate ( offset ) ) ) ) == ! isInside ) {
# ifdef TEXELFETCH_USABLE
const mediump float distanceSquared = dot ( vec2 ( offset ) , vec2 ( offset ) ) ;
# else
const mediump float distanceSquared = dot ( pixelOffset , pixelOffset ) ;
# endif
/ * Set smaller distance , if found , or continue with lookup for
smaller * /
if ( minDistanceSquared < distanceSquared ) continue ;
else minDistanceSquared = distanceSquared ;
/ * Set radius limit to max radius which can contain smaller
value , e . g . for distance 3.5 we can find smaller value even
in radius 3 * /
# ifdef NEW_GLSL
radiusLimit = min ( RADIUS , int ( floor ( length ( vec2 ( offset ) ) ) ) ) ;
# else
radiusLimit = int ( min ( float ( RADIUS ) , floor ( length ( vec2 ( offset ) ) ) ) ) ;
# endif
}
if ( j + 1 >= jmax ) break ;
}
if ( i + 1 > radiusLimit ) break ;
}
const mediump float minDistance = sqrt ( float ( findMinDistanceSquared ( position , isInside ) ) ) ;
/* Final signed distance, normalized from [-radius-1, radius+1] to [0, 1] */
value = sign * sqrt ( minDistanceSquared ) / float ( RADIUS * 2 + 2 ) + 0.5 ;
const highp float halfSign = isInside ? 0.5 : - 0.5 ;
value = halfSign * minDistance / float ( RADIUS + 1 ) + 0.5 ;
}