Graphics disappear under certain geometries.

In the course of writing a little FEM project, a rather interesting bug was encountered. So this program simulates the displacement of a two dimensional body, fixed at certain points, under load. These bodies can be any simple polygon. Many bodies work fine; for instance a square and an L under loads:
[ATTACH=CONFIG]663[/ATTACH]

Some bodies do not work fine. Upon changing the upper shape from a square to a backwards L, essentially mirroring in x the lower shape, it disappears!

[ATTACH=CONFIG]664[/ATTACH]

Nothing has changed about it’s position, rotation, or anything else I can tell. Just the specification of the outer hull.

Weird, eh?

Well, on to the code. Please feel free to point out where my code sucks, I’m rather new to OpenGL.

First, a brief context. FEM breaks an object down into “elements” in order to calculate some result throughout that object. These elements, in this example, are triangles made of six nodes. Three nodes at their corners, and three at their midpoints. These can be seen in the above pictures. The nodes are represented as positions in 2d space, so two floats. The elements are represented as six indices to these nodes, so six unsigned integers, allowing some nodes to be reused.

Now the code. Please feel free to tell me where my code sucks, as I’m rather new to OpenGL.

This function sets up the OpenGL objects that are used in rendering. The physics pointer links to the object that is responsible for the FEM calculations.


void HullDraw::init() {
    PhysicsableMessage msg;
    msg.physicsable = &physics;
    getRoot()->handle(msg);

    unsigned int pPvbo, pCvbo;
    glGenBuffers(1, &pPvbo);
    glGenBuffers(1, &pCvbo);
    glGenVertexArrays(1, &pVao);

    vector<float> points, colours;
    for(unsigned int i = 0; i < physics->getNumNodes(); ++i) {
        points.push_back(physics->getNodePos()[X][i]);
        points.push_back(physics->getNodePos()[Y][i]);

        colours.push_back(1.f);
        colours.push_back(.3f);
        colours.push_back(.3f);
    }

    glBindBuffer(GL_ARRAY_BUFFER, pPvbo);
    glBufferData(GL_ARRAY_BUFFER,
                 points.size() * sizeof(float),
                 points.data(),
                 GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, pCvbo);
    glBufferData(GL_ARRAY_BUFFER,
                 colours.size() * sizeof(float),
                 colours.data(),
                 GL_STATIC_DRAW);

    glBindVertexArray(pVao);

    glBindBuffer(GL_ARRAY_BUFFER, pPvbo);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);

    glBindBuffer(GL_ARRAY_BUFFER, pCvbo);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    //NOW DISPLACEMENTS
    glGenBuffers(1, &pPvbo);
    glGenBuffers(1, &pCvbo);
    glGenVertexArrays(1, &dVao);

    points.clear();
    colours.clear();
    for(unsigned int i = 0; i < physics->getNumNodes(); ++i) {
        points.push_back(physics->getNodeDis()[X][i]);
        points.push_back(physics->getNodeDis()[Y][i]);

        colours.push_back(.5f);
        colours.push_back(.5f);
        colours.push_back(1.f);
    }

    glBindBuffer(GL_ARRAY_BUFFER, pPvbo);
    glBufferData(GL_ARRAY_BUFFER,
                 points.size() * sizeof(float),
                 points.data(),
                 GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, pCvbo);
    glBufferData(GL_ARRAY_BUFFER,
                 colours.size() * sizeof(float),
                 colours.data(),
                 GL_STATIC_DRAW);

    glBindVertexArray(dVao);

    glBindBuffer(GL_ARRAY_BUFFER, pPvbo);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);

    glBindBuffer(GL_ARRAY_BUFFER, pCvbo);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);

    //And now indices....
    vector<unsigned int> indexV(physics->getNumElementNodes(), 0);
    nodes = physics->getNodesPerElement();
    elems = physics->getNumElements();
    unsigned int edges = nodes / 2;
    for(unsigned int i = 0; i < elems; ++i) {
        for(unsigned int n = 0; n < edges; ++n) {
            //Need n to go 0 3 1 4 2 5
            indexV[i * nodes + 2 * n + 0] = physics->getElemNod()[n + 0][i];
            indexV[i * nodes + 2 * n + 1] = physics->getElemNod()[n + 3][i];
        }
    }

    glGenBuffers(1, &indices);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                 indexV.size() * sizeof(unsigned int),
                 indexV.data(),
                 GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    debugged = false;
}

Each time an object is drawn, it’s position is queried from the physics object and glUniformMatrix4fv is used to set a model matrix in the used shader. This function is then called to draw all the original nodes, all the displaced nodes, and GL_LINE_LOOP of six displaced nodes each, using the element indices:


void HullDraw::run() {
    glBindVertexArray(pVao);
    glPointSize(2.0);
    glDrawArrays(GL_POINTS, 0, physics->getNumNodes());
    glBindVertexArray(0);

    glBindVertexArray(dVao);
    glPointSize(3.0);
    glDrawArrays(GL_POINTS, 0, physics->getNumNodes());
    glPointSize(1.0);
    glBindVertexArray(0);

    glBindVertexArray(dVao);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices);
    for(unsigned int i = 0; i < elems; ++i) {
        glDrawElements(GL_LINE_LOOP,
                       nodes,
                       GL_UNSIGNED_INT,
                       (void*)(i * nodes * sizeof(unsigned int)));
    }
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    if(!debugged) {
        //Debug output generated here!
        debugged = true;
    }
}

In attempting to figure out what’s happening, data was retreived using various calls to glGetVertexAttrib and glGetBufferSubData. Here’s the output for a working an non-working example:

HullDraw :: Debug ------------------------ Working Example
VertexArray pVao(3) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(0) = 8
VertexArray 0 bound
Buffer 8 bound to GL_ARRAY_BUFFER
    Contains [0, 10, 0, 0, 10, 0, 10, 5, 5, 5, 5, 10, 5, 0, 0, 5, 2.5, 0, 7.5, 2.5, 10, 2.5, 7.5, 5, 2.5, 7.5, 5, 7.5, 2.5, 10, 2.5, 2.5, 0, 2.5, 2.5, 5, 5, 2.5, 7.5, 0, 0, 7.5]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray pVao(3) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(1) = 9
VertexArray 0 bound
Buffer 9 bound to GL_ARRAY_BUFFER
    Contains [1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray pVao(3) bound
GL_VERTEX_ATTRIB_ARRAY_ENABLED(0) = 1
GL_VERTEX_ATTRIB_ARRAY_ENABLED(1) = 1
GL_VERTEX_ATTRIB_ARRAY_SIZE(0) = 2
GL_VERTEX_ATTRIB_ARRAY_SIZE(1) = 3
GL_VERTEX_ATTRIB_ARRAY_STRIDE(0) = 0
GL_VERTEX_ATTRIB_ARRAY_STRIDE(1) = 0
GL_FLOAT = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(0) = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(1) = 5126
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(0) = 0
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(1) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(0) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(1) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(0) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(1) = 0
GL_CURRENT_VERTEX_ATTRIB(0) = [0, 0, 0, 1]
GL_CURRENT_VERTEX_ATTRIB(1) = [1, 0, 0, 1]
VertexArray dVao(4) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(0) = 10
VertexArray 0 bound
Buffer 10 bound to GL_ARRAY_BUFFER
    Contains [0, 10, -0.529294, 0.148426, 9.20434, -1.37926, 10.1515, 3.74684, 4.9973, 4.72948, 5, 10, 4.35036, -0.371619, 0, 5, 1.93815, -0.100438, 7.19151, 1.72836, 9.68394, 1.21394, 7.61157, 4.22781, 2.51778, 7.45039, 5.00136, 7.39146, 2.5, 10, 2.23168, 2.40813, -0.269872, 2.60885, 2.47385, 4.92229, 4.70552, 2.16422, 6.74973, -0.793537, 0, 7.5]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray dVao(4) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(1) = 11
VertexArray 0 bound
Buffer 11 bound to GL_ARRAY_BUFFER
    Contains [0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray dVao(4) bound
GL_VERTEX_ATTRIB_ARRAY_ENABLED(0) = 1
GL_VERTEX_ATTRIB_ARRAY_ENABLED(1) = 1
GL_VERTEX_ATTRIB_ARRAY_SIZE(0) = 2
GL_VERTEX_ATTRIB_ARRAY_SIZE(1) = 3
GL_VERTEX_ATTRIB_ARRAY_STRIDE(0) = 0
GL_VERTEX_ATTRIB_ARRAY_STRIDE(1) = 0
GL_FLOAT = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(0) = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(1) = 5126
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(0) = 0
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(1) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(0) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(1) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(0) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(1) = 0
GL_CURRENT_VERTEX_ATTRIB(0) = [0, 0, 0, 1]
GL_CURRENT_VERTEX_ATTRIB(1) = [1, 0, 0, 1]
Indices: [3, 11, 4, 9, 2, 10, 5, 14, 0, 12, 4, 13, 1, 8, 6, 15, 7, 16, 6, 18, 4, 17, 7, 15, 2, 9, 4, 18, 6, 19, 4, 12, 0, 20, 7, 17]



HullDraw :: Debug ------------------------ Broken Example
VertexArray pVao(1) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(0) = 3
VertexArray 0 bound
Buffer 3 bound to GL_ARRAY_BUFFER
    Contains [0, 0, 10, 0, 10, 10, 5, 10, 5, 5, 0, 5, 10, 5, 5, 0, 10, 2.5, 7.5, 7.5, 10, 7.5, 7.5, 10, 2.5, 0, 2.5, 2.5, 0, 2.5, 5, 2.5, 7.5, 0, 7.5, 2.5, 2.5, 5, 5, 7.5, 7.5, 5]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray pVao(1) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(1) = 4
VertexArray 0 bound
Buffer 4 bound to GL_ARRAY_BUFFER
    Contains [1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray pVao(1) bound
GL_VERTEX_ATTRIB_ARRAY_ENABLED(0) = 1
GL_VERTEX_ATTRIB_ARRAY_ENABLED(1) = 1
GL_VERTEX_ATTRIB_ARRAY_SIZE(0) = 2
GL_VERTEX_ATTRIB_ARRAY_SIZE(1) = 3
GL_VERTEX_ATTRIB_ARRAY_STRIDE(0) = 0
GL_VERTEX_ATTRIB_ARRAY_STRIDE(1) = 0
GL_FLOAT = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(0) = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(1) = 5126
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(0) = 0
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(1) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(0) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(1) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(0) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(1) = 0
GL_CURRENT_VERTEX_ATTRIB(0) = [0, 0, 0, 1]
GL_CURRENT_VERTEX_ATTRIB(1) = [1, 0, 0, 1]
VertexArray dVao(2) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(0) = 5
VertexArray 0 bound
Buffer 5 bound to GL_ARRAY_BUFFER
    Contains [0, 0, 9.90576, -0.379254, 10.7407, 9.3301, 5.70343, 10.0117, 5.1489, 4.94528, 0, 5, 10.2081, 4.55689, 5, 0, 10.0553, 2.10638, 7.90766, 7.22786, 10.4216, 6.95352, 8.20434, 9.71403, 2.5, 0, 2.52135, 2.50055, 0, 2.5, 5.04784, 2.4658, 7.43277, -0.199046, 7.552, 2.2869, 2.56458, 4.99254, 5.4052, 7.47837, 7.68379, 4.75436]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray dVao(2) bound
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(1) = 6
VertexArray 0 bound
Buffer 6 bound to GL_ARRAY_BUFFER
    Contains [0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1]
Buffer 0 bound to GL_ARRAY_BUFFER
VertexArray dVao(2) bound
GL_VERTEX_ATTRIB_ARRAY_ENABLED(0) = 1
GL_VERTEX_ATTRIB_ARRAY_ENABLED(1) = 1
GL_VERTEX_ATTRIB_ARRAY_SIZE(0) = 2
GL_VERTEX_ATTRIB_ARRAY_SIZE(1) = 3
GL_VERTEX_ATTRIB_ARRAY_STRIDE(0) = 0
GL_VERTEX_ATTRIB_ARRAY_STRIDE(1) = 0
GL_FLOAT = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(0) = 5126
GL_VERTEX_ATTRIB_ARRAY_TYPE(1) = 5126
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(0) = 0
GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(1) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(0) = 0
GL_VERTEX_ATTRIB_ARRAY_INTEGER(1) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(0) = 0
GL_VERTEX_ATTRIB_ARRAY_DIVISOR(1) = 0
GL_CURRENT_VERTEX_ATTRIB(0) = [0, 0, 0, 1]
GL_CURRENT_VERTEX_ATTRIB(1) = [1, 0, 0, 1]
Indices: [2, 11, 3, 9, 6, 10, 5, 14, 0, 12, 7, 13, 1, 17, 4, 15, 7, 16, 4, 18, 5, 13, 7, 15, 3, 19, 4, 20, 6, 9, 4, 17, 1, 8, 6, 20]

The strange part is that all the data checks out, nothing seems to be wrong. The non-rendered object still has proper displacement calculcations. When we plot the points that occupy vertex array attribute 0 in dVao (the displaced positions) we get for the working example:
[ATTACH=CONFIG]665[/ATTACH]

…and for the broken example:
[ATTACH=CONFIG]666[/ATTACH]

All the right data seems to be in the right places, but for some geometries, glDrawArrays and glDrawElements seem to work, and for some other geometries, seem to not work.

…wat?

see https://www.opengl.org/sdk/docs/man3/xhtml/glCullFace.xml
The default is to cull a polygon whose back is to the viewer.
Add the disable, and those faces should reappear

Nice thought. Had the same one myself when this all started.

Doesn’t work though.

If you look at the rendering code, you see that it renders two VAOs as GL_POINTS, and then portions of the second VAO as GL_LINE_LOOPs. According to spec, glCullFace controls facets (triangles, quadrilaterals, polygons, and rectangles). Additionally, facet culling is initially disabled and, though you don’t have all of my code, I haven’t enabled it.

Secondly, one can see in the debug output, the very last line for each example lists “Indices”. These are the indices used, in consecutive groups of six, to render the line loops. If you find the points, in the vertex attribute 0 buffer, that these indices correspond to, you discover that the GL_LINE_LOOPs render counter-clockwise anyways.

So yeah, GL_POINTS and GL_LINE_LOOP seem unaffected by culling, plus culling is off, plus all the shapes are counter-clockwise already.

Thanks for the effort though.