Create SVG Lighting Effects With The feDiffuseLighting Filter Primitive

Lighting effects can be used to show the 3-dimensionality of an object, to brighten an object, or to illuminate a specific part of an object or background.

I’ve been looking at filter primitives the last few weeks, first covering the feColorMatrix and feComponentTransfer primitives as a mean for adding color effects to an image or graphic.

Last week I began discussing lighting effects and talked about the different types of light sources and the SVG primitives associated with them. Today I want to continue and talk about the two types of lighting and the the primitive for one of those types (feDiffuseLighting). I’ll talk about the primitive for the other type (feSpecularLighting) next week.

Note: This post assumes you read the one from last week where I talked about the three different light sources, feDistantLight, fePointLight, and feSpotLight. If you’re unfamiliar with them, you’ll want to read my post from last week before continuing with this one.

Two Types of Lighting

When light hits an object some of the light is absorbed by the object and some is reflected. The light rays that strike the object strike it an an angle called the angle of incidence. The reflected light leaves the object with an angle of reflection.

Specular and Diffuse Reflections

If the object is perfectly smooth, say a mirror or a perfectly still lake, the light rays will all strike the object at exactly the same angle of incidence and they’ll all reflect off the object with the exact same angle of reflection.

However if the object has a rough surface, like a field of gravel the light rays will strike the object at different angles because of the uneven surface. The light will naturally reflect from the object at different angles as well.

Diffuse lighting is light striking a rough surface. The reflected light illuminates the object and because the light leaves the object at all sorts of angles the luminance tends to be equal in all directions.

Specular lighting is light striking a smooth surface. The reflected light highlights instead of illuminates because all the light leaves at the exact same angle. Here’s a simple primer if you’d like to know more.

The two types of SVG lighting primitives, feDiffuseLighting and feSpecularLighting, are meant to mimic these different types of light.

The feDiffuseLighting Filter Primitive

The feDiffuseLighting filter primitive lights an image using the alpha channel of the image as a bump map. Bump maps simulate bumps and wrinkles on the surface of an object without changing the underlying object. With diffuse lighting the sides of the object facing the light are brighter and the sides facing away are darker and in shadow.

The primitive uses the diffuse component of the Phong lighting model in calculations and produces an image that depends on the color of the light, the position of the light, and the surface geometry of the bump map. The alpha value of the final image will be 1.0 everywhere.

That probably means as little to you as it did to me when I first read through what the spec said about feDiffuseLighting. Keep reading, though.

The feDiffuseLighting filter primitive takes three attributes, surfaceScale, diffuseConstant, and kernelUnitLength.

  • surfaceScale = "<number>”—The height of the surface for an alpha value of 1. It’s a factor that is multiplied by the alpha value. The default value is 1.
  • diffuseConstant = “<number>”—kd in the Phong lighting model. It’s used to determine the final RGB value of a given pixel and the brighter your lighting-color, the smaller you want this number to be. The diffuseConstant can be any non-negative number and the default is also 1.
  • kernelUnitLength = “<number-optional-number>”—Indicates the intended distance for dx and dy in the surface normal calculation formulas. The first number is dx and the second if included is dy. If only one value is supplied it becomes both dx and dy.

Still confused? Join the club. I had a hard time creating an example so I borrowed one from the Mozilla Developer Network and modified it a little.

Let’s start with an unlit circle so we have a baseline for comparison. There’s nothing special here. It’s just an ordinary green circle in the center of the viewport.

<svg width="100%" height="250" style="outline: 1px solid red"> 
 <circle cx="330" cy="125" r="100" fill="green" />

Here’s what it looks like.

Now let’s add a filter that lights the circle using the feDiffuseLighting filter primitive. As a reminder here’s the form the primitive should take. If you need more than a reminder, take a look at last week’s post where I talked about this in greater detail.

<type-of-lighting in="" lighting-color="">
   <light source />
 </type of lighting>

We know the type of lighting will be feDiffuseLighting and I’ll give three examples, one for each light source, feDistantLight, fePointLight, and feSpotLight.

An feDistantLight Example

We’ll start with feDistantLight. I used the same green circle, but this time I added a reference to a filter by setting its id to distant. The filter uses feDiffuseLighting on the SourceGraphic and sends the result to “light” I chose a lighting-color of white. I also set surfaceScale, diffuseConstant, and kernalUnitLength to their default of 1.

The light source is feDistantLight and I used values of 45 and 90 degrees for the azimuth and elevation.

Finally I combined the SourceGraphic with the result of the feDiffuseLighting primitive using the feComposite filter primitive so the new lit circle sits on top of the original circle.

<svg width="100%" height="250" style="outline: 1px solid red"> 
 <filter id="distant">
  <feDiffuseLighting in="SourceGraphic" result="light" lighting-color="white" surfaceScale="1" diffuseConstant="1" kernelUnitLength="1">
   <feDistantLight azimuth="45" elevation="90"/>
  <feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="0" k3="0" k4="0"/>
  <circle cx="330" cy="125" r="100" fill="green" filter="url(#distant)" />

Here’s the result, which you can see is a similar, though brighter, circle as the one we started with.

You might have expected something more dramatic, but this actually makes sense when you think about the light source, which is set to mimic the sun directly overhead. Because the elevation is high enough, it lights the circle evenly and brightens it.

If you change the elevation to something larger or smaller than 90 degrees, you’ll find the circle is a little less bright. I experimented with different values for the azimuth, though I couldn’t find any value that led to a noticeable change.

An fePointLight Example

Now let’s change the light source to fePointLight. Everything else in the example remains the same. We need to define the location of the light source and I used values of 140, 140, and 50 for x, y, and z.

<svg width="100%" height="250" style="outline: 1px solid red">
 <filter id="point">
  <feDiffuseLighting in="SourceGraphic" result="light" lighting-color="white" surfaceScale="1" diffuseConstant="1" kernelUnitLength="1">
   <fePointLight x="140" y="140" z=50" />
  <feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="0" k3="0" k4="0" />
  <circle cx="330" cy="125" r="100" fill="green" filter="url(#point)" />

The result makes the circle appear as 3-dimensional sphere being lit from somewhere to the left and above. Changing the values of x and y will change the location of the sphere that’s lit.

Changing the value of z brings the light sources closer to or further from the sphere. Reducing the value will leave the overall sphere darker and leave a highlight over a smaller area. Increasing the value will do the opposite and if you increase the value of z to something like 500, you’ll effectively turn the point light into a distant light. The sphere will again look like a circle, albeit a brighter one than the original.

An feSpotLight Example

Finally let’s try a spot light using feSpotLight. I had the most difficulty controlling the light using this light source. Again everything in the example is the same other than the light source.

With feSpotLight we need to define the location of the light source (x, y, x), where the light points (pointsAtX, pointsAtY, pointsAtZ), and the size of the area lit (limitingConeAngle).

<svg width="100%" height="250" style="outline: 1px solid red">
 <filter id="spot">
  <feDiffuseLighting in="SourceGraphic" result="light" lighting-color="white">
   <feSpotLight x="505" y=130" z="50" limitingConeAngle="15" pointsAtX="245" pointsAtY="230" pointsAtZ="0"/>
  <feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="0" k3="0" k4="0"/>
 <circle cx="330" cy="125" r="100" fill="green" filter="url(#spot)" />

Notice the result is a mostly dark circle, with only a small part of it lit. The light sources appears to be located in the upper left and it’s pointing down and to the right from that point. What I think we’re seeing is part of the area being lit with the rest of the area being just to the left and slightly down from the edge of the circle.

The main reason I found feSpotLight the most difficult to work with is that I seldom ended up with a result I expected based on the values I used. I did attempt to have the source in the upper left and pointing down and to the right in this example, though it took quite a bit of experimentation to get there.

In case this helps in your own trial and error, I started by keeping the limitingConeAngle small and then I tried to position the spot light directly over the center of the circle and pointing to the center as well.

I did that by keeping x and y the same as pointsAtX and pointsAtY. For pointsAtX I used 0 and I kept z to something not too great. Once I had a small lit area in the center of the circle, I experimented with the values one at a time to get a feel for what it would do to the light.

I’m still not sure I have a great handle on what values to use next time, but I think I have a better handle on them than when I started working on this post.

Closing Thoughts

Diffuse lighting is light striking a rough surface where the angle of reflected light will vary depending on where the light strikes the surface. SVG allows us to work with diffuse lighting through the feDiffuseLighting filter primitive.

I admit I had difficulty working with this primitive, though I think my difficulty comes mainly from a lack of experience with it. I was often unsure what the result would be when changing some of the light source values as well as the three attributes feDiffuseLighting takes. I have a better sense now than I did before playing around with these examples, but still not as good a sense as I have working with the other primitives. I’m telling you in case you feel the same, you’ll know you aren’t alone. And if you don’t feel the same, feel free to enlighten me.

Next week I’ll talk about the other type of lighting, specular lighting and it’s associated filter primitive feSpecularLighting. I’ll walk through examples using the three different light sources and hopefully I’ll have a better handle on the examples than I did this week.

« »

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *