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
and following when same code is run on emulator(Nexus 5 API 21) (with emulated host gpu option) Which is the expected output.
My texture wrapping mode is set to GL_REPEAT
. What might be wrong ?
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:
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.