<?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 : Shading</title><link>http://blogs.msdn.com/wpf3d/archive/tags/Shading/default.aspx</link><description>Tags: Shading</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></channel></rss>