Translation of texture doesn't work in gl_TextureMatrix

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

Hi,

i think i've found a bug with CMatrix4::setTextureTranslate. :o

I scale and translate textures and therefore i want to access the texture matrix in my GLSL vertex shader.

So i tried this:

Code: Select all

gl_TexCoord[0] = gl_TextureMatrix[0]*gl_MultiTexCoord0;
Doesn't do anything. Neither translation nor scale is accessible in TextureMatrix[0].

Next thing i tried was to send the texture matrix as a uniform variable to the shader:

Code: Select all

 
this->textureMatrixID = services->getVertexShaderConstantID("tMatrix");
...
services->setVertexShaderConstant(this->textureMatrixID, reinterpret_cast<float*>(material.getTextureMatrix(0).pointer()), 16);
...
uniform mat4 tMatrix;
...
gl_TexCoord[0] = tMatrix * gl_MultiTexCoord0;
 
This only works for scale, translation doesn't work?!

Using this ugly workaround finally did it:

Code: Select all

 
irr::core::matrix4 m;
irr::f32 tx, ty, sx, sy;
 
material.getTextureMatrix(0).getTextureTranslate(tx, ty);
material.getTextureMatrix(0).getTextureScale(sx, sy);
 
m.setTranslation(irr::core::vector3df(tx, ty, 0));
m.setScale(irr::core::vector3df(sx, sy, 1));
 
services->setVertexShaderConstant(this->textureMatrixID, reinterpret_cast<float*>(m.pointer()), 16);
 
So it seems there are two bugs:

1. Irrlichts texture matrix is not accessible in gl_TextureMatrix, it doesn't "inject" its values.
2. Irrlichts texture matrix stores its translation values in a place that is not accessible in gl_TextureMatrix when sent as a uniform variable, you have to copy the translation to an new matrix using translation rather than texture translation.


PS: It seems to have worked some time ago.
PPS: I am shader noob, did i miss something?
"Whoops..."
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

We send it with glLoadMatrixf... which looks correct to me (don't really know other options). Strange stuff - I would have to write some example to reproduce that first - so can't say anything else right now.

edit: About your second solution. Irrlicht uses d3d texture matrices - so you can't send them directly like that. OpenGL driver has internally a function getGLTextureMatrix to transform matrices to OGL style (I also didn't know about that...).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

I made a minimal compilable example which reproduces the problem.

There is a simple cube with a scaled texture.

The shader does nothing special, just display the texture 1:1. Unfortunately, the texture matrix gets ignored.

Code: Select all

 
#include <irrlicht.h>
 
const irr::core::stringc vertexShader =
        "\
        void main(void)\
        {\
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
            gl_TexCoord[0] = gl_TextureMatrix[0]*gl_MultiTexCoord0;\
        }";
 
const irr::core::stringc fragmentShader =
        "uniform sampler2D Texture;\
        \
        void main(void)\
        {\
            gl_FragColor = texture2D(Texture, gl_TexCoord[0].xy);\
        }";
 
class MyShaderCallBack: public irr::video::IShaderConstantSetCallBack
{
 
public:
 
    MyShaderCallBack() :
            TextureID(-1), FirstUpdate(true)
    {
    }
 
    virtual void OnSetConstants(irr::video::IMaterialRendererServices* services,
            irr::s32 userData)
    {
        //get shader constant id.
        if (this->FirstUpdate)
        {
            this->TextureID = services->getPixelShaderConstantID("Texture");
            this->FirstUpdate = false;
        }
 
        //set texture
        const irr::s32 TextureLayerID = 0;
        services->setPixelShaderConstant(this->TextureID, &TextureLayerID, 1);
    }
 
private:
 
    irr::s32 TextureID;
 
    bool FirstUpdate;
};
 
int main()
{
    irr::IrrlichtDevice* const device = irr::createDevice(
            irr::video::EDT_OPENGL, irr::core::dimension2du(800, 600));
 
    irr::video::IVideoDriver* const driver = device->getVideoDriver();
    irr::scene::ISceneManager* const smgr = device->getSceneManager();
 
    irr::scene::ICameraSceneNode* const camera = smgr->addCameraSceneNode(0,
            irr::core::vector3df(-0.5f, 1.5, -1.5f));
    camera->setTarget(irr::core::vector3df(0.5f, 0.5f, 0.5f));
 
    //shader
    irr::video::IGPUProgrammingServices* const gpu =
            driver->getGPUProgrammingServices();
 
    MyShaderCallBack* const mc = new MyShaderCallBack;
 
    irr::s32 shaderMaterialType = gpu->addHighLevelShaderMaterial(
            vertexShader.c_str(), "main", irr::video::EVST_VS_1_1,
            fragmentShader.c_str(), "main", irr::video::EPST_PS_1_1, mc);
 
    mc->drop();
 
    //mesh
    irr::scene::IMeshSceneNode* const cube = smgr->addCubeSceneNode(1.0f);
    cube->setMaterialTexture(0,
            driver->getTexture(
                    "./irrlicht-svn/media/wall.jpg"));
    cube->setMaterialFlag(irr::video::EMF_LIGHTING, false);
 
    //Scale the texture, should be accessible with gl_TextureMatrix[0]!
    cube->getMaterial(0).getTextureMatrix(0).setTextureScale(2.3, 4.2);
 
    //comment this to render the cube without shader (and scaled texture)
    cube->setMaterialType((irr::video::E_MATERIAL_TYPE) shaderMaterialType);
 
    while (device->run())
    {
        driver->beginScene();
        smgr->drawAll();
        driver->endScene();
    }
 
    device->drop();
 
    return 0;
}
 
"Whoops..."
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by hendu »

Image

Works on my 1.7-based fork, so must be broken after that.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

Huh, just when I wanted to give up. Yeah - it also works in 1.8 it seems. Hm, guess then I can give it another check :-)

Btw.. for the workaround with passing it yourself you might give that a try:

Code: Select all

 
core::matrix4 Matrix = driver->getTransform(ETS_TEXTURE_0);
 
(just seen that one used in the ogles2 code). Meaning - don't convert the matrix yourself, but just use current matrix.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

OK - seems this is deliberately disabled for shaders (and enabled for fixed function pipeline materials). Meaning - you _have_ to pass the info yourself. While I don't know the reason - my guesses are:
- I found several websites mentioning that using gl_MultiTexCoord0 is deprecated.
- Passing it always (even when it's mostly not necessary) probably adds some serious cost.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

CuteAlien wrote:

Code: Select all

core::matrix4 Matrix = driver->getTransform(ETS_TEXTURE_0);
Only works for scale. Translation is ignored too.
CuteAlien wrote:I found several websites mentioning that using gl_MultiTexCoord0 is deprecated.
gl_MultiTexCoord0 and gl_TextureMatrix are deprecated, yes. Full list of deprecated built-in variables in GLSL Shaders. I try to find the replacements. Thanks!
"Whoops..."
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

randomMesh wrote: Only works for scale. Translation is ignored too.
Hm, strange - I'll have to check that (not today, still have to do something else, maybe weekend).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by Nadro »

CuteAlien wrote:OK - seems this is deliberately disabled for shaders (and enabled for fixed function pipeline materials). Meaning - you _have_ to pass the info yourself. While I don't know the reason - my guesses are:
- I found several websites mentioning that using gl_MultiTexCoord0 is deprecated.
- Passing it always (even when it's mostly not necessary) probably adds some serious cost.
Yes, we disabled stuff like gl_TextureMatrix[N] (+other functions deprecated in OpenGL 3.0) for shader based materials due to a performance reasons.

@randomMesh
As CuteAlien said, you can use IVideoDriver::getTransform and pass this matrix manually.
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

Nadro wrote:@randomMesh
As CuteAlien said, you can use IVideoDriver::getTransform and pass this matrix manually.
I tried this but it only works for scaling. Translation is ignored.
"Whoops..."
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

@randomMesh: Sorry, haven't check it out yet. My suspicion (without coding yet) would be that you try to set translation and scaling in the same matrix and one overwrites the other. But maybe I' on the wrong track as it would also fail without shaders in that case. I hope I'll get to it later this week.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

CuteAlien wrote:@randomMesh: My suspicion (without coding yet) would be that you try to set translation and scaling in the same matrix and one overwrites the other.
No, i set scale for one buffer and translate for another buffer. These values are altered in my update method in case it makes a difference.

Code: Select all

 
...
this->buffer[0].Material.TextureLayer[0].getTextureMatrix().setTextureTranslate(x, y);
...
this->buffer[1].Material.TextureLayer[0].getTextureMatrix().setTextureScale(scale, scale);
 
driver->beginScene();
 
"Whoops..."
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by randomMesh »

Sorry for bumping, but i just made an example which uses the suggested method to send

Code: Select all

core::matrix4 Matrix = driver->getTransform(ETS_TEXTURE_0);
as a uniform variable.

As you can see in the screenshot, only scaling works this way. (Bottom left cube is the shader one, bottom right is textured with the transformations applied, top cube is the plain texture without any transformation). 1 and 2 should look the same, but they don't!

http://imgur.com/a/yPE4A

Example code:

Code: Select all

 
#include <irrlicht.h>
 
const irr::core::stringc vertexShader =
        "uniform mat4 matrix;\
        void main(void)\
        {\
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
            gl_TexCoord[0] = matrix*gl_MultiTexCoord0;\
        }";
 
const irr::core::stringc fragmentShader =
        "uniform sampler2D Texture;\
        \
        void main(void)\
        {\
            gl_FragColor = texture2D(Texture, gl_TexCoord[0].xy);\
        }";
 
class MyShaderCallBack: public irr::video::IShaderConstantSetCallBack
{
 
public:
 
    MyShaderCallBack() :
            TextureID(-1), textureMatrixID(-1), FirstUpdate(true)
    {
    }
 
    virtual void OnSetConstants(irr::video::IMaterialRendererServices* services,
            irr::s32 userData)
    {
        //get shader constant id.
        if (this->FirstUpdate)
        {
            this->TextureID = services->getPixelShaderConstantID("Texture");
            this->textureMatrixID = services->getPixelShaderConstantID(
                    "matrix");
            this->FirstUpdate = false;
        }
 
        //set texture
        const irr::s32 TextureLayerID = 0;
        services->setPixelShaderConstant(this->TextureID, &TextureLayerID, 1);
 
        //set matrix;
        const irr::core::matrix4& matrix =
                services->getVideoDriver()->getTransform(
                        irr::video::ETS_TEXTURE_0);
        services->setVertexShaderConstant(this->textureMatrixID,
                matrix.pointer(), 16);
    }
 
private:
 
    irr::s32 TextureID;
 
    irr::s32 textureMatrixID;
 
    bool FirstUpdate;
};
 
/**
 * Determines if a point is inside a circle.
 *
 * \param x The x coordinate of the point
 * \param y The y coordinate of the point
 * \param cx The x coordinate of the center of the circle
 * \param cy The y coordinate of the center of the circle
 * \param radius The radius of the circle
 * \return True if point is inside circle
 */
bool isInCircle(const irr::u32 x, const irr::u32 y, const irr::u32 cx,
        const irr::u32 cy, const irr::u32 radius)
{
    const irr::u32 dx = x - cx;
    const irr::u32 dy = y - cy;
    const irr::u32 distance = dx * dx + dy * dy;
 
    return distance < (radius * radius);
}
 
irr::video::IImage* createJapaneseFlagImage(irr::video::IVideoDriver* driver,
        const irr::core::dimension2du& size = irr::core::dimension2du(512, 512),
        const irr::video::SColor& foregroundColor = irr::video::SColor(255, 199,
                0, 37), const irr::video::SColor& backgroundColor =
                irr::video::SColor(255, 255, 255, 255))
{
    static const unsigned int BITDEPTH = 4;
    const unsigned int dataSize = size.Width * size.Height * BITDEPTH;
    irr::u8 imageData[dataSize];
 
    const irr::u32 centerX = size.Width / 2;
    const irr::u32 centerY = size.Height / 2;
 
    irr::u32 radius = 0;
    if (centerX > centerY)
        radius = size.Height - centerY;
    else
        radius = size.Width - centerX;
 
    radius*=0.5f;
 
    for (irr::u32 i = 0; i < dataSize; i += BITDEPTH)
    {
        const irr::u32 x = (i / BITDEPTH) % size.Width;
        const irr::u32 y = i / (BITDEPTH * size.Width);
 
        if (isInCircle(x, y, centerX, centerY, radius))
        {
            //point is in circle, so draw in foreground color
            imageData[i] = foregroundColor.getBlue();
            imageData[i + 1] = foregroundColor.getGreen();
            imageData[i + 2] = foregroundColor.getRed();
            imageData[i + 3] = foregroundColor.getAlpha();
        }
        else //draw background
        {
            imageData[i] = backgroundColor.getBlue();
            imageData[i + 1] = backgroundColor.getGreen();
            imageData[i + 2] = backgroundColor.getRed();
            imageData[i + 3] = backgroundColor.getAlpha();
        }
    }
 
    void* data = &imageData[0];
    return driver->createImageFromData(irr::video::ECF_A8R8G8B8,
            irr::core::dimension2du(size.Width, size.Height), data);
}
 
int main()
{
    irr::IrrlichtDevice* const device = irr::createDevice(
            irr::video::EDT_OPENGL, irr::core::dimension2du(800, 600));
 
    irr::video::IVideoDriver* const driver = device->getVideoDriver();
    irr::scene::ISceneManager* const smgr = device->getSceneManager();
 
    irr::scene::ICameraSceneNode* const camera = smgr->addCameraSceneNode(0,
            irr::core::vector3df(1.0f, 1.0, -3.5f));
    camera->setTarget(irr::core::vector3df(1.0f, 0.5f, 0.5f));
 
    //shader
    irr::video::IGPUProgrammingServices* const gpu =
            driver->getGPUProgrammingServices();
 
    MyShaderCallBack* const mc = new MyShaderCallBack;
 
    irr::s32 shaderMaterialType = gpu->addHighLevelShaderMaterial(
            vertexShader.c_str(), "main", irr::video::EVST_VS_1_1,
            fragmentShader.c_str(), "main", irr::video::EPST_PS_1_1, mc);
 
    mc->drop();
 
    //make texture out of an IImage
    irr::video::IImage* image = createJapaneseFlagImage(driver);
    irr::video::ITexture* texture = driver->addTexture("Another texture", image);
    image->drop();
 
    //shared material
    irr::video::SMaterial material;
    material.Lighting = false;
    material.TextureLayer[0].Texture = texture;
    material.TextureLayer[0].getTextureMatrix().setTextureScale(2, 2);
    material.TextureLayer[0].getTextureMatrix().setTextureTranslate(0.5, 0);
 
    //cube 1 with shader
    {
        irr::scene::IMeshSceneNode* node = smgr->addCubeSceneNode(1.0f);
        node->getMaterial(0) = material;
        node->setMaterialType((irr::video::E_MATERIAL_TYPE) shaderMaterialType);
    }
 
    //cube 2 without shader
    {
        irr::scene::IMeshSceneNode* node = smgr->addCubeSceneNode(1.0f, 0, -1,
                irr::core::vector3df(1.5, 0, 0));
        node->getMaterial(0) = material;
    }
 
    //cube 3 with vanilla texture
    {
        irr::scene::IMeshSceneNode* node = smgr->addCubeSceneNode(1.0f, 0, -1,
                irr::core::vector3df(1.0, 1.5, 0.0));
        node->setMaterialTexture(0, texture);
        node->setMaterialFlag(irr::video::EMF_LIGHTING, false);
    }
 
    while (device->run())
    {
        driver->beginScene();
        smgr->drawAll();
        driver->endScene();
    }
 
    device->drop();
 
    return 0;
}
 
 
"Whoops..."
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Translation of texture doesn't work in gl_TextureMatrix

Post by CuteAlien »

Workaround for now:

Code: Select all

 
void D3DtoGLTextureMatrix(irr::core::matrix4& m)
{
    m[2] = 0.f;
    m[3] = 0.f;
 
    m[6] = 0.f;
    m[7] = 0.f;
 
    m[12] = m[8];
    m[13] = m[9];
    m[8] = 0.f;
    m[9] = 0.f;
    m[10] = 1.f;
    m[11] = 0.f;
 
 
    m[14] = 0.f;
    m[15] = 1.f;
}
 
Call D3DtoGLTextureMatrix on your matrix before sending it.
Note that this function doesn't work when you used setTextureTranslateTransposed on the matrix.

There should obviously be a converter function in the matrix and the setTexture functions whould all mention that they are for d3d (and maybe additional functions for opengl). Just not adding that now because of the setTextureTranslateTransposed not being supported (I got the conversion function out of opengl driver where it's wrong already).

Or maybe it should even convert it internally at some point (but might be somewhat tricky getting that working without breaking all user-apps).

Also seems you are not the only person who got trapped by that - the code in the ogles2 driver and the COpenGLMaterialRenderer_SPHERE_MAP materials also don't seem to do that stuff (might be correct in COpenGLMaterialRenderer_SPHERE_MAP, but probably wrong in ogles2).

Sorry, got too much stuff right now on my todo's to dig deeper into this currently - but with the conversion function it should at least work now.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply