Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Javascript's “Math.sin/Math.cos” and WebGL's “sin/cos” give different results


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 lallish   Members   -  Reputation: 191

Like
0Likes
Like

Posted 08 July 2013 - 05:29 PM

I'm trying to generate fibonacci distributed points on a sphere. However when I try to do so in the shader on the GPU, the calculation of sin/cos becomes very different than if I would have calculated Math.sin/Math.cos on the CPU. Here is part of the vertex shader of the fibonacci function:
 
    
attribute float index;          //array with numbers between [1, 2500000]


       float inc = 3.141592653589793238462643383279 * (3.0 - sqrt(5.0));
       float off = 2.0 / 2500000;
       float yy = index * off - 1.0 + (off / 2.0);
       float rr = sqrt(1.0 - yy * yy);
       float phi = index* inc;                     // big number, "5476389.695241543"-biggish
       vec3 fibPoint = vec3(cos(phi) * rr, yy, sin(phi) * rr);  // calculates sin/cos wrong
 
This part gives the wrong fibPoint vectors of locations that looks like this: http://i.imgur.com/Z1crisy.png
 
And when I calculate the Math.sin(phi) and Math.cos(phi) of the fibPoint vector in javascript, and throw these values in as attributes into the shader instead, so the code looks like:
 
   
 /* short version of code from javascript */
    var y = index * off - 1 + (off / 2.0);
    var r = Math.sqrt(1 - y * y);
    var phi = index * inc;
    var cosphi = Math.cos(phi);
    var sinphi = Math.sin(phi);
      ....
      ..


    /* throw cosphi/sinphi into the shader as attributes along with the index */
    
    
    /* vertex shader */
    attribute float index;
    attribute float sinphi;
    attribute float cosphi;


       float inc = 3.141592653589793238462643383279 * (3.0 - sqrt(5.0));
       float off = 2.0 / 2500000;
       float yy = index * off - 1.0 + (off / 2.0);
       float rr = sqrt(1.0 - yy * yy);
       //float phi = index* inc;
       vec3 fibPoint = vec3(cosphi * rr, yy, sinphi * rr);
 
 
This gives the correct fibPoint vectors. Now the locations looks like this: http://i.imgur.com/DeRoXkL.png
 
So my question is, why doesn't WebGL's sin/cos give same or similar result as javascript's Math.sin/cos? As far as I know, both input is radians, and both output [-1, 1]. May it be because "phi" are too large numbers so some part get truncated in the cos/sin?
 
Thanks for reading.
 


Sponsor:

#2 Cornstalks   Crossbones+   -  Reputation: 6999

Like
5Likes
Like

Posted 08 July 2013 - 06:17 PM

The big number is an issue. If your vertex shader is working with 32-bit floats, that only gives you 6 decimal digits of precision. 5476389.695241543 to 6 decimal digits of precision is 5476380.000000 (truncating everything after the 6 digits). Pi is only ~3.14, and since sin/cos are periodic, using large numbers doesn't give you any benefit over using smaller numbers (because the large numbers just wrap around). However, your numbers are so large that they wrap around so much that they don't even precisely map to the [-pi, pi] (or [0, 2pi]) range. Basically, the wrapping around throws away all the "high" digits and only keeps the relevant low digits, but unfortunately for you all your low digits are junk because you spent all 6 of your precision digits on the ones that get thrown away, and now all your low (but most important) digits are meaningless.

 

In short, yes, those huge numbers will kill you.

 

However, in JavaScript, all floating point numbers are 64-bit, which gives you 15 decimal digits of precision. That means in JavaScript you can actually properly represent 5476389.69524154, so your trig calculations are actually accurate (assuming your JavaScript code is processing the same large values that your vertex shader is).


Edited by Cornstalks, 08 July 2013 - 06:26 PM.

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#3 MarkS   GDNet+   -  Reputation: 1562

Like
1Likes
Like

Posted 08 July 2013 - 06:59 PM

Maybe I am completely off here, but if your desire is for the cubes to be distributed across the surface of a sphere, why not make the inputs to sin and cos lie between -pi and pi? This would make the initial positions lie on a unit sphere and then you can scale them by the radius of the large sphere to get their final positions.



#4 lallish   Members   -  Reputation: 191

Like
0Likes
Like

Posted 09 July 2013 - 07:37 AM

Thanks for that information Cornstalks. Do you suggest calculating the fibPoint on the CPU instead and throw the vec3 in as an attribute? Because I'll be dealing with much larger numbers later, index might go as high as 400 000 000

 

Maybe I am completely off here, but if your desire is for the cubes to be distributed across the surface of a sphere, why not make the inputs to sin and cos lie between -pi and pi? This would make the initial positions lie on a unit sphere and then you can scale them by the radius of the large sphere to get their final positions.

 

 

Yes, I could do that, with something like: 

float tphi = phi - twoPI*ceil(phi/twoPI - 1.0);

But I would still need the precision of phi to do that, and "yy" and "off" too if I expand my index in the future.


Edited by lallish, 09 July 2013 - 07:37 AM.


#5 apatriarca   Crossbones+   -  Reputation: 1939

Like
1Likes
Like

Posted 09 July 2013 - 08:04 AM

Computing that number in Javascript on the CPU solves the problem only partially. At some point you may have problems using doubles too. Big numbers and trigonometric functions do not work really well together... Why are you using such formula to generate the points?



#6 Cornstalks   Crossbones+   -  Reputation: 6999

Like
1Likes
Like

Posted 09 July 2013 - 08:26 AM

Thanks for that information Cornstalks. Do you suggest calculating the fibPoint on the CPU instead and throw the vec3 in as an attribute? Because I'll be dealing with much larger numbers later, index might go as high as 400 000 000

If the points don't move around and are static, that might make the most sense to do. Even if you simplify the formula to be θj = arccos(1 - (2j + 1) / N), Φj = 2jπφ-1, 0 <= j < N, you run into precision errors as j grows > 999,999 using 32-bit floats.

 

 

Computing that number in Javascript on the CPU solves the problem only partially. At some point you may have problems using doubles too. Big numbers and trigonometric functions do not work really well together... Why are you using such formula to generate the points?

It's a specific formula for generating a perfect distribution of points over a sphere.


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#7 lallish   Members   -  Reputation: 191

Like
0Likes
Like

Posted 09 July 2013 - 08:46 AM

Computing that number in Javascript on the CPU solves the problem only partially. At some point you may have problems using doubles too. Big numbers and trigonometric functions do not work really well together... Why are you using such formula to generate the points?

I want to distribute points equally over a sphere and I don't know another way of doing so, do you know of a better formula?

 

If the points don't move around and are static, that might make the most sense to do. Even if you simplify the formula to be θj = arccos(1 - (2j + 1) / N), Φj = 2jπφ-1, 0 <= j < N, you run into precision errors as j grows > 999,999 using 32-bit floats.

 

 

They are static yep. What I will try now then is to use Web Workers to calculate the points to off load the main thread in javascript, only thing I'm afraid of is how many attributes I can update at the same time without any major performance hit, as it is three floats now instead of just a single index float.


Edited by lallish, 09 July 2013 - 09:01 AM.


#8 Cornstalks   Crossbones+   -  Reputation: 6999

Like
1Likes
Like

Posted 09 July 2013 - 09:14 AM

They are static yep. What I will try now then is to use Web Workers to calculate the points to off load the main thread in javascript, only thing I'm afraid of is how many attributes I can update at the same time without any major performance hit, as it is three floats now instead of just a single index float.

You could do it with 2 floats and 1 uniform: the two floats are phi (in range [0, pi]) and theta (in range [0, 2pi]), and the uniform is the sphere's radius.

 

If that turns out to be too many objects you may need to do some space partitioning.


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#9 Bacterius   Crossbones+   -  Reputation: 10825

Like
0Likes
Like

Posted 09 July 2013 - 09:19 AM

If I understand properly the problem is that as "index" grows arbitrarily large, which means "index * inc" (where "inc" corresponds to some rotation in radians) loses all fractional precision, where it matters. The expected output is an aperiodic list of angles 0, inc, 2inc, 3inc, etc.. wrapping around the unit circle.

 

To that end, why not simply keep a running sum of the angle (phi) by adding "inc" at each iteration, and subtract 2 * pi every time phi exceeds 2 * pi (do that on the CPU). That will solve the problem nicely.

 

Or something like that. Maybe I misunderstood.


“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”


#10 lallish   Members   -  Reputation: 191

Like
0Likes
Like

Posted 09 July 2013 - 10:33 AM

You could do it with 2 floats and 1 uniform: the two floats are phi (in range [0, pi]) and theta (in range [0, 2pi]), and the uniform is the sphere's radius.

 

If that turns out to be too many objects you may need to do some space partitioning.

 

Yep, that's correct. Thanks!

 

If I understand properly the problem is that as "index" grows arbitrarily large, which means "index * inc" (where "inc" corresponds to some rotation in radians) loses all fractional precision, where it matters. The expected output is an aperiodic list of angles 0, inc, 2inc, 3inc, etc.. wrapping around the unit circle.

 

To that end, why not simply keep a running sum of the angle (phi) by adding "inc" at each iteration, and subtract 2 * pi every time phi exceeds 2 * pi (do that on the CPU). That will solve the problem nicely.

 

Or something like that. Maybe I misunderstood.

 

It would work, except I don't do it iterative, I fetch precomputed indices from surrounding and only generate the needed points.


Edited by lallish, 09 July 2013 - 10:37 AM.


#11 apatriarca   Crossbones+   -  Reputation: 1939

Like
0Likes
Like

Posted 09 July 2013 - 12:40 PM

I didn't know this algorithm to generate uniform points on a sphere. smile.png That's interesting.. How do you assign indices to regions? Wouldn't be easier to just assign the precomputed points instead of the indices?

If all you need is generate a uniform set of points on some sub-region of the sphere, a set of spherical triangles for example, you can also use some kind of uniform sampling strategy on spherical triangles. I know one algorithm which can be found in the chapter "Stratified Sampling of 2-Manifolds" by James Arvo in the "State of the Art in Monte Carlo Ray Tracing for Realistic Image Synthesis" Siggraph 2001 course. This is quite expensive however. There may be faster methods which I don't know.

#12 lallish   Members   -  Reputation: 191

Like
0Likes
Like

Posted 09 July 2013 - 02:34 PM

I didn't know this algorithm to generate uniform points on a sphere. smile.png That's interesting.. How do you assign indices to regions? Wouldn't be easier to just assign the precomputed points instead of the indices?

If all you need is generate a uniform set of points on some sub-region of the sphere, a set of spherical triangles for example, you can also use some kind of uniform sampling strategy on spherical triangles. I know one algorithm which can be found in the chapter "Stratified Sampling of 2-Manifolds" by James Arvo in the "State of the Art in Monte Carlo Ray Tracing for Realistic Image Synthesis" Siggraph 2001 course. This is quite expensive however. There may be faster methods which I don't know.

I have an Icosahedron sphere, which is divided into almost equally big triangles, or hexagons with an occasional pentagon, so they create a tile system for you if you're inside the sphere. And each triangle have assigned indices to them. 

 

And why I've precomputed indices and not the actual points is a good question! It's a matter of reducing the size of the files, especially if it's a file that a client will download from your webserver, reducing the file transfer by half might be a better choice than the small performance hit your client will get from computing the points him/herself. I'm still experimenting, Just now, the indices in the interval [0, 2500000] so far take 18 mb. But, as the indices grow higher, the points will remain with a consistent precision, and size. So in the future when I know more about Javascript compression, I'll probably consider precomputing the points instead.



#13 apatriarca   Crossbones+   -  Reputation: 1939

Like
0Likes
Like

Posted 10 July 2013 - 06:37 AM

I'm not an expert on JavaScript but if your points are static, then I think it is probably better to create all the point once on the CPU side and then load them in some static buffer. Using this solution you can later decide to directly send the points without changing the rendering code and you can use doubles for the increased precision they provide.
Are you transmitting all the data at once or on demand? If you only need a small subset of the indices you may save some time requesting the indices only when needed. Have you tried compressing your file?




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS