Search code examples
androidopengl-esgame-makerglsles

GameMaker Studio, GLSL ES Shader, different results on Android


I try to implement a fullscreen GLSL ES shader in my GameMaker Studio project. However, I get different results on almost every device I test. I made a small test project to showcase the problem.

Vertex Shader:

attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;

    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
}

Fragment shader:

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    if(v_vTexcoord.x > 0.5)
    {
        gl_FragColor = vec4(0,1,0,1);
    }
    else if(v_vTexcoord.y > 0.5)
    {
        gl_FragColor = vec4(0,0,1,1);
    }
    else
    {
        gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    }
}

And these are the results:
https://dl.dropboxusercontent.com/u/14513191/shader_problem.jpg

This is how it should look on PC / 1920x1080
Samsung Galaxy Alpha / 1280x720
PadFone Infinity / 1920x1080

Has anyone an idea what's going on here?


Solution

  • I suspect the problem is precision. First thing to do is confirm or reject that hypothesis. Try adding the line:

    precision highp float;
    

    Up at the top of both the vertex and fragment shader. If that fixes the problem, then don't leave it there. When you write GLSLES shaders you really should choose correct precisions. My rule of thumb is:

    Positions: Use highp
    Texture coordinates: Use mediump (occasionally need highp)
    Normals: Use mediump
    Colours: Use lowp (occasionally need mediump)
    

    If the precision change doesn't help, then perhaps try to look closer at the values of v_vTexcoord by changing your fragment shader to:

    gl_FragColor = vec4(v_vTexcoord.x,v_vTexcoord.y,1,1);
    

    And post a new screenshot.

    EDIT:

    Thank you for the updated screenshot.

    My best guess is that GameMaker is rounding up the intermediate render targets to the next power of two.

    This means that intermediate render targets on the 1280x720 galaxy alpha are rounded up to 2048x1024 and the 1920x1080 PadFone Infinity up to 2048x2048.

    GameMaker is then only rendering to the top-left section of these over-sized render targets, and when you do full screen shader which copy the screen, it must use bottom-right UV coordinates of: GalaxyAlpha (1280/2048, 720/1024)=(0.625,0.703125) PadFone (1920/2048, 1080/2048)=(0.9375,0.52734375)

    This is pure speculation about what's going on because I don't know GameMaker, but it makes sense and matches the screenshot well (plenty of red on the PadFone, more green on the Galaxy than the PadFone)

    As for what you should do about it... I'm not sure. Clearly you can't use the UV coordinates in the manner you'd hoped. Does GameMaker give you the flexibility to add extra per vertex information? If so, then you could pass through more usable screen coordinates. Otherwise, you could pass the transformed position from your vertex shader into your fragment shader, but it can be a bit messy to untangle (I think you have to divide by W or multiply by W, and it'll be in the -1 to 1 range and might be upside down to how you expect it).