Draw smooth spheres using WPF and C#

[draw smooth spheres]

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];
        index1 = mesh.Positions.Count;
        dict.Add(point1, index1);

    if (dict.ContainsKey(point2)) index2 = dict[point2];
        index2 = mesh.Positions.Count;
        dict.Add(point2, index2);

    if (dict.ContainsKey(point3)) index3 = dict[point3];
        index3 = mesh.Positions.Count;
        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.

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 and look at the code to see the rest of the details.

Download Example   Follow me on Twitter   RSS feed   Donate

About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawing, geometry, graphics, mathematics, wpf, XAML and tagged , , , , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to Draw smooth spheres using WPF and C#

  1. Pingback: Make a 3D globe in WPF and C# - C# HelperC# Helper

  2. JordanA says:

    Hey Mr. Setephens! Thank you so much for this useful article, it’s been a great help with what I’m doing.

    Can you shed some insight as to why I’m getting bizarre results when applying a roll, pitch, and yaw rotation to spheres created with this method?

    This is the article I’m using to do the rotations: http://danceswithcode.net/engineeringnotes/rotations_in_3d/rotations_in_3d_part2.html

    • RodStephens says:

      What results are you getting?

      Are you building the matrices according to that article and then applying them yourself to the points? Or are you trying to make the WPF drawing system apply the matrix?

      • JordanA says:

        Here’s an example of a single sphere after rotation is applied: https://imgur.com/a/6O4vQSw

        I am setting up the matrix like in the article and applying the three rotations to each point before they get added as triangles. I’ve even copy pasted the function I’m using into this example: https://pastebin.com/RNDXsGBV

        The function is called “Rotate”

      • JordanA says:

        The spheres are getting torn when the rotation is applied to them.

        Here’s an image example: https://imgur.com/a/6O4vQSw

        I’ve added the rotation code to this example and copied it over to pastebin: https://pastebin.com/RNDXsGBV

          • RodStephens says:

            There were two problems, both involving the same lines of code. Here’s the original version.

            pt00 = Rotate(pt00);
            pt00 = Rotate(pt01);
            pt00 = Rotate(pt10);
            pt00 = Rotate(pt11);
            // Create the triangles.
            AddTriangle(mesh, pt00, pt11, pt10);
            AddTriangle(mesh, pt00, pt01, pt11);

            The first four lines set point pt00 repeatedly when they should be setting p00, p01, p10, and p11.

            The second problem is that the loop only calculates points pt01 and pt11, At the end of the loop it saves those points as pt00 and pt10 for the next trip through the loop. The Rotate code was modifying pt00 and pt10, and then modifying them again in the next trip through the loop so they were not where they needed to be the next time around.

            One way to fix the problem is to save the rotated points in new variables that are then used to define the triangles leaving p00, p01, p10, and p11 unrotated so they will work the next time through the loop. The following code uses that approach.

            //Apply rotation to the points
            Point3D new_pt00 = Rotate(pt00);
            Point3D new_pt01 = Rotate(pt01);
            Point3D new_pt10 = Rotate(pt10);
            Point3D new_pt11 = Rotate(pt11);
            // Create the triangles.
            AddTriangle(mesh, new_pt00, new_pt11, new_pt10);
            AddTriangle(mesh, new_pt00, new_pt01, new_pt11);

            I hope that helps. Your program seems to only rotate the spheres and not the cylinders, so you might still need to do that. I don’t *think* the AddCylinder method uses saved points in its loops so it shouldn’t have the same problem.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.