mirror of https://github.com/mosra/magnum.git
Browse Source
Not yet ported anywhere else than GL >= 3.3 with some GL 4.3 features. Still, this closes #3.pull/278/head
5 changed files with 233 additions and 1 deletions
@ -0,0 +1,97 @@ |
|||||||
|
/*
|
||||||
|
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Magnum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License version 3 |
||||||
|
only, as published by the Free Software Foundation. |
||||||
|
|
||||||
|
Magnum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License version 3 for more details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "TextureTools/DistanceField.h" |
||||||
|
|
||||||
|
#include <Utility/Resource.h> |
||||||
|
#include "Math/Geometry/Rectangle.h" |
||||||
|
#include "AbstractShaderProgram.h" |
||||||
|
#include "Extensions.h" |
||||||
|
#include "Framebuffer.h" |
||||||
|
#include "Mesh.h" |
||||||
|
#include "Shader.h" |
||||||
|
#include "Texture.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace TextureTools { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
class DistanceFieldShader: public AbstractShaderProgram { |
||||||
|
public: |
||||||
|
enum: Int { |
||||||
|
TextureLayer = 8 |
||||||
|
}; |
||||||
|
|
||||||
|
explicit DistanceFieldShader(); |
||||||
|
|
||||||
|
inline DistanceFieldShader* setRadius(Int radius) { |
||||||
|
setUniform(radiusUniform, radius); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
inline DistanceFieldShader* setScaling(Vector2 scaling) { |
||||||
|
setUniform(scalingUniform, scaling); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
static const Int radiusUniform = 0, |
||||||
|
scalingUniform = 1; |
||||||
|
}; |
||||||
|
|
||||||
|
DistanceFieldShader::DistanceFieldShader() { |
||||||
|
MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL330); |
||||||
|
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::explicit_attrib_location); |
||||||
|
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::explicit_uniform_location); |
||||||
|
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::shading_language_420pack); |
||||||
|
|
||||||
|
/** @todo compatibility! */ |
||||||
|
|
||||||
|
Corrade::Utility::Resource rs("MagnumTextureTools"); |
||||||
|
attachShader(Shader::fromData(Version::GL330, Shader::Type::Vertex, rs.get("DistanceFieldShader.vert"))); |
||||||
|
|
||||||
|
Shader fragmentShader(Version::GL330, Shader::Type::Fragment); |
||||||
|
fragmentShader.addSource(rs.get("compatibility.glsl")); |
||||||
|
fragmentShader.addSource(rs.get("DistanceFieldShader.frag")); |
||||||
|
attachShader(fragmentShader); |
||||||
|
|
||||||
|
link(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius) { |
||||||
|
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::framebuffer_object); |
||||||
|
|
||||||
|
/** @todo Disable depth test and then enable it back (if was previously) */ |
||||||
|
|
||||||
|
Framebuffer framebuffer(rectangle); |
||||||
|
framebuffer.attachTexture2D(Framebuffer::ColorAttachment(0), output, 0); |
||||||
|
framebuffer.bind(Framebuffer::Target::Draw); |
||||||
|
|
||||||
|
DistanceFieldShader shader; |
||||||
|
shader.setRadius(radius) |
||||||
|
->setScaling(Vector2(input->imageSize(0))/rectangle.size()) |
||||||
|
->use(); |
||||||
|
|
||||||
|
input->bind(DistanceFieldShader::TextureLayer); |
||||||
|
|
||||||
|
Mesh mesh; |
||||||
|
mesh.setPrimitive(Mesh::Primitive::Triangles) |
||||||
|
->setVertexCount(3) |
||||||
|
->draw(); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
#ifndef Magnum_TextureTools_DistanceField_h |
||||||
|
#define Magnum_TextureTools_DistanceField_h |
||||||
|
/*
|
||||||
|
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Magnum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License version 3 |
||||||
|
only, as published by the Free Software Foundation. |
||||||
|
|
||||||
|
Magnum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License version 3 for more details. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Function Magnum::TextureTools::distanceField() |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum.h" |
||||||
|
|
||||||
|
#include "TextureTools/magnumTextureToolsVisibility.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace TextureTools { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Create signed distance field |
||||||
|
@param input Input texture |
||||||
|
@param output Output texture |
||||||
|
@param rectangle Rectangle in output texture where to render |
||||||
|
@param radius Max lookup radius in input texture |
||||||
|
|
||||||
|
Converts binary image (stored in red channel of @p input) to signed distance |
||||||
|
field (stored in red channel in @p rectangle of @p output). The purpose of this |
||||||
|
function is to convert high-resolution binary image (such as vector artwork or |
||||||
|
font glyphs) to low-resolution grayscale image. The image will then occupy much |
||||||
|
less memory and can be scaled without aliasing issues. Additionally it provides |
||||||
|
foundation for features like outlining, glow or drop shadow essentialy for free. |
||||||
|
|
||||||
|
For each pixel inside @p rectangle the algorithm looks at corresponding pixel in |
||||||
|
@p input and tries to find nearest pixel of opposite color in area given by |
||||||
|
@p radius. Signed distance between the points is then saved as value of given |
||||||
|
pixel in @p output. Value of `0` means that the pixel was originally colored |
||||||
|
white and nearest black pixel is farther than @p radius, value of `1` means that |
||||||
|
the pixel was originally black and nearest white pixel is farther than |
||||||
|
@p radius. Values around `0.5` are around edges. |
||||||
|
|
||||||
|
The resulting texture can be used with bilinear filtering. It can be converted |
||||||
|
back to binary form in shader using e.g. GLSL `smoothstep()` function with step |
||||||
|
around `0.5` to create antialiased edges. Or you can exploit the distance field |
||||||
|
features to create many other effects. |
||||||
|
|
||||||
|
Based on: *Chris Green - Improved Alpha-Tested Magnification for Vector Textures |
||||||
|
and Special Effects, SIGGRAPH 2007, |
||||||
|
http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf*
|
||||||
|
|
||||||
|
@attention This is GPU-only implementation, so it expects active context. |
||||||
|
*/ |
||||||
|
void MAGNUM_TEXTURETOOLS_EXPORT distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius); |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
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; |
||||||
|
} |
||||||
Loading…
Reference in new issue