11

I am trying to write a compact and simple noise function with a strictly FP16 limit. This is with what I came out so far, but I think somewhere on the operation the number gets too small for fract or sin, since in the GPU I must write this for these are within the FP16 limits. Any ideas on what am I doing wrong? BY the way, I cannot use a time variables, neither sample noise textures. The function I need to get right must be compact, small and self-sufficient, and produce a simple grainy noise effect. Note: The next algorithm works fine in any desktop GPU card, but fails completely on the "MALI 400 MP" GPU, since this one has a FP16 limitation on float values.

vec3 noise(vec3 color)
{
    float variation = length(color);
    float dot_product = dot(variation, -0.577350269);
    float sin_result = sin(dot_product) * 1.19245;
    float random = fract(sin_result);
    return color + vec3(random);
}

If any one can recommend any other random function for GLSL-ES but strictly with a FP16 limit, would also be great. I know about other random implementations such as simplex noise, but these are too large and slow for what I need to do. So Perlin and Simplex noise algorithms are not an option.

PerracoLabs
  • 16,449
  • 15
  • 74
  • 127
  • By adding a value which is a function of the color to the color, the same "noise" value gets added to all fragments that have the same color. Because objects usually have color gradients, you'll get bands across the object. You can see what I mean by changing the constant inside the dot function to something larger. – Gato Jan 20 '13 at 07:46
  • Does this post [random number with mali 400 mp](http://forums.arm.com/index.php?/topic/16364-random-number-with-mali-400-mp/) help? – andy256 Jul 11 '13 at 05:23
  • 2
    Doesn't make much sense, with this interface and without ability to introduce randomness inside the function, a color block consisting of a single color only can never have actual noise generated, because whatever calculations one does, the return value will still be constant as the input never changes. The function should get in an extra parameter, an index, coordinates, whatever. – Antti Huima Jul 17 '13 at 13:04
  • Anttii, color parameter is the one that helps to produce the noise, as is a single fragment pìxel color, so by doing a length(color) it is possible to generate random noise as each pixel is different. The algorithm works perfect on all GPUs except for the ones that have FP16 – PerracoLabs Jul 17 '13 at 14:38
  • i am afraid that to code the real noise some static variables are needed which are not allowed inside fragment. (uniform or in aren't usable, because they cannot be chnged and or preserved for next fragment) so the only way is to make distortion as function of fragment position,color or texture coordinates but that is not noise :(. – Spektre Sep 13 '13 at 16:09
  • Just saw your question after almost duplicating it: http://stackoverflow.com/questions/23319289/is-there-a-good-glsl-hash-function Practically speaking, why can't you use a noise texture? – starmole Apr 27 '14 at 06:44
  • If F16 is your problem then look at which step in your process is breaking that. It's probably the dot product with that long digit fp number. Can -0.577350269 be stored in FP16? You say it doesn't work but what does that mean? what is your vec3 turning out to be? return only the vec3(random) to test it. – badweasel May 29 '14 at 21:56
  • And I agree with Antti. I only need noise to reduce banding in gradients. If each color returns the same variant it's not really *noise*. How does this function even help you? Look at my fract random function below. It uses the fragment coordinates as a seed on the random instead of the color. – badweasel May 29 '14 at 21:59

3 Answers3

1

These are the ones that I use but I don't know if either works in a FP16 limit:

// source: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
highp float rand(vec2 co)
{
      highp float a = 12.9898;
      highp float b = 78.233;
      highp float c = 43758.5453;
      highp float dt= dot(co.xy ,vec2(a,b));
      highp float sn= mod(dt,3.14);
      return fract(sin(sn) * c);
}

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

I didn't create either of these. The link to the original author is above. I actually use rand2 and didn't have the issue mentioned on that blog. To make a greyscale noise do something like:

float randColor = rand(v_position);
gl_FragColor = vec4(randColor);

To do a full color noise it would take 3 times longer and you'd do:

gl_FragColor = vec4(rand(v_position), rand(v_position), rand(v_position), 1.0);

To add noise to whatever you're drawing you could:

float randColor = rand(v_position) * .1;  // to add 10% noise
gl_FragColor = vec4(gl_FragColor.r + randColor, gl_FragColor.g + randColor, gl_FragColor.b + randColor, 1.0);

By the way, this is slow. On an iPhone5 this works fine with no major slowdown. But on a 4S it dropped by fps down to 30. If I removed adding the noise it raised it to 60. So beware.

badweasel
  • 2,349
  • 1
  • 19
  • 31
  • The problem is using sin() which is very, very implementation dependent in ES2. It might work ok on IOS but his Maali clearly does it differently. – starmole Jun 24 '14 at 05:06
  • I would think using a texture as a lookup table would be the next best thing. But I guess that was out as well. – badweasel Jun 25 '14 at 04:10
0

Would a hash function suffice? Pearson hashing was designed for 8-bit registers back in the days of yore and is fantastically simple: you hardcode a 256-byte lookup table (or if that's out of the question, a simple-but-nonlinear permutation of some kind) which we'll call T. For each byte of the input, you XOR it with the hash so far and then look up the value to get a new hash.

In your case, let R, G and B be your input bytes. Then the hashes could be

  • Rnoise = T[R^T[G^T[B]]]
  • Gnoise = T[G^T[B^T[R]]]
  • Bnoise = T[B^T[R^T[G]]]

E: To be clear, this won't produce a random output because there's no randomness in your input. But I think it mimics what your code is trying to do.

Andy Jones
  • 4,723
  • 2
  • 19
  • 24
  • Unfortunately openGL ES has no integer math and thus no xor. Also any lut is crazy expensive compared to cpus. – starmole Jun 24 '14 at 05:04
0

Although is an old question I eventually found the solution long ago. Next the script so anyone can use it. As seed you should pass a dynamic or random value which you may pass to the shader as an attribute.

float getNoise(vec2 seed)
{
    vec2 theta_factor_a = vec2(0.9898, 0.233);
    vec2 theta_factor_b = vec2(12.0, 78.0);
    
    float theta_a = dot(seed.xy, theta_factor_a);
    float theta_b = dot(seed.xy, theta_factor_b);
    float theta_c = dot(seed.yx, theta_factor_a);
    float theta_d = dot(seed.yx, theta_factor_b);
    
    float value = cos(theta_a) * sin(theta_b) + sin(theta_c) * cos(theta_d);
    float temp = mod(197.0 * value, 1.0) + value;
    float part_a = mod(220.0 * temp, 1.0) + temp;
    float part_b = value * 0.5453;
    float part_c = cos(theta_a + theta_b) * 0.43758;

    return fract(part_a + part_b + part_c);
}
PerracoLabs
  • 16,449
  • 15
  • 74
  • 127