<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>WPF3D Team Blog : Lighting</title><link>http://blogs.msdn.com/wpf3d/archive/tags/Lighting/default.aspx</link><description>Tags: Lighting</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>WPF3D Lighting and Shading</title><link>http://blogs.msdn.com/wpf3d/archive/2009/06/29/wpf3d-lighting-and-shading.aspx</link><pubDate>Tue, 30 Jun 2009 04:54:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9809090</guid><dc:creator>wpf3d</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/wpf3d/comments/9809090.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wpf3d/commentrss.aspx?PostID=9809090</wfw:commentRss><description>&lt;p&gt;We use the standard fixed-function &lt;a href="http://en.wikipedia.org/wiki/Blinn-Phong_shading_model"&gt;Blinn-Phong model&lt;/a&gt;. You can read up on all of the equations &lt;a href="http://msdn.microsoft.com/en-us/library/bb147178(VS.85).aspx"&gt;here&lt;/a&gt; at MSDN.&lt;/p&gt;  &lt;p&gt;If you have a Tier 2 card, we actually do our lighting in a vertex shader. If you don’t have a Tier 2 card, we do the lighting on the CPU. Why not just use D3D9’s fixed-function APIs you might ask? We wanted to be able to blend a texture (SpecularMaterial.Brush) only with the specular component and it turns out the D3D9 fixed-function APIs can’t do that.&lt;/p&gt;  &lt;p&gt;An important thing to know is all Material Brushes are treated as textures even though they might not be implemented as such (e.g. SolidColorBrush). This means if the amount of light hitting a vertex is more than 1.0, it will be capped at 1.0 before being blended with the texture. This is an unfortunate behavior of the fixed function pipeline. I discussed this more in the &lt;a href="http://blogs.msdn.com/wpf3d/archive/2006/12/08/material-color-knobs.aspx"&gt;color knobs post&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;There is one thing that may be unique to out lighting model. Since we are a retained system, we factor in a Light’s transform to all of its properties. We approximate the scale being applied to the light by taking the cube root of the absolute value of the determinant of the upper 3x3 part of the Light’s transform. For a uniform scale this will be the actual scale but for non-uniform it’s good enough (the real solution is too costly for something uncommon). Internally, this number is factored into the range and attenuation values so things make sense. For example, if the Light undergoes a uniform scale of 2.0, we’ll multiple its range by 2.0.&lt;/p&gt;  &lt;p&gt;-- Jordan&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9809090" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Materials/default.aspx">Materials</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Lighting/default.aspx">Lighting</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/3D/default.aspx">3D</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/WPF/default.aspx">WPF</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Shading/default.aspx">Shading</category></item><item><title>Cel Shading</title><link>http://blogs.msdn.com/wpf3d/archive/2007/09/07/cell-shading.aspx</link><pubDate>Sat, 08 Sep 2007 07:02:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4821848</guid><dc:creator>wpf3d</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/wpf3d/comments/4821848.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wpf3d/commentrss.aspx?PostID=4821848</wfw:commentRss><description>&lt;P&gt;Charles Petzold has been experimenting with cel shading on &lt;A href="http://www.charlespetzold.com/blog/blog.xml" mce_href="http://www.charlespetzold.com/blog/blog.xml"&gt;his blog&lt;/A&gt; at the request of &lt;A href="http://chriscavanagh.wordpress.com/" mce_href="http://chriscavanagh.wordpress.com/"&gt;Chris Cavanagh&lt;/A&gt; (whom has updated his &lt;A href="http://chriscavanagh.wordpress.com/2007/05/26/minor-xbap-update/" mce_href="http://chriscavanagh.wordpress.com/2007/05/26/minor-xbap-update/"&gt;3D physics XBAP&lt;/A&gt; btw). Though we do use shaders internally, WPF3D's API is fixed function so you have to dig out the ol' fixed function playbook to achieve fancier effects. The plays usually boil down to abusing textures :)&lt;/P&gt;
&lt;P&gt;First, make a small, one dimensional texture containing the colors you want. Here are a couple of examples I've zoomed in on and highlighted the pixels to make things clearer. Why I'm using grayscale will make sense in a minute:&lt;/P&gt;
&lt;P align=center&gt;&lt;A href="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip_2.png" mce_href="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip_2.png"&gt;&lt;IMG id=id style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=16 alt=big_strip src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip_thumb.png" width=156 border=0 mce_src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip_thumb.png"&gt;&lt;/A&gt; &lt;BR&gt;&lt;A href="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip2_2.png" mce_href="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip2_2.png"&gt;&lt;IMG id=id style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=16 alt=big_strip2 src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip2_thumb.png" width=156 border=0 mce_src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/big_strip2_thumb.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P align=left&gt;Next, we need a function that maps to [0, 1] and takes into account the light position relative to a vertex. How about the cosine of the angle to the light? Turns out we can compute that fairly quickly because it's equal to the dot product of the normal and the vector to the light divided by their lengths. &lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Int32Collection&lt;/SPAN&gt; idxs = mesh.TriangleIndices;
    &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Point3DCollection&lt;/SPAN&gt; pts = mesh.Positions;
    &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Vector3DCollection&lt;/SPAN&gt; nrms = mesh.Normals;
    &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;PointCollection&lt;/SPAN&gt; txs = mesh.TextureCoordinates;
    mesh.TextureCoordinates = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;null&lt;/SPAN&gt;;
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;for&lt;/SPAN&gt; (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; i = 0, count = idxs.Count; i &amp;lt; count; ++i)
    { 
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; idx = idxs[i]; 
        &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Vector3D&lt;/SPAN&gt; toLight = lightPos - pts[idx]; 
        toLight.Normalize(); 

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// The normals are pre-normalized, no need to do it again &lt;/SPAN&gt; 
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;double&lt;/SPAN&gt; dp = &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Vector3D&lt;/SPAN&gt;.DotProduct(toLight, nrms[idx]); 
        
        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// A negative dot product means the vertex is facing away &lt;/SPAN&gt; 
        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// from the light so let's set that to the darkest color &lt;/SPAN&gt; 
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;if&lt;/SPAN&gt; (dp &amp;lt; 0) 
        { 
            dp = 0; 
        } 
        
        txs[idx] = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;new&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Point&lt;/SPAN&gt;(dp, 0); 
    } 
    mesh.TextureCoordinates = txs;&lt;/PRE&gt;
&lt;P&gt;Naturally, you're going to need to redo this calculation any time the relationship between the light and the mesh changes so it needs to be fast. This is why I go through all of that collection nonsense (&lt;A href="http://blogs.msdn.com/timothyc/archive/2006/08/31/734308.aspx" mce_href="http://blogs.msdn.com/timothyc/archive/2006/08/31/734308.aspx"&gt;read this&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;So why did I use a grayscale texture? If I baked the color in, I'd have to make a new texture any time I wanted to change the color. Instead, I can use the &lt;A class="" href="http://blogs.msdn.com/wpf3d/archive/2006/12/08/material-color-knobs.aspx" mce_href="http://blogs.msdn.com/wpf3d/archive/2006/12/08/material-color-knobs.aspx"&gt;color knobs&lt;/A&gt; to set the color and use the grayscale texture like a mask. In these photos, I have a single AmbientLight with a DiffuseMaterial with AmbientColor="green" and Brush equal to the one dimensional texture. I don't actually have a real point light in the scene which helps performance.&lt;/P&gt;
&lt;P align=center&gt;&lt;IMG src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/cellpot_6.png"&gt; &lt;IMG src="http://blogs.msdn.com/blogfiles/wpf3d/WindowsLiveWriter/CellShading_1204C/celltorus_4.png"&gt;&lt;/P&gt;
&lt;P&gt;Being that it's per-vertex, more vertices means a higher quality image and you're going to see some popping when it's animated. The teapot doesn't have too many vertices so the spout and black line on the body don't look great. On the highly tesselated torus, those issues are gone but it's still hard to get a clean, crisp edge because of the linear interpolation of the texture.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;-- Jordan&lt;/P&gt;
&lt;P&gt;P.S. If you use a texture with two colors and instead dot the normal with the direction to the camera, you'll get silhouette edges but they could look really bad because of the per-vertex nature again.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4821848" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Materials/default.aspx">Materials</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Lighting/default.aspx">Lighting</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/3D/default.aspx">3D</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/WPF/default.aspx">WPF</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Shading/default.aspx">Shading</category></item><item><title>Material Color Knobs</title><link>http://blogs.msdn.com/wpf3d/archive/2006/12/08/material-color-knobs.aspx</link><pubDate>Fri, 08 Dec 2006 23:40:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1237335</guid><dc:creator>wpf3d</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/wpf3d/comments/1237335.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wpf3d/commentrss.aspx?PostID=1237335</wfw:commentRss><description>&lt;P&gt;We affectionately refer to DiffuseMaterial.Color, DiffuseMaterial.AmbientColor, SpecularMaterial.Color, and EmissiveMaterial.Color as the "color knobs." The color knobs are probably the least understood WPF3D feature because they were one of the last (if not the last) features we added and most of the things you can do with them are not obvious. Hopefully this article will give you insight into how the properties work and help you write better XAML exporters.&lt;/P&gt;
&lt;P&gt;The full lighting equation used by WPF&amp;nbsp;is definitely something we need to cover later, but essentially the final color for a single Material is going to be: Color * lighting result * Brush. DiffuseMaterial includes both ambient and diffuse lighting so it has two color properties making the&amp;nbsp;equation:&amp;nbsp;(AmbientColor * ambient lighting result + Color * diffuse lighting result) * Brush. From this equation, you can see that you can't just set Color and be done -- you also need a Brush. By default, all of the Color properties are white giving them no effect on the final appearance.&lt;/P&gt;
&lt;P&gt;Now let's look at a few ways to use these properties.&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;(By the way, if I refer to a color channel going from 0.0 -&amp;gt; 1.0 it's the same as 0 -&amp;gt; 255 if you're using bytes)&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Color Knobs as a Filter&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Let's say a&amp;nbsp;GeometryModel3D with a DiffuseMaterial is being hit by a light containing red. If you set&amp;nbsp;the red channel of the color knob&amp;nbsp;to 0, the mesh won't reflect any red light. If you set it to 1, it'll reflect all of the red light the Brush reflects. By changing the value between 0 and&amp;nbsp;1 you can "dial in" how much you want to reflect back. This is&amp;nbsp;how we started calling them "knobs."&lt;/P&gt;
&lt;P&gt;The color knobs are full colors so they do have alpha. Setting the alpha value does what you would expect (e.g. alpha of 0.5 makes it half transparent). There is one exception to things and, surprise, it's DiffuseMaterial. Only DiffuseMaterial.Color.A affects the transparency of the Model. Turning down DiffuseMaterial.AmbientColor.A does make the ambient&amp;nbsp;component transparent and increase the impact of the diffuse component, but it does not affect Model transparency.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;AmbientMaterial&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;DiffuseMaterial, SpecularMaterial, EmissiveMaterial... where's AmbientMaterial? We don't have it. What do you if you want a Model to respond to only ambient light? Before color knobs, it couldn't be done. With color knobs, create a DiffuseMaterial, set Color to (0,0,0,0), and all diffuse contribution will be wiped out leaving you with just ambient!&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Fully Illuminated Solid Colors&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Say you wanted to make a Model look green. You have two good options: a white brush with a green color knob &lt;EM&gt;or&lt;/EM&gt; a green brush with a white (the default value) color knob. Is there a difference between the two?&amp;nbsp;It depends on how much light is hitting the Model. If the amount of light hitting isn't greater than 1.0 in each channel, then they're equivalent. If not, here's what happens:&lt;/P&gt;
&lt;CENTER&gt;
&lt;P&gt;&lt;IMG title="green knob, white brush" style="WIDTH: 227px; HEIGHT: 228px" height=228 alt="green knob, white brush" src="http://blogs.msdn.com/photos/wpf3d/images/1237189/original.aspx" width=227 mce_src="http://blogs.msdn.com/photos/wpf3d/images/1237189/original.aspx"&gt;&lt;IMG title="white knob, green brush" style="WIDTH: 229px; HEIGHT: 230px" height=230 alt="white knob, green brush" src="http://blogs.msdn.com/photos/wpf3d/images/1237210/original.aspx" width=229 mce_src="http://blogs.msdn.com/photos/wpf3d/images/1237210/original.aspx"&gt;&lt;/P&gt;&lt;/CENTER&gt;
&lt;P&gt;Both pictures are of a sphere mesh with a DiffuseMaterial and two full power point lights shining on it. On the left, the the Color is set to #ff006000 and the Brush is set to white. On the right, the Color is set to white and the Brush is a #ff006000 SolidColorBrush. Why is the one on the left so vibrant and the one on the right so lackluster?&lt;/P&gt;
&lt;P&gt;In the graphics pipeline, lighting is calculated in the vertex processing stage and textures are sampled later on in pixel processing. In WPF, all Brushes applied to 3D are done as textures -- even SolidColorBrushes. Unfortunately, the graphics card clamps colors coming out of the vertex stage to 1 so no matter how many hundreds of lights you have you will never be able to exceed the color of the SolidColorBrush. As you can see in the image on the right, the big solid color splotch in the middle has a value of #ff006000, the color of the SCB. The color knob value is incorporated &lt;EM&gt;during vertex processing&lt;/EM&gt; so it is multiplied by the unclamped lighting result. For those of you used to per-vertex colors in other APIs, you can think of the color knob as a single vertex color for&amp;nbsp;the entire mesh. This explanation is tricky and requires some knowledge of graphics hardware but hopefully it made some sense :)&lt;/P&gt;
&lt;P&gt;In general, you may end up wanting to use the color knobs for all of your solid color needs*. &lt;/P&gt;
&lt;P&gt;-- Jordan&lt;/P&gt;
&lt;P&gt;* There is one minor perf issue to take note of if you don't have a Tier 2&amp;nbsp;graphics card:&amp;nbsp;If you have a MaterialGroup with more than one DiffuseMaterial or more than one SpecularMaterial and those duplicate Materials have different&amp;nbsp;color knob values, you will incur a performance penalty. For example, a MaterialGroup with two DiffuseMaterials one with Color (.5, .5,&amp;nbsp;.5) and one with Color&amp;nbsp;(1, 1, 1).&amp;nbsp;Fortunately, this doesn't happen too often.&amp;nbsp;See &lt;A href="http://msdn2.microsoft.com/en-us/library/ms742196.aspx"&gt;this MSDN article&lt;/A&gt; for information about tiering.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1237335" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Materials/default.aspx">Materials</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/Lighting/default.aspx">Lighting</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/3D/default.aspx">3D</category><category domain="http://blogs.msdn.com/wpf3d/archive/tags/WPF/default.aspx">WPF</category></item></channel></rss>