Followers 0

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

## 12 posts in this topic

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?

0

##### Share on other sites

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.

1

##### Share on other sites

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
0

##### Share on other sites

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?

1

##### Share on other sites

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.

1

##### Share on other sites

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
0

##### Share on other sites

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.

1

##### Share on other sites

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.

0

##### Share on other sites

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
0

##### Share on other sites
I didn't know this algorithm to generate uniform points on a sphere. 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.
0

##### Share on other sites

I didn't know this algorithm to generate uniform points on a sphere. 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.

0

##### Share on other sites
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?
0

## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account