lighting artefacts

Hi all,

First post for me. Can’t believe i hadn’t found this resource before today.

My problem is regarding the display of simple objects.

If you look at the image below, the areas that i extruded have some strange lighting artefacts around them. I spent absolutely ages trying to figure out where the problem is coming from…(normals calculation or modelling program), but with no success. I constructed the model in max, if anyone has models of a similar simplicity could they possibly send them across so i can test if the errors are coming from my modelling?

many thanks

ribot

your problem comes from the object’s smoothing. set it just below 90 degrees, and they will disappear. how do you load the object? What normal calculator do you use?
By the way, it seems that there is a default in your object. there is a sudden smoothing in the vertical last part at right.

so what you’re saying is that if i adjust the angle of the object by 2-5 degrees the artifacts should disappear?

Why is this so?

The normal calculations that i use:



//////////////////////////////      Math Functions  ////////////////////////////////*

      // This computes the magnitude of a normal.   (magnitude = sqrt(x^2 + y^2 + z^2)
      double Mag(CVector3 Normal)
      {
            return (Math.sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z));
      }

      // This calculates a vector between 2 points and returns the result
      CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2)
      {
            CVector3 vVector = new CVector3();                  // The variable to hold the resultant vector

            vVector.x = vPoint1.x - vPoint2.x;                  // Subtract point1 and point2 x's
            vVector.y = vPoint1.y - vPoint2.y;                  // Subtract point1 and point2 y's
            vVector.z = vPoint1.z - vPoint2.z;                  // Subtract point1 and point2 z's

            return vVector;                                                // Return the resultant vector
      }

      // This adds 2 vectors together and returns the result
      CVector3 AddVector(CVector3 vVector1, CVector3 vVector2)
      {
            CVector3 vResult = new CVector3();                  // The variable to hold the resultant vector
            
            vResult.x = vVector2.x + vVector1.x;            // Add Vector1 and Vector2 x's
            vResult.y = vVector2.y + vVector1.y;            // Add Vector1 and Vector2 y's
            vResult.z = vVector2.z + vVector1.z;            // Add Vector1 and Vector2 z's

            return vResult;                                                // Return the resultant vector
      }

      // This divides a vector by a single number (scalar) and returns the result
      CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler)
      {
            CVector3 vResult = new CVector3();                  // The variable to hold the resultant vector
            
            vResult.x = vVector1.x / Scaler;                  // Divide Vector1's x value by the scaler
            vResult.y = vVector1.y / Scaler;                  // Divide Vector1's y value by the scaler
            vResult.z = vVector1.z / Scaler;                  // Divide Vector1's z value by the scaler

            return vResult;                                                // Return the resultant vector
      }

      // This returns the cross product between 2 vectors
      CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
      {
            CVector3 vCross = new CVector3();                  // The vector to hold the cross product
                                                                              // Get the X value
            vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
                                                                              // Get the Y value
            vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
                                                                              // Get the Z value
            vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));

            return vCross;                                                // Return the cross product
      }

      // This returns the normal of a vector
      CVector3 Normalize(CVector3 vNormal)
      {
            double Magnitude;                                          // This holds the magnitude                  

            Magnitude = Mag(vNormal);                              // Get the magnitude

            vNormal.x /= (float)Magnitude;                        // Divide the vector's X by the magnitude
            vNormal.y /= (float)Magnitude;                        // Divide the vector's Y by the magnitude
            vNormal.z /= (float)Magnitude;                        // Divide the vector's Z by the magnitude

            return vNormal;                                                // Return the normal
      }

      ///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
      /////
      /////      This function computes the normals and vertex normals of the objects
      /////
      ///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

      void ComputeNormals(t3DModel pModel)
      {
            CVector3 vVector1 = new CVector3();
            CVector3 vVector2 = new CVector3();
            CVector3 vNormal = new CVector3();
            CVector3[] vPoly = new CVector3[3];

            // If there are no objects, we can skip this part
            if(pModel.numOfObjects <= 0)
                  return;

            // What are vertex normals?  And how are they different from other normals?
            // Well, if you find the normal to a triangle, you are finding a "Face Normal".
            // If you give OpenGL a face normal for lighting, it will make your object look
            // really flat and not very round.  If we find the normal for each vertex, it makes
            // the smooth lighting look.  This also covers up blocky looking objects and they appear
            // to have more polygons than they do.    Basically, what you do is first
            // calculate the face normals, then you take the average of all the normals around each
            // vertex.  It's just averaging.  That way you get a better approximation for that vertex.

            // Go through each of the objects to calculate their normals
            for(int index = 0; index < pModel.numOfObjects; index++)
            {
                  // Get the current object
                  t3DObject pObject = (t3DObject)pModel.pObject.get(index);

                  // Here we allocate all the memory we need to calculate the normals
                  CVector3[] pNormals            = new CVector3 [pObject.numOfFaces];
                  CVector3[] pTempNormals      = new CVector3 [pObject.numOfFaces];
                  pObject.pNormals            = new CVector3 [pObject.numOfVerts];

                  // Go though all of the faces of this object
                  for(int i=0; i < pObject.numOfFaces; i++)
                  {                                    
                        // To cut down LARGE code, we extract the 3 points of this face
                        vPoly[0] = pObject.pVerts[pObject.pFaces[i].vertIndex[0]];
                        vPoly[1] = pObject.pVerts[pObject.pFaces[i].vertIndex[1]];
                        vPoly[2] = pObject.pVerts[pObject.pFaces[i].vertIndex[2]];

                        // Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)

                        vVector1 = Vector(vPoly[0], vPoly[2]);            // Get the vector of the polygon (we just need 2 sides for the normal)
                        vVector2 = Vector(vPoly[2], vPoly[1]);            // Get a second vector of the polygon

                        vNormal  = Cross(vVector1, vVector2);            // Return the cross product of the 2 vectors (normalize vector, but not a unit vector)
                        pTempNormals[i] = vNormal;                              // Save the un-normalized normal for the vertex normals
                        vNormal  = Normalize(vNormal);                        // Normalize the cross product to give us the polygons normal

                        pNormals[i] = vNormal;                                    // Assign the normal to the list of normals
                  }

                  //////////////// Now Get The Vertex Normals /////////////////

                  CVector3 vSum = new CVector3();
                  CVector3 vZero = vSum;
                  int shared=0;

                  for (int i = 0; i < pObject.numOfVerts; i++)            // Go through all of the vertices
                  {
                        for (int j = 0; j < pObject.numOfFaces; j++)      // Go through all of the triangles
                        {                                                                        // Check if the vertex is shared by another face
                              if (pObject.pFaces[j].vertIndex[0] == i || 
                                    pObject.pFaces[j].vertIndex[1] == i || 
                                    pObject.pFaces[j].vertIndex[2] == i)
                              {
                                    vSum = AddVector(vSum, pTempNormals[j]);// Add the un-normalized normal of the shared face
                                    shared++;                                                // Increase the number of shared triangles
                              }
                        }      
                        
                        // Get the normal by dividing the sum by the shared.  We negate the shared so it has the normals pointing out.
                        pObject.pNormals[i] = DivideVectorByScaler(vSum, (float)(-shared));

                        // Normalize the normal for the final vertex normal
                        pObject.pNormals[i] = Normalize(pObject.pNormals[i]);      

                        vSum = vZero;                                                      // Reset the sum
                        shared = 0;                                                            // Reset the shared
                  }
            }
      }


Can you see anything wrong with my normal computation?

That sudden smoothing on the far right of the object is another indent (the object is supposed to represent a network card). :wink:

Cheers,

ribot.

Okay. Before changing your normals, verify if the two faces you are testing have an angle of more than 89.99 degrees or not. if they have more, don’t merge the vectors. In that case, each polygon 's point at this place must have its vector, so don’t merge the points (if ever you do). That vector should be perpendicular to the polygon.
Is there any reason you don’t use java3d’s normal calculation? It has a parameter for angle smoothing and takes care of everything while optimising your object. Very nice utils.
For example, here is how i use it for a landscape program of mine.


GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
gi.setCoordinates(pts); // sending points into the array
gi.setTextureCoordinateParams(1,2);
gi.setTexCoordSetMap(new int[]{0, 0, 0});
gi.setTextureCoordinates(0,texCoords);// sending mapping coordinates
gi.setColors(colors);// sending colors
NormalGenerator ng = new NormalGenerator();// creating a new normal generator
// here, use ng.setCreaseAngle( Math.toRadians(89.9) );  not to have problems with your normals
ng.generateNormals(gi);// generating normals for the object
gi.recomputeIndices();// some cleaning
Stripifier st = new Stripifier();//optimising the object for speed
st.stripify(gi);
gi.recomputeIndices();// cleaning
Shape3D scape=new Shape3D();
scape.setGeometry(gi.getGeometryArray());