Menu

#440 OpenGL driver handle multiple-texture materials wrongly

current stable SDK
closed-fixed
None
5
2017-12-31
2017-12-13
number Zero
No

When a material has several textures, but drawtype does not support that (e.g. EMT_TRANSPARENT_ALPHA_CHANNEL_REF, which is documented to use the first texture only, regardless of overall count), calling setMaterial twice with the same material makes it use several textures for drawing, which produces obviously wrong result.

screenshot
Top left: drawn after setMaterial() with two-texture (orange 0th, blue 1st)
Top right: drawn after second setMaterial()
Bottom left: drawn after setMaterial() with single-texture material.
Bottom right: drawn after second setMaterial() with the same single-texture material.

Example to reproduce:

#include <irrlicht.h>
#include <SMeshBuffer.h>

using namespace irr;
using namespace core;
using namespace video;

static S3DVertex vertices1[] = {
    S3DVertex(-1.0, 0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 0.0),
    S3DVertex( 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 0.0),
    S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 1.0),
    S3DVertex( 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 1.0),
};

static S3DVertex vertices2[] = {
    S3DVertex(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 0.0),
    S3DVertex(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 0.0),
    S3DVertex(0.0, 1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 1.0),
    S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 1.0),
};

static S3DVertex vertices3[] = {
    S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 0.0),
    S3DVertex( 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 0.0),
    S3DVertex(-1.0,  0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 1.0),
    S3DVertex( 0.0,  0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 1.0),
};

static S3DVertex vertices4[] = {
    S3DVertex(0.0, -1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 0.0),
    S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 0.0),
    S3DVertex(0.0,  0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 0.0, 1.0),
    S3DVertex(1.0,  0.0, 0.0, 0.0, 0.0, 1.0, SColor(255, 255, 255, 255), 1.0, 1.0),
};

static u16 indices[] = {
    0, 2, 1,
    1, 2, 3,
};

int main()
{
    IrrlichtDevice *device =
        createDevice(EDT_OPENGL, dimension2d<u32>(800, 600), 32,
            false, true, true, 0);
    if (!device)
        return 1;
    device->setWindowCaption(L"Bug Demo");
    IVideoDriver *driver = device->getVideoDriver();
    SMaterial mat2;
    SMaterial mat;
    mat.TextureLayer[0].Texture = driver->getTexture("lava.png");
    mat.TextureLayer[1].Texture = driver->getTexture("water.png");
    //mat.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
    mat.setFlag(video::EMF_BILINEAR_FILTER, false);
    mat.setFlag(video::EMF_TRILINEAR_FILTER, false);
    mat.setFlag(video::EMF_BACK_FACE_CULLING, true);
    mat.setFlag(video::EMF_LIGHTING, false);
    mat2 = mat;
    mat2.TextureLayer[0].Texture = mat2.TextureLayer[1].Texture; // must be different from mat#0
    mat2.TextureLayer[1].Texture = nullptr; // keep first texture only
    scene::SMeshBuffer buf1, buf2, buf3, buf4;
    buf1.append(vertices1, 4, indices, 6);
    buf2.append(vertices2, 4, indices, 6);
    buf3.append(vertices3, 4, indices, 6);
    buf4.append(vertices4, 4, indices, 6);
    while (device->run()) {
        driver->beginScene(true, true, SColor(255, 0, 0, 128));
        driver->setMaterial(mat);
        driver->drawMeshBuffer(&buf1); // drawn normally
        driver->setMaterial(mat); // messes things up
        driver->drawMeshBuffer(&buf2); // drawn with both textures
        driver->setMaterial(mat2); // necessary to restore driver to sane state
        driver->drawMeshBuffer(&buf3); // ok
        driver->setMaterial(mat2); // no-op
        driver->drawMeshBuffer(&buf4); // ok
        driver->endScene();
    }
    device->drop();
    return 0;
}

Actual textures do not matter.

Discussion

  • Michael Zeilfelder

    Thanks for report and example. I've already noticed some problem with opengl using second texture (d3d version doesn't), but had just put it on todo and not yet made a good example. I'll try to look into it after x-mas.

     
  • Michael Zeilfelder

    Findings so far:
    The bug got introduced in OpenGL in Irrlicht 1.7 when setActiveTexture was moved into setMaterial.
    The second texture is activated in setMaterial. Then the material renderer does reset it again to 0. Then it is activated again in next setMaterial call. But now the bug is caused because the material renderers are only called when the current and last material are not identical. They are identical in this case so the material renderer no longer resets the texture.
    This does not happen in D3D because the material renders there keep the second texture active and only disable the color-operations for it (which happens without any caching).

    Possible ways to solve:
    Driver could change LastMaterial in disableTextures. LastMaterial seems like a bad name as it is rather a cache-material. And it should reflect the current state of OpenGL.
    Another solution would be to get rid of LastMaterial completely. And introduce a complete cache-management for opengl instead (which is not based on Irrlicht materials at all but on opengl states).
    Another option might be to check if OpenGL can be made to work more like D3D and enabling textures can be split from the color-operations. Thought in that case we might want to cache color operations and would be back at the same problem.

    Have to think about it some more - any feedback welcome.

    edit:
    I suspect the 2 lines with "LastMaterial =" in OpenGL should rather receive their content from COpenGLCoreCacheHandler. Or maybe we can let COpenGLCoreCacheHandler validate them (like in this case going over the texture pointers). Not sure if best solution (I don't think so), but would allow to fix this quickly without adding many changes (which is good).

     

    Last edit: Michael Zeilfelder 2017-12-27
  • Michael Zeilfelder

    • status: open --> closed-fixed
    • assigned_to: Michael Zeilfelder
     
  • Michael Zeilfelder

    Fixed irrlicht trunk r5595 and will be in Irrlicht 1.9

     

Log in to post a comment.