Skip to main content
The cover image for "Drop shadows in SFML"

Drop shadows in SFML

Sidebar

Drop shadows are created using a [Gaussian blur](https://en.wikipedia.org/wiki/Gaussian_blur) drawn underneath the original element. You can do this using either a fixed texture or a post-processing fragment shader.

Drop shadows are created using a Gaussian blur drawn underneath the original element. You can do this using either a fixed texture or a post-processing fragment shader.

Texture-based Approach #

If you do not need to have dynamic drop shadows, you should consider baking the shadow into a texture and directly rendering that. However, a static shadow is not suitable for things like text drop shadows, which will change drastically depending on user input.

Shader-based Approach #

The blur is done in two passes - horizontal and vertical. A pass blurs each pixel using the weighted sum of its neighbours along a direction, up to some radius. Each pass is done using the same shader, which is given different values for direction.

A text drawn with and without a drop shadow
A text drawn with and without a drop shadow
uniform sampler2D source;
uniform vec4 mask; // shadow color
uniform vec2 direction; // step size

void main() {
	vec2 textureCoordinates = gl_TexCoord[0].xy;

	vec4 color = vec4(0.0);
	color += texture2D(source, textureCoordinates - 4.0 * direction) * 0.0162162162;
	color += texture2D(source, textureCoordinates - 3.0 * direction) * 0.0540540541;
	color += texture2D(source, textureCoordinates - 2.0 * direction) * 0.1216216216;
	color += texture2D(source, textureCoordinates - direction) * 0.1945945946;
	color += texture2D(source, textureCoordinates) * 0.2270270270;
	color += texture2D(source, textureCoordinates + direction) * 0.1945945946;
	color += texture2D(source, textureCoordinates + 2.0 * direction) * 0.1216216216;
	color += texture2D(source, textureCoordinates + 3.0 * direction) * 0.0540540541;
	color += texture2D(source, textureCoordinates + 4.0 * direction) * 0.0162162162;
	color = mask * vec4(100.0, 100.0, 100.0, color[3]) / 100.0;

	gl_FragColor = color;
}

This needs to be done using post-processing. The way to do this in SFML is to render your scene to a RenderTexture, which you then render to the screen using a fragment shader like so:

// This is an example, `texture` is a RenderTexture
sf::Sprite sprite(texture.getTexture());

shadowShader->setUniform("mask", sf::Glsl::Vec4(0, 0, 0, 50));
shadowShader->setUniform("direction", sf::Vector2f(0.f, pixelSize));
sf::RenderStates states = baseStates;
states.shader = shadowShader.get();
states.transform.translate(pos);
target.draw(sprite, states);

The source drawable should be rendered to a temporary RenderTexture. This RenderTexture is then blurred in one direction to another RenderTexture. This final RenderTexture is then blurred in the other direction to the screen. The reason that you render the drawable to a RenderTexture before you first blur it is to make the fragment shader act like a post-processing shader - you don’t want to act on the source drawable’s faces, but on the resulting pixels.

Full example source code can be found here, with a helper class:

rubenwardy's profile picture, the letter R

Hi, I'm Andrew Ward. I'm a software developer, an open source maintainer, and a graduate from the University of Bristol. I’m a core developer for Luanti, an open source voxel game engine.

Comments

Leave comment

Shown publicly next to your comment. Leave blank to show as "Anonymous".
Optional, to notify you if rubenwardy replies. Not shown publicly.
Max 1800 characters. You may use plain text, HTML, or Markdown.