Page 1 of 2

[no bug] Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 4:05 pm
by Foaly
Textures are flipped along the Y axis when rendering with OpenGL, but not when rendering with D3D9.
I'm not that great writing C++ programs, so the code is just an altered example.
It just renders an image into a render target and then from the render target onto the screen.

Code: Select all

#include <irrlicht.h>
#include <iostream>
#include "driverChoice.h"
#include "exampleHelper.h"
 
using namespace irr;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
IrrlichtDevice* device = 0;
 
class MyShaderCallBack : public video::IShaderConstantSetCallBack
{
public:
    MyShaderCallBack() : WorldViewProjID(-1), FirstUpdate(true)
    {
    }
 
    virtual void OnSetConstants(video::IMaterialRendererServices* services,
            s32 userData)
    {
        video::IVideoDriver* driver = services->getVideoDriver();
 
        // get shader constants id.
 
        if (FirstUpdate)
        {
            WorldViewProjID = services->getVertexShaderConstantID("cameraTransformMatrix");
 
            // Textures ID are important only for OpenGL interface.
 
            if(driver->getDriverType() == video::EDT_OPENGL)
                TextureID = services->getVertexShaderConstantID("TexA");
 
            FirstUpdate = false;
        }
 
        // set clip matrix
 
        core::matrix4 worldViewProj;
        worldViewProj = driver->getTransform(video::ETS_PROJECTION);
        worldViewProj *= driver->getTransform(video::ETS_VIEW);
        worldViewProj *= driver->getTransform(video::ETS_WORLD);
 
        services->setVertexShaderConstant(WorldViewProjID, worldViewProj.pointer(), 16);
 
 
        // set texture, for textures you can use both an int and a float setPixelShaderConstant interfaces (You need it only for an OpenGL driver).
        s32 TextureLayerID = 0;
        services->setPixelShaderConstant(TextureID, &TextureLayerID, 1);
    }
 
private:
    s32 WorldViewProjID;
    s32 TextureID;
 
    bool FirstUpdate;
};
 
/*
The next few lines start up the engine just like in most other tutorials
before. But in addition, we ask the user if he wants to use high level shaders
in this example, if he selected a driver which is capable of doing so.
*/
int main()
{
    // ask user for driver
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;
 
    // create device
 
    device = createDevice(driverType, core::dimension2d<u32>(640, 480));
 
    if (device == 0)
        return 1; // could not create selected driver.
 
 
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
    gui::IGUIEnvironment* gui = device->getGUIEnvironment();
 
    const io::path mediaPath = getExampleMediaPath();
 
    char* vsShaderProgram;
    char* psShaderProgram;
 
    switch(driverType)
    {
    case video::EDT_DIRECT3D9:
        vsShaderProgram =
"struct VS_INPUT\n"
"{\n"
"   float4 position : POSITION;\n"
"   float2 tcoord0  : TEXCOORD0;\n"
"};\n"
""
"struct VS_OUTPUT\n"
"{\n"
"   float4 position : POSITION;\n"
"   float2 tcoord0  : TEXCOORD0;\n"
"};\n"
""
"float4x4 cameraTransformMatrix;\n"
""
"VS_OUTPUT vertexMain(VS_INPUT input)\n"
"{\n"
"   VS_OUTPUT output;\n"
"   output.position = mul(input.position, cameraTransformMatrix);\n"
"   output.tcoord0 = input.tcoord0;\n"
"   return output;\n"
"}\n";
        psShaderProgram = 
"struct PS_OUTPUT\n"
"{\n"
"   float4 color    : COLOR;\n"
"};\n"
""
"struct PS_INPUT\n"
"{\n"
"   float4 position : POSITION;\n"
"   float2 tcoord0  : TEXCOORD0;\n"
"};\n"
""
"sampler2D texA;\n"
""
"PS_OUTPUT pixelMain(PS_INPUT input)\n"
"{\n"
"   PS_OUTPUT output;\n"
"   float4 color = tex2D(texA, input.tcoord0);\n"
"   output.color = color;\n"
"   return output;\n"
"}\n";
        break;
    case video::EDT_OPENGL:
        vsShaderProgram =
"uniform mat4 cameraTransformMatrix;\n"
""
"void main(void)\n"
"{"
"   gl_Position = cameraTransformMatrix * gl_Vertex;\n"
"   gl_TexCoord[0] = gl_MultiTexCoord0;\n"
"}\n";
        psShaderProgram =
"uniform sampler2D texA;\n"
""
"void main(void)\n"
"{\n"
"   vec4 color = texture(texA, gl_TexCoord[0]);\n"
"   gl_FragColor = color;\n"
"}\n";
        break;
    }
 
    video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
    s32 newMaterialType = 0;
 
    if (gpu)
    {
        MyShaderCallBack* mc = new MyShaderCallBack();
 
        const video::E_GPU_SHADING_LANGUAGE shadingLanguage = video::EGSL_DEFAULT;
        newMaterialType = gpu->addHighLevelShaderMaterial(
            vsShaderProgram, "vertexMain", video::EVST_VS_2_0,
            psShaderProgram, "pixelMain", video::EPST_PS_3_0,
            mc, video::EMT_SOLID, 0, shadingLanguage);
 
        mc->drop();
    }
 
    //simple material with any image on it.
    video::SMaterial material1 = video::SMaterial();
    material1.setTexture(0, driver->getTexture(mediaPath + "irrlichtlogo.bmp"));
    material1.setFlag(video::EMF_LIGHTING, false);
    material1.setFlag(video::EMF_BACK_FACE_CULLING, false);
    material1.MaterialType = ((video::E_MATERIAL_TYPE)newMaterialType);
 
    video::ITexture* targetTex1 = driver->addRenderTargetTexture(core::dimension2du(1280, 720), "target1");
 
    //second material has the render target as a texture
    video::SMaterial material2 = video::SMaterial(material1);
    material2.setTexture(0, targetTex1);
 
    float zValue = -0.5f;
    video::SColor defaultColor = video::SColor(255, 255, 255, 255);
 
    // 0 - 1
    // 2 - 3
    video::S3DVertex quadVerts[4] =
    {
        video::S3DVertex(core::vector3df(-.5f,+.5f,zValue), core::vector3df(0), defaultColor, core::vector2df(0,0)),
        video::S3DVertex(core::vector3df(+.5f,+.5f,zValue), core::vector3df(0), defaultColor, core::vector2df(1,0)),
        video::S3DVertex(core::vector3df(-.5f,-.5f,zValue), core::vector3df(0), defaultColor, core::vector2df(0,1)),
        video::S3DVertex(core::vector3df(+.5f,-.5f,zValue), core::vector3df(0), defaultColor, core::vector2df(1,1))
    };
    u16 quadIndices[6] =
    {
        0, 1, 2,
        1, 3, 2
    };
 
    core::matrix4 projectMatrix = core::matrix4();
    core::matrix4 worldMatrix = core::matrix4();
    core::matrix4 cameraMatrix = core::matrix4();
    cameraMatrix = cameraMatrix.buildProjectionMatrixOrthoRH(1.0f, 1.0f, 0.01f, 100.0f);
 
    int lastFPS = -1;
 
    while(device->run())
        if (device->isWindowActive())
    {
        driver->beginScene(video::ECBF_ALL, video::SColor(255,0,0,0));
 
        driver->setTransform(video::ETS_PROJECTION, projectMatrix);
        driver->setTransform(video::ETS_WORLD, worldMatrix);
        driver->setTransform(video::ETS_VIEW, cameraMatrix);
 
 
        //render on the first render target
        driver->setRenderTarget(targetTex1, video::ECBF_ALL);
        driver->setMaterial(material1);
        driver->drawVertexPrimitiveList(quadVerts, 4, quadIndices, 2);
 
        //render on the second render target
        driver->setRenderTarget(nullptr);
        driver->setMaterial(material2);
        driver->drawVertexPrimitiveList(quadVerts, 4, quadIndices, 2);
 
        driver->endScene();
 
        int fps = driver->getFPS();
 
        if (lastFPS != fps)
        {
            core::stringw str = L"Irrlicht Engine - Y flipping bug [";
            str += driver->getName();
            str += "] FPS:";
            str += fps;
 
            device->setWindowCaption(str.c_str());
            lastFPS = fps;
        }
    }
 
    device->drop();
 
    return 0;
}
As you can see, the code already contains the shaders, but here they are again, so they are easier to read:
OpenGL:

Code: Select all

//Vertex Shader:
uniform mat4 cameraTransformMatrix;
 
void main(void)
{
    gl_Position = cameraTransformMatrix * gl_Vertex;
    gl_TexCoord[0] = gl_MultiTexCoord0;
}
 
//Pixel Shader:
uniform sampler2D texA;
 
void main(void)
{
    vec4 color = texture(texA, gl_TexCoord[0]);
    gl_FragColor = color;
}
Direct3D9:

Code: Select all

struct VS_INPUT
{
    float4 position : POSITION;
    float2 tcoord0  : TEXCOORD0;
};
 
struct VS_OUTPUT
{
    float4 position : POSITION;
    float2 tcoord0  : TEXCOORD0;
};
 
float4x4 cameraTransformMatrix;
 
VS_OUTPUT vertexMain(VS_INPUT input)
{
    VS_OUTPUT output;
    output.position = mul(input.position, cameraTransformMatrix);
    output.tcoord0 = input.tcoord0;
    return output;
}
 
struct PS_OUTPUT
{
    float4 color    : COLOR;
};
 
struct PS_INPUT
{
    float4 position : POSITION;
    float2 tcoord0  : TEXCOORD0;
};
 
sampler2D texA;
 
PS_OUTPUT pixelMain(PS_INPUT input)
{
    PS_OUTPUT output;
    float4 color = tex2D(texA, input.tcoord0);
    output.color = color;
    return output;
}

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 4:31 pm
by CuteAlien
Seems to be something new. When running in Irrlicht 1.8 (with a few changes to get it to compile) it still works.
Also when using non-shader materials it works (in trunk as well). Only fails with shader-material (for second material, first one doesn't matter) and in trunk and with opengl. Maybe related to changed texture lock()?

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 5:01 pm
by Nadro
It's related to OpenGL and textures origin at the lower left corner. During v1.9 development process we disabled some stuff for shader based materials like an auto flip for RT texture matrices in OpenGL, because it caused unnecessary overhead (you can flip UV directly in the shader code).

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 5:11 pm
by Foaly
So it's supposed to be that way and it will stay like that?

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 5:23 pm
by CuteAlien
No can't stay. That would mean this is a specific behavior for one driver, specific materials and depending on the texture-type! You change any of those 3 and the texture flips. That's not good.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 5:43 pm
by Nadro
It's really a corner case. Why we want to force all users to worse performance. I understand that it should be flipped in built-in fixed-pipeline materials, because users don't have control of them, but in the shaders it's not a problem anymore.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 5:56 pm
by CuteAlien
It's highly confusing. You have to write different shaders now based on texture-type as it does not flip the usual textures. The cost was always there, but looks like in the past it was decided that it was worth to handle this in the engine so the users won't have to care. If we stop caring about this the engine usability is worse than what we already had. If you want to add a flag to get optimized behavior for experts - that's fine. But please don't make this the default.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 7:10 pm
by Nadro
OK, we would add compile-time optimization flag (disabled at default), however please remember that it will not work for OpenGL ES2.0+ and OpenGL 3.x Core Profile+ (second option is still unavailable in Irrlicht). Please also remember that if you use shaders you still need to write driver specific shaders and for most of OpenGL/GLSL programmers situation with 'flipped' FBO is normal, thats why I think that it shouldn't be a problem (texture matrices overhead is really high and if more texture unit will be active overhead will be even higher, at now we just have 4 active texture units per SMaterial at default, but user can change this value).

BTW. You don't have to write different shaders for RT and normal textures. You can send texture matrix as an uniform (at this case user just send this matrix if it's required). In GLSL (vertex shader) you just need to multiply tex coords by matrix eg:
vec2 goodUV = vec4(TexMatrixPassedAsUniform * gl_MultiTexCoord0).xy;
Most of the time RT textures are used at post processing effects with full screen quad, where you can set UV what you want, so you don't even need FlippedTextureMatrix passed as uniform (the best performance). If you use shaders there is a few solutions for this problem.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 7:26 pm
by CuteAlien
You could also use a runtime-flag. We got a few of those for textures.
Having different results in opengl es is obviously also not good :-(
Why is OpenGL flipping rendertarget textures...?

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 7:44 pm
by Foaly
As long as it's documented, I don't think that it would be a big problem if OpenGL just kept flipping the render target.
It only happens if you're writing a shader anyway and it can be fixed easily if you're aware of it.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 8:19 pm
by CuteAlien
I think it was that way in the past. Not a big problem but one of the regular kind where every programmer runs into it again and wastes hours. Which is why you should have a 3D engine which cares about it for you. I can't say this is something important to me, but if we don't do that we will be back to regular posts and irc questions about this. Having it just work (as in Irrlicht 1.8) is simply nice to have. And yeah - speed hit is ugly... tech is just in a state where you can't do things perfect anymore.

Re: Texture Y flipped with OpenGL.

Posted: Mon Mar 07, 2016 8:39 pm
by hendu
FYI, in 1.7 I disabled the auto-flipping, because when chaining multiple RTTs it resulted in randomly flipped or not image (if the number of RTTs was divisible by 2 or not).

Re: Texture Y flipped with OpenGL.

Posted: Tue Mar 08, 2016 7:43 pm
by Nadro
Flipping ping-pong mentioned by Hendu is another reason why this thing should stay as it is now.

Re: Texture Y flipped with OpenGL.

Posted: Tue Mar 08, 2016 8:44 pm
by CuteAlien
Was that still happening in Irrlicht 1.8?

It's pretty horrible, but if OpenGL really doesn't allow working around that in a sane way then I guess there's nothing we can do.

Re: Texture Y flipped with OpenGL.

Posted: Tue Mar 08, 2016 8:58 pm
by hendu
I don't know, I haven't used 1.8.

Recent GL has a "be like DX" extension which changes the origin, depth, and some other things.