-
Notifications
You must be signed in to change notification settings - Fork 28
Creating light sources
In this tutorial we will explain how to use light sources in SVG. Note that these light sources are rather limited, compared to light sources in 3D drawings (where you can have shadows and that kind of features).
In SVG you can define light source elements of different types:
-
feDistantLight
defines a distant light source, similar to how the sun lights everything during the day. -
fePointLight
defines a light source that radiates from a single point, similar to how a light bulb lights a room. -
feSpotLight
defines a light source similar to a spot light that shines intensely over a small area.
The fePointLight seems to me the best match to simulate a light bulb in a home automation floorplan.
We will never be able to create a realistic lightning effect in a 2D SVG drawing, compared to what we are used to in 3D drawings. However we can at least try to make our light source looking a bit better...
To do that, we need to create a filter chain, which allows us to apply a sequence of filters to the light source. The result of a filter will be the input of the next filter.
After a bit of trial and error, I have created the following filter chain:
<filter id="green_light" x="0%" y="0%" width="100%" height="100%">
<feGaussianBlur in = "SourceAlpha" stdDeviation="1" result="blur1" />
<feSpecularLighting result="specOut" in="blur1" specularExponent="30" lighting-color="#00cc00" >
<fePointLight x="141" y="73" z="80" />
</feSpecularLighting>
</filter>
</defs>
Some explanation about this filter chain:
-
The light has been positioned in the middle of the room. Don't know if I can make this relative to the room, because now it is relative to the origin of the SVG, which causes the definition not being reusable for other rooms...
-
The feSpecularLighting has been used to apply a colour to the light, in this case GREEN ("#00cc00").
-
The feGaussianBlur applies some blur to the light.
-
It is important to specify a filter region (x="0%" y="0%" width="100%" height="100%"), because the default region (x="-10%" y="-10%" width="120%" height="120%") would cause the filter to be applied also around our room (rectangle):
As can be seen in the previous drawing, the light will shine through the walls into the next room. Which is not looking good...
To workaround that issue, we will draw a rectangle on top of the room contour. Next we will apply the light filter only to that rectangle:
<image width="100%" height="100%" id="background" xlink:href="https://www.roomsketcher.com/wp-content/uploads/2016/10/1-Bedroom-Floor-Plans.jpg" />
<rect id="rect_living" x="80" y="30" width="123" height="86" fill="black" fill-opacity="0.1" filter=""/>
Some explanation about this:
-
By default we apply an empty filter (i.e. no filter) to this rectangle, because the light is OFF when we start.
-
We set the color to black and opacity to 0.1 to make the room a bit darker (since the light is off by default):
Now you can apply the filter to the rectangle when the light bulb is being clicked.
With this simple flow:
[{"id":"4a7aaca8.bd6084","type":"ui_svg_graphics","z":"7f1827bd.8acfe8","group":"7185c2fb.c66bbc","order":0,"width":"12","height":"12","svgString":"<div class=\"contain-demo\"> \n <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 250 250\" enable-background=\"new 0 0 250 250\">\n <defs>\n <!--The default filter region is x=-10% y=-10% width=120% height=120%-->\n <!--Which need to be shrinked to clip the filter at the room boundaries-->\n <filter id=\"green_light\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n <feGaussianBlur in = \"SourceAlpha\" stdDeviation=\"1\" result=\"blur1\" />\n <feSpecularLighting result=\"specOut\" in=\"blur1\" specularExponent=\"30\" lighting-color=\"#00cc00\" >\n <fePointLight x=\"141\" y=\"73\" z=\"80\" />\n </feSpecularLighting>\n </filter>\n </defs>\n \n <image width=\"100%\" height=\"100%\" id=\"background\" xlink:href=\"https://www.roomsketcher.com/wp-content/uploads/2016/10/1-Bedroom-Floor-Plans.jpg\" />\n\n <rect id=\"rect_living\" x=\"80\" y=\"30\" width=\"123\" height=\"86\" fill=\"black\" fill-opacity=\"0.1\" filter=\"\"/>\n <text id=\"light_living\" x=\"141\" y=\"73\" font-family=\"FontAwesome\" fill=\"green\" stroke=\"darkgreen\" font-size=\"25\" text-anchor=\"middle\" alignment-baseline=\"middle\" stroke-width=\"1\">fa-lightbulb-o</text>\n </svg>\n</div>","clickableShapes":[{"targetId":"#light_living","action":"click","payload":"light_living","payloadType":"str","topic":"light_living"}],"javascriptHandlers":[],"smilAnimations":[],"bindings":[],"showCoordinates":false,"autoFormatAfterEdit":false,"showBrowserErrors":true,"showBrowserEvents":false,"enableJsDebugging":false,"sendMsgWhenLoaded":false,"outputField":"payload","editorUrl":"//drawsvg.org/drawsvg.html","directory":"","panning":"disabled","zooming":"disabled","panOnlyWhenZoomed":false,"doubleClickZoomEnabled":false,"mouseWheelZoomEnabled":false,"dblClickZoomPercentage":150,"name":"","x":680,"y":2400,"wires":[["e4f8bd1d.95b4a"]]},{"id":"e4f8bd1d.95b4a","type":"link out","z":"7f1827bd.8acfe8","name":"SVG output msg","links":["b5e846dd.ae7e88"],"x":815,"y":2400,"wires":[]},{"id":"b5e846dd.ae7e88","type":"link in","z":"7f1827bd.8acfe8","name":"","links":["e4f8bd1d.95b4a"],"x":95,"y":2400,"wires":[["9327a30d.6d2f2"]]},{"id":"6000cdf2.42d564","type":"change","z":"7f1827bd.8acfe8","name":"Update filter attribute","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t\"command\": \"set_attribute\",\t\"selector\": \"#rect_living\",\t\"attributeName\": \"filter\",\t\"attributeValue\": payload\t} ","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"event","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":2400,"wires":[["4a7aaca8.bd6084"]]},{"id":"9327a30d.6d2f2","type":"function","z":"7f1827bd.8acfe8","name":"Toggle light state","func":"if (msg.topic === \"light_living\") {\n var state = flow.get(\"state_light\");\n \n node.error(\"old state = \" + state );\n \n if (state === \"ON\") {\n state = \"OFF\";\n msg.payload = \"\";\n }\n else {\n state = \"ON\";\n msg.payload = \"url(#green_light)\";\n }\n \n node.error(\"new state = \" + state );\n \n flow.set(\"state_light\", state);\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":230,"y":2400,"wires":[["6000cdf2.42d564"]]},{"id":"7185c2fb.c66bbc","type":"ui_group","z":"","name":"SVG light demo","tab":"35f53152.449cde","order":2,"disp":true,"width":"12","collapse":false},{"id":"35f53152.449cde","type":"ui_tab","z":"","name":"Lights","icon":"dashboard","disabled":false,"hidden":false}]
You can add a light effect to a clickable light bulb icon:
Not sure whether this is the best way to do it, but you can change the specularConstant
:
<feSpecularLighting result="specOut" in="blur1" specularConstant="2" specularExponent="50" lighting-color="#00cc00" >
By default the value is 1, but below you can see the result for some different values:
You can change the specularExponent
to adjust the range of the light:
<feSpecularLighting result="specOut" in="blur1" specularConstant="2" specularExponent="50" lighting-color="#00cc00" >
By default the value is 1, but below you can see the result for some different values: