Correct face normal calculation

Hi,

I've noticed that this topic has been coming up a lot, without a conclusive solution, so: Is there a unique way to determine an outwards facing face normal? I've tried GProps, SLProps, using orientation info to invert the normals, you name it. However, some faces still get culled, some don't but their normals are funky, e.g. when you look straight at 'em the surface is dark, while some faces are displayed correctly etc.

So, has anyone come up with an immaculate solution to this? Otherwise I'm gonna be rummaging through OC's Viewer code all night long.

Thanks,
Janko Jerinic

CloneX's picture

I'm not sure if this is what you need but I use the following to get the normal to a face and it seems to work reliably:

gp_Dir FaceNormal(const TopoDS_Face &face)
{
// get bounds of face
Standard_Real umin, umax, vmin, vmax;
BRepTools::UVBounds(face, umin, umax, vmin, vmax); // create surface
Handle(Geom_Surface) surf=BRep_Tool::Surface(face); // get surface properties
GeomLProp_SLProps props(surf, umin, vmin, 1, 0.01); // get surface normal
gp_Dir norm=props.Normal(); // check orientation
if(face.Orientation()==TopAbs_REVERSED) norm.Reverse();
return norm;
}

Cheers,
Chris

jackietsaah's picture

Hi Chris,

Thanks for your answer. However, the method you've posted produces incorrect examples in my case. I'm gonna try and explain what I'm doing, so maybe you can see if I'm doing something else wrong, because basically I'm iterating through the faces of my shape (a compound consisting of one of more solids, in general) and the method you gave me returns results such as (-1,0,0), (0,1,0) etc. when the faces (which are planar, for certain) are nothing but parallel to XoY or XoZ planes. Maybe there are no surfaces assigned to the faces, is that possible? I've isolated one example, just for you to check, it's basically
an extrusion of a rectangle, let's call it a wall:

double xdim = profile->XDim/2, ydim = profile->YDim/2;
gp_Trsf trsf = profile->Position;

polygon.Add(gp_Pnt(-xdim, -ydim, 0).Transformed(trsf));
polygon.Add(gp_Pnt(xdim, -ydim, 0).Transformed(trsf));
polygon.Add(gp_Pnt(xdim, ydim, 0).Transformed(trsf));
polygon.Add(gp_Pnt(-xdim, ydim, 0).Transformed(trsf));
polygon.Close();

// Build a rectangular wire
TopoDS_Wire wire = polygon.Wire();

// Build a face from the wire
BRepBuilderAPI_MakeFace faceBuilder(wire);
TopoDS_Face face = faceBuilder.Face();

// Make a prism and position it
TopoDS_Shape prism = BRepPrimAPI_MakePrism(face , vec);
BRepBuilderAPI_Transform transformer(prism, profilePlacement);
prism = transformer.Shape();

Now as I've told you, the "wall" is nowhere near being aligned to X or Y axes, and yet I get the results I've mentioned. I'm just realizing that the face must have an assigned surface, because otherwise your method will probably crash, since BRep_Tool::Surface(face) would probably return NULL. So what may be the problem here? Do I have to transform the direction returned using some local coordinate system transformation?

Thanks,
Janko Jerinic

jackietsaah's picture

Hi,

It would definitely be nice for the guys from OpenCascade to step up and clear this once and for all. Rummaging through fifteen thousands of headers and code files is not really a good way to look for a solution, so far I have absolutely no idea as to how the meshes get rendered with correct normals, there is not a visible connection between meshes and, say, glDrawArrays/Elements calls so I still don't have a clue about how they get their correct normals. Are they using triangulation at all or something else? Every single quasi-solution encountered on this forum seems to be flawed, so that's an issue that definitiely needs a clarification from the authors.

Thanks,
Janko Jerinic

Davide Canalia's picture

have you found a solution to this problem?

I'm still searching it...

nishant.taluja_164312's picture

Hi,

Has anyone got solution for this problem? I am searching to get the correct face normals but so far nothing. I have found surface normals but they are not reliable as it doesn't always point outwards and face orientation flag forward or reversed doesnt give that information. 

Thanks,

Nishant

Trick X's picture

This might work. The face must be planar.

	Handle(Geom_Surface) gSurface = BRep_Tool::Surface(face);
	Handle(Geom_ElementarySurface) aElementarySurface = Handle(Geom_ElementarySurface)::DownCast(gSurface);
	gp_Dir normal = aElementarySurface->Axis().Direction();
	if (face.Orientation() == TopAbs_REVERSED)
		return -normal;
Guido van Hilst not specified's picture

Hi,

I think the Normal calculation in first example is OK.
I have checked it with your example: FaceNormalTest
(In the example I did some random transformations.)

The only thing is that it is giving the normal at Umin and Vmin.
so only for faces with constant normal over the U,V plane this is correct( planer faces)