3

I am developing a game for Android using OpenGL ES 2.0 and have a problem with a fragment shader for drawing stars in the background. I've got the following code:

precision mediump float;
varying vec2 transformed_position;

float rand(vec2 co) {
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main(void) {
    float distance = 10.0;
    float quantization_strength = 4.0;
    vec3 background_color = vec3(0.09, 0.0, 0.288);
    vec2 zero = vec2(0.0, 0.0);
    vec2 distance_vec = vec2(distance, distance);
    vec2 quantization_vec = vec2(quantization_strength, quantization_strength);
    vec2 new_vec = floor(transformed_position / quantization_vec) * quantization_vec;
    if(all(equal(mod(new_vec, distance_vec), zero))) {
        float rand_val = rand(new_vec);
        vec3 current_color = background_color * (1.0 + rand_val);
        gl_FragColor = vec4(current_color.x, current_color.y, current_color.z, 1.0);
    } else {
        gl_FragColor = vec4(background_color.x, background_color.y, background_color.z, 1.0 );
    }
}

My aim is to 'quantize' the fragment coordinates, so 'stars' are not 1px in size, and then light up quantized pixels that are distant enough by a random amount. This code, however, produces different results depending on where it is executed. I have used GLSL Sandbox (http://glsl.heroku.com), Nexus 7 and HTC Desire S to create comparison:

Shader results comparison

As you can see, GLSL Sandbox produces dense grid with many stars visible. On Nexus 7 stars are much fewer and distributed along lines (which may be not obvious on this small image) - the rand function does not work as expected. Desire S draws no stars at all.

Why does the rand function work so strangely on Nexus 7 (if I modify the vector used for dot product, stars are distributed along lines at different angle)? And what might cause Desire S not to render the stars?

I would also appreciate any optimization tips for this shader, as I am very inexperienced with GLSL. Or perhaps there is better way to draw 'stars' via fragment shader?

UPDATE

I changed the code to this (I used http://glsl.heroku.com/e#9364.0 as reference):

precision mediump float;
varying highp vec2 transformed_position;
highp float rand(vec2 co) {
    highp float a = 1e3;
    highp float b = 1e-3;
    highp float c = 1e5;
    return fract(sin((co.x+co.y*a)*b)*c);
}

void main(void) {
    float size = 15.0;
    float prob = 0.97;
    lowp vec3 background_color = vec3(0.09, 0.0, 0.288);
    highp vec2 world_pos = transformed_position;
    vec2 pos = floor(1.0 / size * world_pos);
    float color = 0.0;
    highp float starValue = rand(pos);
    if(starValue > prob) {
    vec2 center = size * pos + vec2(size, size) * 0.5;
    float xy_dist = abs(world_pos.x - center.x) * abs(world_pos.y - center.y) / 5.0;
    color = 0.6 - distance(world_pos, center) / (0.5 * size) * xy_dist;
    }
    if(starValue < prob || color < 0.0) {
        gl_FragColor = vec4(background_color, 1.0);
    } else {
        float starIntensity = fract(100.0 * starValue);
        gl_FragColor = vec4(background_color * (1.0 + color * 3.0 * starIntensity), 1.0);
    }
}

Desire S now gives me very nice, uniformly distributed stars. But the problem with Nexus 7 is still there. With prob = 0.97, no stars are displayed, and with very low prob = 0.01, they appear very sparsely placed along horizontal lines. Why does Tegra 3 behave so strangely?

Matis
  • 503
  • 5
  • 17
  • did you check if on Desire S you're getting compiled and linked shader – Selvin Jul 11 '13 at 14:55
  • Yes, my application would throw an exception otherwise and background color would also be different without the shader. – Matis Jul 11 '13 at 14:58
  • I recommend you make the critical variables "highp" instead of letting the shader compiler decide what precision to use. Not all GPUs have mediump. Also, you should test this on an AVD with a current Nvidia card and OpenGL drivers installed for a reliable reference. See: http://software.intel.com/en-us/articles/porting-opengl-games-to-android-on-intel-atom-processors-part-1 – ClayMontgomery Jul 11 '13 at 18:51
  • Try exchanging x and y in you rand calculation function and see if lines will turn 90 degrees, also it worth trying setting precision to high. – Vasaka Jul 17 '13 at 14:38
  • From the results I'm seeing, I'm *suspecting* that the sin function in the GPU on the Nexus 7 has a different precision from that in most other Android devices, but I'm still digging into things. In the meantime, if you've found an answer that works, please let us know, @Matis! – Matt Gibson Sep 28 '13 at 14:03
  • Related: http://stackoverflow.com/questions/11293628/noise-algorithm-fails-in-samsung-galaxy-siii-gles – Matt Gibson Sep 28 '13 at 15:06
  • @MattGibson I asked similar question on NVIDIA forums and got the answer: https://devtalk.nvidia.com/default/topic/552391/android-development/possible-shader-precision-issue-in-fragment-shader-on-nexus-7/. Tegra uses fp20 precision, while Adreno 205 (found in Desire S) uses fp32. – Matis Sep 29 '13 at 14:19
  • @Matis Ah, I didn't realise that post was yours, too. Sadly I've not come across a proper fix. While there seem to be good generators for Perlin or Simplex noise out there that work, that's not really what I want. I'm looking to generate TV-style static -- [as described by Daniel Jalkut here](http://indiestack.com/2013/09/static-analysis/). Daniel has the advantage of writing for iOS, where it seems like the shaders are fp32 across all the hardware... – Matt Gibson Sep 29 '13 at 17:00

0 Answers0