The Mysterious Normals Property

来源:互联网 发布:360panorama 类似软件 编辑:程序博客网 时间:2024/05/16 04:41

The Mysterious Normals Property (Part 1)

December 16, 2006
Roscoe, NY

These two figures are known as square cuboids:

But notice how differently they're colored. The one on the left has gradiated shades of blue with highest intensity in the upper-left-front vertex. In the one on the right, each of the three visible faces is colored uniformly.

Both figures were defined in WPF 3D in the following XAML file:

TwoSquareCuboids.xaml

Both figures have the same light sources, which is a combination of AmbientLight and DirectionalLight. The figure on the left is defined with a mesh geometry with a Positions property that contains the 8 points for the four vertices of the cuboid:

    <MeshGeometry3D Positions="0 0  0, 1 0  0, 1 1  0, 0 1  0,
                               0 0 -4, 1 0 -4, 1 1 -4, 0 1 -4"

                    TriangleIndices="0 1 2, 0 2 3
                                     0 3 4, 3 7 4,
                                     2 6 3, 3 6 7,
                                     1 5 2, 2 5 6,
                                     0 5 1, 0 4 5,
                                     4 6 5, 4 7 6" />

The one on the right defines separate vertices for each of the three visible faces:

    <MeshGeometry3D Positions="0 0 0, 1 0 0, 1 1  0, 0 1  0,
                               1 1 0, 0 1 0, 1 1 -4, 0 1 -4,
                               0 0 0, 0 1 0, 0 0 -4, 0 1 -4"
                                        
                    TriangleIndices="0 1 3, 1 2 3
                                     4 6 5, 5 6 7
                                     8 9 10, 9 11 10" />

The three hidden faces are not defined in this markup (they would require 12 more 3D points and 6 additional integer triplets), but they wouldn't make any difference in the appearance.

So, why does a cube defined with 8 vertices look so different from a cube constructed from separate flat surfaces? It all has to do with the most mysterious property of MeshGeometry3D: Normals.

Forget everything you may have read read in the WPF 3D documentation about Normals being a collection of vectors that determine how faces of 3D figures are oriented. It's just not true. The Normals collection is in a one-to-one correspondence with the Positions collection and determines how vertices — and the edges and faces around these vertices — are oriented and hence, how they reflect directional light. If you don't supply a Normals collection, one is provided for you, which is what's causing the behavior illustrated in this program.

I have made it my short-term goal in life to "crack" the Normals property and figure out how to use it intelligently. I'll be reporting my progress right here.

 

The Mysterious MeshGeometry3D Normals Property (Part 2)

December 18, 2006
Roscoe, NY

Yesterday I showed you two square cuboids in a WPF 3D XAML file that had very different types of shading on their surfaces, and I said that this difference basically had to do with the Normals property of the MeshGeometry3D class. If you don't define a Normals collection, WPF will calculate values for you.

Here are two more square cuboids:

The XAML file is here:

TwoSquareCuboids2.xaml

These two figures are defined nearly identically, and they are illuminated by the same DirectionalLight object. But look at the intensity of the upper-left-front vertex of each figure. Using my little WhatColor utility, I read a decimal RGB value of about 0-0-180 on the vertex of the figure on the left, and about 0-0-248 on the figure on the right. That's a significant difference considering that you really have to dig into the file to figure out exactly what's causing it.

As you know, everything in WPF 3D is built up from triangles. Each of the six sides of these two figures consists of two symmetrical triangles that together form a rectangle. There are basically two ways you can make a rectangle from two triangles:

And yes, it makes a difference. In the XAML file, look at the 1st and 3rd lines of the two TriangleCoordinates collections. The first line indicates the indices within the Positions collection of the coordinates of the two triangles that comprise the front face of the square cuboid. The third line of the TriangleCoordinates property involves the top face. These triangles are defined differently in the two figures, and that difference is what causes the difference in the intensity of that upper-left-front vertex.

When the WPF 3D system renders each triangle, it must determine how much light is reflected from that triangle. For DirectionalLight, the calculation is based on the angle the light makes with the figure. This calculation requires the determination of a vector perpendicular to the surface of the figure, called a normal vector. This normal vector is fairly easy to calculate: The three vertices of the triangle define two vectors, and taking the cross product of those vectors yields the normal vector. The amount of reflected light is then proportional to the negative cosine of the angle between the normal vector and the direction of the light. (If the normal vector and the light-direction vector are both normalized, then this is the same as the negative dot product.)

For example, look at the second figure on the right from yesterday's blog entry. The DirectionalLight has a Direction vector of (2, -4, -1). The normal vector of the front face of the square cuboid is (0, 0, 1). The negative cosine of the angle between these vectors is 0.22. Therefore the front face reflects 22% of the directional light. (Because the brush is blue, only blue light is reflected.) 22% of 255 is 56. Add 64 for the AmbientLight. The front face should have an RGB value (in decimal) of 0-0-110, and WhatColor says that it's actually 0-0-109.

That's the case for individual triangles, or when multiple triangles form a flat surface (as in the second figure from Saturday's blog entry). When multiple triangles meet at common vertices at angles, it gets considerably more complicated. Basically, WPF 3D calculates a normal for each vertex of the figure. Vertices don't seem to have normals, but the WPF calculates a vertex normal as an average of all the normals of all the triangles that meet at that vertex. Then, for each point within the triangle, the reflected light is an interpolation of the reflected light at the three vertices of the triangle.

For example, consider the upper-left-front vertex of the first figure from today's XAML file. Four triangles meet at that vertex — one from the left face, one from the top face, and two from the front face. The normal vectors of these faces are, respectively, (-1, 0, 0), (0, 1, 0), and two times (0, 0, 1), for a total of (-1, 1, 2). The DirectionalLight vector is (2, -4, -1). There is no ambient light. The negative cosine of the angle between (-1, 1, 2) and (2, -4, -1) is 0.71, and 71% of 255 is 181, so the RGB color of that vertex should be (in decimal) 0-0-181. That about matches what I found empirically.

In the second figure, four triangles again meet at that vertex — one from the left face, two from the top face, and one from the front face. The normal vectors are (-1, 0, 0), two times (0, 1, 0), and (0, 0, 1) for a total of (-1, 2, 1). The negative cosine of the angle between (-1, 2, 1) and the DirectionalLight vector of (2, -4, -1) is 0.98, which when multiplied by 255 is 250, for an RGB value of 0-0-250, or about what I found empirically.

Fortunately, everything that's happening here is deterministic and not very complex. Yet, if you don't know how WPF 3D is calculating these vertex normals, it can seem very mysterious and unpredictable. Because the illumination of each point in each triangle is calculated as an interpolation of the illumination of the vertices, sometimes edge enhancement is apparent, and sometimes not. Sometimes bright accented lines cut across the diagonal of a flat rectangular surface, and sometimes not.

What this tells me is that providing an explicit Normals property is crucial if you don't want the illumination of your 3D figures to have the often haphazard results of the default calculation.

 

From: http://www.charlespetzold.com/blog/2006/12/171020.html

原创粉丝点击