Clipping plane by using stencil buffer for mesh objects with unclosed tubes

:doh:
I have a project, which requires to intersect anatomical mesh objects along specified axis (e.g. from head to foot) into a series of 2D gray-level slices. I used OpenGL stencil buffer with defined clipping plane to generate 2D slices.
For most of mesh objects, I can obtain good 2D intersected slices. But some mesh objects, like heart and trachea, with more tubes not-enclosed (airways, arteries, veins), this clipping algorithm did not work well. After stacking 2D slices to a volume, it looks to be extended along the end of tube to the bottom. Fig. 1 is the mesh object with texturing, Fig.2 shows each slice and Fig. 3 the stacked volume.

Please help find possible reasons for this issue, and any helpful suggestions are welcome.
[ATTACH=CONFIG]154[/ATTACH] [ATTACH=CONFIG]155[/ATTACH] [ATTACH=CONFIG]156[/ATTACH]

The following is my code snippets,

Code cpp:


    GLdouble eq[] = {A, B, C, D};
    glClipPlane(GL_CLIP_PLANE0, eq);
 
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_CLIP_PLANE0);
    glEnable(GL_STENCIL_TEST);
 
    glClear(GL_STENCIL_BUFFER_BIT);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
    // first pass: back face increment
    glStencilFunc(GL_ALWAYS, 0, 0);
    glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
    glCullFace(GL_FRONT);
    glEnable(GL_CULL_FACE);
 
    DrawCore(false, false, false, false); // draw mesh objects
 
    // second pass: front face increment
    glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
 
    DrawCore(false, false, false, false); // draw mesh objects
 
    // drawing clip planes masked by stencil buffer content
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_CLIP_PLANE0);
 
    glStencilFunc(GL_NOTEQUAL, 0, ~0);
 
    // rendering clip edges to frame buffer
    glPushMatrix();
    glLoadMatrixf(view_mat);    // Clip plane had been set up after view_mat space, thus we need to load
                        // view_mat matrix before rendering clip plane
    glBegin(GL_QUADS);
            CVector nrml = cross(CVector(verts[m_nEquationSelection][2] - verts[m_nEquationSelection][1]), CVector(verts[m_nEquationSelection][1] - verts[m_nEquationSelection][0]));
            nrml.normalize();
            glNormal3f(nrml(0), nrml(1), nrml(2)); //We want plane to be lit properly, thus we need to specify it's normal.
 
            glVertex3f(verts[m_nEquationSelection][0](0) * 100.0, verts[m_nEquationSelection][0](1) - eq[3], verts[m_nEquationSelection][0](2) * 100.0);
            glVertex3f(verts[m_nEquationSelection][1](0) * 100.0, verts[m_nEquationSelection][1](1) - eq[3], verts[m_nEquationSelection][1](2) * 100.0);
            glVertex3f(verts[m_nEquationSelection][2](0) * 100.0, verts[m_nEquationSelection][2](1) - eq[3], verts[m_nEquationSelection][2](2) * 100.0);
            glVertex3f(verts[m_nEquationSelection][3](0) * 100.0, verts[m_nEquationSelection][3](1) - eq[3], verts[m_nEquationSelection][3](2) * 100.0);
 
    glEnd();
 
    glPopMatrix();
 
    saveStencilBufferToExtraMemory(…, …);    // using "glReadPixels()" to read the stencil buffer and save to memory and then to a file
 
    glDisable(GL_DEPTH_TEST);
 
    glDisable(GL_STENCIL_TEST);
    glClear(GL_STENCIL_BUFFER_BIT);

i’d try to use a 3D texture to store the “slices”, or an 2D array texture, but that would require to program your own pipeline (without legacy GL functions).

for example, you disable depth testing / culling / stencil test etc, and attach a 3D texture to an framebuffe object, then use a simple vertex + fragment shader to render the meshes as usual, but in the fragment shader you use the fragments depth value to determine the final 3D texture texel in which to write the resulting color.

when you want to visualize an arbitrary “slice”, render a simple textured quad (use the generated 3D texture), depending on the orientation of your quad you can look into arbitrary positions on the mesh.

[QUOTE=michael_ca;1288239]
For most of mesh objects, I can obtain good 2D intersected slices. But some mesh objects, like heart and trachea, with more tubes not-enclosed (airways, arteries, veins), this clipping algorithm did not work well.[/QUOTE]
Is the presence of uncapped tubes the only feature of the problematic meshes? Are they 2-manifold surfaces otherwise? Is face winding consistent (i.e. are front/back faces identified correctly)? Does it make any difference if you use a single pass (both front and back faces) with GL_INVERT?

I can’t think of any explanation for the vertical “smear” in the third attachment. It shouldn’t be possible to modify the stencil value of any pixel which isn’t covered by at least one surface.

He isn’t rendering voxel data. He’s rendering meshes, using stencilling to detect regions which are made “open” by clipping.

Thank very much you guys.
GClements’ hints are enlightening me. I am now checking my meshes and capping code.

My mesh rendering looks good. I have intersected bones, lungs, kidneys, colon, and 2D capping looks very well. Only the capping for trachea, heart, stomach did not work well. Especially the result of intersecting for human heart mesh is so bad, because it has more uncapped tubes.

In these objects, definition for the position of some meshes is mirrored(scale value is negative). Before capping, I changed the face winding direction (from GL_CCW to GL_CW). Only these meshes with uncapped tubes can not work.

I also tried to use more passing processing. But till now, I did not get good results yet.

[QUOTE=GClements;1288244]Is the presence of uncapped tubes the only feature of the problematic meshes? Are they 2-manifold surfaces otherwise? Is face winding consistent (i.e. are front/back faces identified correctly)? Does it make any difference if you use a single pass (both front and back faces) with GL_INVERT?

I can’t think of any explanation for the vertical “smear” in the third attachment. It shouldn’t be possible to modify the stencil value of any pixel which isn’t covered by at least one surface.

He isn’t rendering voxel data. He’s rendering meshes, using stencilling to detect regions which are made “open” by clipping.[/QUOTE]

Hello everybody,

Finally I got very perfect volumes. GClements’ suggestion is very helpful.

I first tried my static model, and found the problem’s core is hole issue. I used meshlab to close hole and run my code, it worked.

Because my major interest is on dynamic data. I applied my needed vcglib (that meshlab uses) functions to my code, keeping the original vertices of mesh, no change was made.

Of course, the result is amazing.

:slight_smile: