Search code examples
androidopenglgraphicsopengl-estextures

Open GL Different Results on Desktop GPU and Mobile GPU


I have been trying to port this shader to Mobile Device. I am doing this on android device in OpenGL ES 2.0. here is fragment shader code from above site for reference:

void main(void)
{
// clamp pixel posiiton in [-1,1]
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
vec2 uv;

// calculate angle of current pixel from origin
// atan return values are in [-pi, pi]
float a = atan(p.y,p.x);

// distance of point from origin
//float r = sqrt(dot(p,p));

float power = 7.0;

// http://en.wikipedia.org/wiki/Minkowski_distance
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );

// add global time for a moving tunnel
uv.x = .2/r + iGlobalTime/2.0;
uv.y = a/(3.1416);

// multiplication by r to give a darkened effect  in center
vec3 col = texture2D(iChannel0, uv).xyz * (1.0-r);
//vec3 col = vec3(uv.y, 0.0,0.0);


gl_FragColor = vec4(col,1.0);
}

I am getting following result on my mobile phone(Moto G, Samsung Galaxy S Advance). Note how texture is flat and kind of clamped in middle Image in actual device (Moto G gen 1)

and following when same code is run on emulator(Nexus 5 API 21) (with emulated host gpu option) Image in Emulator/WebGL Which is the expected output.

My texture wrapping mode is set to GL_REPEAT. What might be wrong ?


Solution

  • This is a precision issue. mediump, which is the highest precision that is guaranteed to be supported in ES 2.0 shaders, has a floating point magnitude range of [2^-14, 2^14], as listed in the table on page 33 of the spec ("The OpenGL ES Shading Language", version 1.00, which can be found at https://www.khronos.org/registry/gles/).

    The following sequence of statements will quickly produce underflow:

    float power = 7.0;
    float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );
    

    Looking at this sub-expression:

    pow(p.x*p.x,power)
    

    Based on the calculation/comment at the start of your shader, the range of p.x is [-1, 1]. Using 0.1 as the value:

    pow(0.1 * 0.1, 7.0) = pow(0.01, 7.0) = 10^-14
    

    This is right around the limit for a medium value to underflow. So these sub-expressions will underflow to zero any time either p.x or p.y approaches the range [-0.1, 0.1].

    It's not obvious what the best workaround is. Some ideas to try:

    • Use highp precision if your device supports it. highp support is optional in ES 2.0, and only available if GL_FRAGMENT_PRECISION_HIGH is defined.
    • Try a lower exponent than 7.0, and see if the visual result still meets your requirement.
    • Use some form of clamping for the pixels that would underflow. You could either discard them, or color them black. While it will not render he wall in the center, it would at least avoid the ugly artifacts.