Qt World Summit: Register Today!

Best way to create multi-textured terrain

  • Hi,

    I have a heightmap that I've generated and have implemented to render a terrain. So far, I can only apply one texture to it. My question is, what is the best way to assign varying textures and render them? I will want to blend textures as well but that can come later. I just need to learn how to render more than one. Searching online certainly has a lot of examples but many are either outdated or conflicting. Most render in immediate mode without shaders. GLSL is something I'm trying to comprehend as well.

    The following code has been adapted from the "Qt OpenGL tutorial.":http://qt-project.org/wiki/Developer-Guides#28810c65dd0f273a567b83a48839d275

    In glwidget.cpp::initializeGL()

    const float bgColor[4] = {.7, .8, 1.0, 1.0};
    glClearColor(bgColor[0], bgColor[1], bgColor[2], bgColor[3]);
    camera = world->initCamera();
    shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
    shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
    texture0 = bindTexture(QPixmap(":/textures/grassb512.bmp"));


    And the rendering occurs in glwidget.cpp::paintGL()
    vMatrix.rotate(180.0f, 0.0f, 0.0f, 1.0f);
    vMatrix.rotate(yRot, 0.0f, 1.0f, 0.0f);

    shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
    shaderProgram.setUniformValue("texture", 0);
    glBindTexture(GL_TEXTURE_2D, texture0);
    shaderProgram.setAttributeArray("vertex", world->vertices.constData());
    shaderProgram.setAttributeArray("textureCoordinate", world->textures.constData());
    QString log = shaderProgram.log();
    if (!log.isEmpty()) {
        qDebug("%s", qPrintable(log));
    glDrawArrays(GL_TRIANGLES, 0, world->vertices.size());


    As you may be able to tell, I have a class "world" which exposes an array of vertices as well as an array of textures to the rendering method (I also have an array of normals etc.)
    It would be nice to bind the textures in the world class but I don't see how they'd be exposed to the rendering method. The only solution that comes to mind is to have the world class generate vertex and texture arrays per texture and then process them in the paintGL method but, what happens when I start adding objects: trees structures people? What happens when I need to blend two textures across mutliple polygons?

    Is there logic I can perform in the shaders?

    A link to a perinent and up-to-date reference would be great or just some lucid step in the right direction. Thanks!

    I am developing in:
    Qt Creator 2.5.0
    Based on Qt 4.8.2 (64 bit)

    Built on Aug 7 2012 at 11:47:27
    running on Linux Debian Wheezy/experimental.

    The OpenGL specs are mesa which is a little bit behind the current openGL standards.

  • Bump on this . . . anyone?

  • eggsmatter,

    Hey that's a great question! The tutorials provide a solid basis to get you up and running and, of course, they aren't necessarily provided to explain all development details for your specific project.

    So, it looks like you are rendering all (or some) of your scene in you "world" class. I would suggest that in that class, you set flags for the textures you want to render. So foreach world->vertices & world->textures collection, You would want to implement an algorithm in your world class that can break this one-off array into numerous arrays per texture.

    It sounds like you already have the data on hand. It's just a matter of parting it out into seperate collections by texture to render.

    A great object to help you store this data would be the QMap STL Object. Initialize it (in your world class) thusly:

    QMap<QString, QVector<QVector3D> > vertexMap;
    //wherever you created your "vertices" array, instead do:
    . . .
    //sort by your texture flags:
    vertexMap["textureFlag1"] = vertexElement //which you would have just inserted into your vertices array

    Use the QMap iterator to map your vertex arrays in the paintGL method, binding textures and calling the glDrawArrays method accordingly as you have done.

    Make sure you share with the rest of the group your end results so other people with this problem can be spared from your frustrations.

    Good Luck!

  • Thanks eggsmatter!

    I should have thought of that. A dictionary hash is a great way to store the data I need. This will help me conceptualize the data I'm working with and then later I can work through and optimize it. I can even set custom texture flags to map to multiple texture files for blending and multiple textures etc. This fits like a glove. Thanks once again!

  • don't mention it. Glad I could help.

  • Curious what's going on?

    When I attempt the following, my code just hangs - no errors or warnings - and my entire system slows to a complete crawl:

    QString textureKey = worldMap[x][y]->planes[j]->texture;
    if (vertexMap.contains(textureKey)) {
    QMap<QString, QVector<QVector3D> >::const_iterator i = vertexMap.find(textureKey);
    while (i != vertexMap.end() && i.key() == textureKey) {
    //append a value in the vertexMap and then re-insert it:
    QVector<QVector3D> temp = QVector<QVector3D>(i.value());
    temp << worldMap[x][y]->planes[j]->getPlane()[0]
    << worldMap[x][y]->planes[j]->getPlane()[1]
    << worldMap[x][y]->planes[j]->getPlane()[2];
    vertexMap.insert(textureKey, temp);

  • Well, based on your prior programming experience, you should know that you must never insert an object into a collection while you're iterating through it. Look at what's happening. You're inserting a value into the map. The next iteration, you test if you've reached the end of the collection (which you haven't as you've just inserted something into it) and inserting another object into the collection. Your computer is bogging down because it is running out of memory.

  • Right, I was under the mistaken assumption that QMultiMap () would insert over the same key. The opposite is true. The documentation was staring me in the face.

Log in to reply