Learning SVG: lesson 3

I looked at edge bevel in last lesson. It’s time to move on, so in this time I’ll study: more edge bevel. Yup.

When googling for beveling techniques, I found that people often call the following method “bevel”: take a shape, give it some gradient; make a larger version of the same shape, give a inverted gradient, and put it behind the original shape. Sort of like:

Now I’m all for taking shortcuts when possible, but not when it’s incorrect. Granted, I didn’t put much effort to make my example look as good as it could have been, but this method is inherently wrong: it only sort of works if the shape in question is more or less regular–what if you want to bevel a pretzel shaped thing? In this method, the pixel colors on the “bevel” doesn’t depend on the nearby edges, but rather the position in the overall geometry. It’s a fake bevel–more so than other methods as far as manipulating pixels is concerned.

There is another often cited method, however, which is valid: the lighting method. There are defined <feSpecularLight> and <feDiffusedLight> filters, and more often than not, people refer to the use of the former “bevel”. I think that’s also not quite right: you can call it “embossing”, which isn’t the same as “edge bevel”. The diffused light method does work, though, up to some degree (I’m probably too dense to figure it out yet, it really should work completely).

First, this is what a diffused light filter could look like:

		<filter id="difflight" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>
			<feDiffuseLighting surfaceScale="3" diffuseConstant="1">
				<feDistantLight azimuth="45" elevation="45"
						lighting-color="white"/>
			</feDiffuseLighting>
		</filter>

Diff lighting is simply doing bump mapping on the alpha channel, meaning the resulting image’s brightness only differs from background where the opacity is not constant; it becomes brighter or darker depending on whether the direction of the opacity gradient is along the light or not. Without the blur in the begining, the edges would only be 1 pixel thick. Anyway, the filter on our star-circle-square trio produces:

In theory, all we need to do is take the edge part, superimpose it on top of the original image, and we are done. Unfortunately, this is what I haven’t figured out how to do properly. I could make two blurs, one larger than the other (by that I mean dilating it), and subtract the two, the result would be the mask containing only the edge part–but that seems redudant, because if I’m going to make two Gaussian blurs and take the difference, I don’t need the lighting at all:

		<filter id="gauss-diff" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>

			<feOffset in="blur" dx="1" dy="1" result="blur1"/>
			<feOffset in="blur" dx="-1" dy="-1" result="blur2"/>

			<feComposite in="blur2" in2="blur1" operator="arithmetic" k2="1" k3="-1" result="diff1"/>
			<feFlood flood-color="black"/>
			<feComposite in2="diff1" operator="in" result="diff1"/>

			<feComposite in="blur1" in2="blur2" operator="arithmetic" k2="1" k3="-1" result="diff2"/>
			<feFlood flood-color="white"/>
			<feComposite in2="diff2" operator="in" result="diff2"/>

			<feMerge>
				<feMergeNode in="SourceGraphic"/>
				<feMergeNode in="diff1"/>
				<feMergeNode in="diff2"/>
			</feMerge>

which looks like this:

But still, the edges of diffused lighting look so nice, there has to be a way to use it. Need to think harder.

Anyway, I’m probably done with edge bevels–for now. Here’s a combined bevel/shadow filter to wrap things up:

		<filter id="bevel-shadow" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>

			<feOffset in="blur" dx="1" dy="1" result="blur1"/>
			<feOffset in="blur" dx="0" dy="0" result="blur2"/>

			<feComposite in="blur2" in2="blur1" operator="arithmetic" k2="1" k3="-1" result="diff1"/>
			<feFlood flood-color="black"/>
			<feComposite in2="diff1" operator="in" result="diff1"/>

			<feComposite in="blur1" in2="blur2" operator="arithmetic" k2="1" k3="-1" result="diff2"/>
			<feFlood flood-color="white" flood-opacity="0.5"/>
			<feComposite in2="diff2" operator="in" result="diff2"/>

			<feMorphology operator="erode" radius="2" in="SourceAlpha"/>
			<feGaussianBlur stdDeviation="2" result="blur"/>
			<feOffset dy="3" dx="3"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="shadowDiff"/>
			<feFlood flood-color="black" flood-opacity=".8"/>
			<feComposite in2="shadowDiff" operator="in" result="shadow"/>

			<feMerge>
				<feMergeNode in="SourceGraphic"/>
				<feMergeNode in="shadow"/>
				<feMergeNode in="diff1"/>
				<feMergeNode in="diff2"/>
			</feMerge>
		</filter>

and the result:

Thus ends lesson 3.

Advertisements
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: