PDA

View Full Version : Front faces, back faces and edge-on faces



sam_hawker
02-22-2017, 06:05 AM
In the absence of floating-point precision issues all faces can be classified as (a) front faces, (b) back faces and (c) faces which are viewed edge-on and therefore have zero area. Suppose I have a closed mesh and render all front faces in one pass and all back faces in a second pass. Any edge-on faces have zero area and should therefore not be rendered in either pass. In theory, for every pixel the number of front faces rendered should equal the number of back faces rendered. If I do this in OpenGL using GL_CULL_FACE then unfortunately this is not always the case for pixels close to triangle edges.

As far as I can tell, if a back face and a front face share an edge then any pixel which happens to land exactly on that edge should be considered either inside both triangles or outside both triangles. Presumably my problem therefore only occurs when I have a front face and a back face connected by one or more edge-on faces? In that case there is an edge on my back face which is theoretically co-linear with one on my front face when projected onto the viewing window. But I guess that because the projected extents of those edges are different they can become separated slightly due to a lack of precision?

Are there any practical ways to achieve the behaviour I am looking for? Failing that, is there a good way to quickly identify the affected pixels? I could count the number of front and back faces rendered in each pixel but for a sufficiently complex mesh I could (theoretically at least) have this kind of inconsistency occurring multiple times for a single pixel such that the count came out correct and I need to be able to identify these pixels too.

Dark Photon
02-22-2017, 10:22 AM
It sounds like you're having a problem, and have collected quite a bit of evidence about that problem, but I don't think you actually stated what you're trying to accomplish. Did I miss it?

Related to what you're seeing.

Presuming a watertight mesh and you ensure position invariance, OpenGL should produce a watertight rasterization. That is, pixels/samples in adjacent triangles sharing edge vertices should be drawn for only one of the triangles.

Now throw back/front facing in the mix. If you render front+back faces for such a watertight mesh, you should be able to ensure that all samples within the silhouette of the shape should be rasterized twice, once for front-facing and once for back-facing. Many stencil-counting algorithms presume this.

Could you talk in more detail about what you're trying to accomplish and what problems you're seeing?

sam_hawker
02-22-2017, 12:10 PM
Basically, I'm doing this:

http://gvirtualxray.sourceforge.net/pdf/Vidal2009TPCG.pdf

Figure 5 in that paper shows the kind of artefact I'm getting. Initially, I figured that their problem was caused by determining whether faces were front or back facing manually in a fragment shader so I used two passes and the built-in front/back culling instead. Unfortunately, whilst it may have reduced the number of artefacts it hasn't eliminated them completely.

The solution used by the authors of this paper is essentially to test whether the number of faces rendered is odd or even. If its odd then they substitute an average of neighbouring pixels. I believe it would be possible to construct a mesh where some pixels pass the odd/even test but nonetheless contain an incorrect result so I'm looking for something better.

GClements
02-23-2017, 07:04 AM
In the absence of floating-point precision issues all faces can be classified as (a) front faces, (b) back faces and (c) faces which are viewed edge-on and therefore have zero area. Suppose I have a closed mesh and render all front faces in one pass and all back faces in a second pass. Any edge-on faces have zero area and should therefore not be rendered in either pass. In theory, for every pixel the number of front faces rendered should equal the number of back faces rendered. If I do this in OpenGL using GL_CULL_FACE then unfortunately this is not always the case for pixels close to triangle edges.

Then either you're doing something wrong (e.g. enabling primitive anti-aliasing or MSAA etc) or your implementation is broken. The above invariant is required by the specification, and isn't dependent upon issues such as floating-point precision.



As far as I can tell, if a back face and a front face share an edge then any pixel which happens to land exactly on that edge should be considered either inside both triangles or outside both triangles.

Correct.



Presumably my problem therefore only occurs when I have a front face and a back face connected by one or more edge-on faces?

Even here, a ray should either intersect nothing or a front-and-back pair. Floating-point accuracy may affect which of these two occurs; but cannot introduce additional possibilities.



In that case there is an edge on my back face which is theoretically co-linear with one on my front face when projected onto the viewing window. But I guess that because the projected extents of those edges are different they can become separated slightly due to a lack of precision?

Precision doesn't matter. Projecting the same vertex repeatedly must yield the same result every time. Projecting a connected mesh results in a connected mesh. Rendering a closed surface and using stencilling to calculate the winding number (i.e. increment for front faces, decrementing for back faces) results in a stencil buffer that's either all zeroes (if the viewpoint is outside the mesh) or all negative-one (if it's inside, i.e. there's one more back face than front face at every pixel).

If you aren't getting this behaviour, anti-aliasing (either GL_POLYGON_SMOOTH or some form of multi-sampling) is enabled, or you have non-manifold geometry, or you're trying to replicate some aspect of the pipeline (e.g. back-face detection) yourself and failing to maintain a required invariant.

sam_hawker
02-23-2017, 07:24 AM
OK, that's really good to know, thanks.

I'm not using any form of antialiasing so I guess the only option left is that my mesh is not watertight after all. It was created by extracting an isosurface from a voxelized model (presumably using a marching squares algorithm) so it should be but maybe the software I used is buggy.

Edit: Meshlab confirms that my mesh is not watertight. Sorry for wasting everyone's time!