Title: Draw smooth spheres using WPF and C#
This example shows how you can draw smooth spheres in a 3D WPF program. The example Draw spheres using WPF and C# shows how to generate the points on a sphere. That program adds new positions for each point every time it is used in the sphere so every triangle that makes up the sphere has its own surface normal. That means the triangles seem flat and the sphere isn't smooth.
To draw smooth spheres, you need to make the triangles that share a corner use the same positions in the mesh data. The example Draw smooth cylinders using WPF and C# uses that technique to draw smooth cylinders. You could use the same approach to draw smooth spheres. You would carefully figure out where each triangle corner was and then make the appropriate triangles share the points. That approach isn't too bad with a cylinder where each point is shared by four adjacent triangles that differ by the angle theta, but for a sphere it's a bit more complicated because each point is shared by eight triangles that differ by both angles phi and theta.
You could still take that approach, but this example uses a simpler solution based on the method used in the example Create a 3D surface really quickly with WPF, XAML, and C#. The idea is to make a Dictionary to keep track of the indexes of the points. When you need to use a point, you first see if it's in the Dictionary. If it is, you reuse its index in the mesh's Positions collection. If the point isn't in the Dictionary, you add it to the Positions collection and you save its index in the Dictionary.
This example's AddSmoothSphere method is similar to the version used by the previous example except it calls the following AddSmoothTriangle method to create triangles.
// Add a triangle to the indicated mesh.
// Reuse points so triangles share normals.
private void AddSmoothTriangle(MeshGeometry3D mesh,
Dictionary<Point3D, int> dict,
Point3D point1, Point3D point2, Point3D point3)
{
int index1, index2, index3;
// Find or create the points.
if (dict.ContainsKey(point1)) index1 = dict[point1];
else
{
index1 = mesh.Positions.Count;
mesh.Positions.Add(point1);
dict.Add(point1, index1);
}
if (dict.ContainsKey(point2)) index2 = dict[point2];
else
{
index2 = mesh.Positions.Count;
mesh.Positions.Add(point2);
dict.Add(point2, index2);
}
if (dict.ContainsKey(point3)) index3 = dict[point3];
else
{
index3 = mesh.Positions.Count;
mesh.Positions.Add(point3);
dict.Add(point3, index3);
}
// If two or more of the points are
// the same, it's not a triangle.
if ((index1 == index2) ||
(index2 == index3) ||
(index3 == index1)) return;
// Create the triangle.
mesh.TriangleIndices.Add(index1);
mesh.TriangleIndices.Add(index2);
mesh.TriangleIndices.Add(index3);
}
This method first checks the Dictionary to see if the first point is present. If it is, the code sets index1 equal to the point's index stored in the Dictionary. If the point isn't yet in the Dictionary, the code sets index1 equal to the next index in the mesh's Positions collection, adds the point to the collection, and saves the index in the Dictionary for later use.
The code repeats those steps for the triangle's second and third points.
Next the code checks whether any of the points are the same. That happens when the AddSmoothSphere method is building the sphere's top and bottom caps. For each theta value, each cap uses a single triangle. The other triangle that would be generated has two corners that are the same, so it really isn't necessary. This code checks for that condition and doesn't add the empty triangles.
Finally, the method adds entries the points' indexes in the mesh's TriangleIndices collection to define the triangle.
The rest of the program is almost the same as the previous version. The only other interesting changes are in the DrawSmoothSphere method. It uses the following statement to create a Dictionary to keep track of the sphere's points.
// Make a dictionary to track the sphere's points.
Dictionary<Point3D, int> dict = new Dictionary<Point3D, int>();
Later the method uses the following code to create triangles.
// Create the triangles.
AddSmoothTriangle(mesh, dict, pt00, pt11, pt10);
AddSmoothTriangle(mesh, dict, pt00, pt01, pt11);
Download the example to experiment with it and to see additional details.
|