0

I am trying to do a simple map rendered in OpenGL 2.1 and Qt5. But I'm failing on very basic issues. The one I'm presenting here is surface normals.

I have 4 object made of a single triangle geometry. A geoetry to make simple, is a dynamically allocated array of Vertex, where a Vertex is a couple of two QVector3D, a 3D position class predefined in Qt.

struct Vertex
{
    QVector3D position;
    QVector3D normal;
};

I'm computing the normal at a vertex by using the cross product of the two vectors from that vertex to the next or previous vertex. Normal computation for the structure seems fine, by debugging or printing results to the console.

QVector3D(-2, -2, -2) has normal QVector3D(0, 0, 1) 
QVector3D(2, -2, -2) has normal QVector3D(0, 0, 1) 
QVector3D(-2, 2, -2) has normal QVector3D(0, 0, 1) 
...

But when I feed the data to the shaders, the result are absurd! Here is a picture of the polygons colored with the normal value at each position:

unexpected rendering result

As in normal maps, red=x, green=y and blue=z. The top left corner of the black square is the origin of the world. As you can see the normal at some point seems to simply be the position at that point, without the z-value. Can you hint me what might be wrong, knowing the painting code is :

glUseProgram(program.programId());
glEnableClientState(GL_NORMAL_ARRAY);
program.setUniformValue("modelViewProjectionMatrix", viewCamera);
program.setUniformValue("entityBaseColor", QColor(0,120,233));
program.setUniformValue("sunColor", QColor("white"));
program.setUniformValue("sunBrightness", 1.0f);
static QVector3D tmpSunDir = QVector3D(0.2,-0.2,1.0).normalized();
program.setUniformValue("sunDir",tmpSunDir);

for( size_t i = 0; i < m_numberOfBoundaries; ++i)
{
    glBindBuffer(GL_ARRAY_BUFFER, m_bufferObjects[i]);

    int vertexLocation = program.attributeLocation("vertexPosition");
    program.setAttributeArray( vertexLocation, GL_FLOAT, &(m_boundaries[i].data->position), sizeof(Vertex) );
    program.enableAttributeArray(vertexLocation);
    glVertexAttribPointer( vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0 );

    int vertexNormal = program.attributeLocation("vertexNormal");
    program.setAttributeArray( vertexNormal, GL_FLOAT, &(m_boundaries[i].data->normal), sizeof(Vertex) );
    program.enableAttributeArray(vertexNormal);
    glVertexAttribPointer( vertexNormal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0 );

    glDrawArrays( GL_POLYGON, 0, m_boundaries[i].sizeOfData );
}

glDisableClientState(GL_NORMAL_ARRAY);

where a boundary is a geometrically connected component of the polygon. program is a QOpenGLShaderProgram, an Qt abstraction for shader programs. Each boundary is bound to a buffer object. The buffer object numbers are stored in the array m_bufferObjects. Polygon “boundaries” are stored as struct in the array m_boundaries. They have two fields : data, a pointer to the start of the array of vertices for the loop, and sizeOfData, the number of points for the polygon.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
Lærne
  • 3,010
  • 1
  • 22
  • 33
  • Upload the image to imgur.com or similar and I'll edit it into your question. – datenwolf Mar 27 '14 at 10:41
  • Hum, I clean up the code, and now I manage to get z-coordinates that somewhat seems correct, but no more x-y coordinates. Code looks like this : `int vertexNormal = program.attributeLocation("vertexNormal"); int normalOffset = (GLbyte const*)(&m_boundaries[i].data->normal) - (GLbyte const*)(m_boundaries[i].data) ; glVertexAttribPointer( vertexNormal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLbyte const*)(normalOffset) ); glEnableVertexAttribArray( vertexNormal );` – Lærne Mar 27 '14 at 13:24
  • Here is the original image : http://i57.tinypic.com/33e0rps.png – Lærne Mar 27 '14 at 13:27

1 Answers1

2

Until I get to the real problem of yours, here's something, probably unrelated but just as wrong:

glEnableClientState(GL_NORMAL_ARRAY);
/*...*/
glDisableClientState(GL_NORMAL_ARRAY);

You're using self defined vertex attributes, so it makes absolutely no sense to use those old fixed function pipeline client state locations. Use glEnableVertexAttribArray(location_index) instead,

Update

So I finally came around to take a closer look at your code and your problem is the mix of Qt's abstraction layer and use of raw OpenGL commands. Essentially your problem boils down to that you have a VBO bound when making calls to QOpenGLShaderProgram::setAttribArray followed by a call of glVertexAttribPointer.

One problem is, that setAttribArray internally makes the call of glVertexAttribPointer for you, so your own call to it is redundant and overwrites whatever Qt's stuff did. The more severe problem is, that you do have a VBO bound by glBindBuffer, so calls to glVertexAttribPointer actually take an byte offset into the VBO data instead of a pointer (in fact with a VBO bound passing a 0, which in pointer terms was a null pointer will yield a perfectly valid data offset). See this answer by me, why this is all a bit misleading and actually violates the C specification: https://stackoverflow.com/a/8284829/524368

Recent OpenGL versions actually have a new API for specifying attrib array offsets that conform to the C language specification.

The correct Qt method to use would be QOpenGLShaderProgramm::setAttribBuffer. Unfortunately your code shows not the exact definition of m_boundaries and your call to glBufferData or glBufferSubData; if I had that I could give you instructions on how to alter your code.

Community
  • 1
  • 1
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Thank you, that was a hopeless attempt to make the whole stuff work, I'll remove it. – Lærne Mar 27 '14 at 12:50
  • Thank you so much ! Acutally removing one of the redundant code was somehow breaking things up. I now use only OpenGL2.1 (I'm required to work on an old computer) function calls and it works : ` int vertexLocation = program.attributeLocation("vertexPosition"); glVertexAttribPointer( vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0 ); glEnableVertexAttribArray( vertexLocation );` I cannot see a method `QOpenGLShaderProgram::setAttribBuffer` in the documentation : http://qt-project.org/doc/qt-5/qopenglshaderprogram.html. I'd vote the updated answer if I could. – Lærne Mar 28 '14 at 07:07
  • By the way, the m_boundaries is simply this struct : `struct SizeAwareArrayPointer { size_t sizeOfData; Vertex* data; };` – Lærne Mar 28 '14 at 07:14