mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
60 lines
2.3 KiB
60 lines
2.3 KiB
|
13 years ago
|
layout(location = 0) uniform int radius;
|
||
|
|
layout(location = 1) uniform vec2 scaling;
|
||
|
|
layout(binding = 8) uniform sampler2D texture;
|
||
|
|
|
||
|
|
layout(pixel_center_integer) in vec4 gl_FragCoord;
|
||
|
|
|
||
|
|
out float value;
|
||
|
|
|
||
|
|
ivec2 rotate(const ivec2 vec) {
|
||
|
|
return ivec2(-vec.y, vec.x);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool hasValue(const ivec2 position, const ivec2 offset) {
|
||
|
|
return texelFetch(texture, position+offset, 0).r > 0.5;
|
||
|
|
}
|
||
|
|
|
||
|
|
void main() {
|
||
|
|
const ivec2 position = ivec2(gl_FragCoord.xy*scaling);
|
||
|
|
|
||
|
|
/* 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). */
|
||
|
|
const bool isInside = hasValue(position, ivec2(0, 0));
|
||
|
|
const float sign = isInside ? 1.0 : -1.0;
|
||
|
|
|
||
|
|
/* Minimal found distance is just out of the radius (i.e. infinity) */
|
||
|
|
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 <= radiusLimit; ++i) {
|
||
|
|
for(int j = 0, jmax = i*2; j != jmax; ++j) {
|
||
|
|
const ivec2 offset = {-i+j, i};
|
||
|
|
|
||
|
|
/* 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) {
|
||
|
|
const float distanceSquared = dot(vec2(offset), vec2(offset));
|
||
|
|
|
||
|
|
/* 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 */
|
||
|
|
radiusLimit = min(radius, int(floor(length(vec2(offset)))));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Final signed distance, normalized from [-radius-1, radius+1] to [0, 1] */
|
||
|
|
value = sign*sqrt(minDistanceSquared)/float(radius*2+2)+0.5;
|
||
|
|
}
|