// 45 degrees in radians
const float Angle = 0.785398;
float rotateAround(float2 p, float2 pivot, float angle) {
float s = sin(angle);
float c = cos(angle);
// Translate point back to origin
p.x -= pivot.x;
p.y -= pivot.y;
// Rotate point
float xnew = p.x * c - p.y * s;
float ynew = p.x * s + p.y * c;
// Translate point back:
p.x = xnew + pivot.x;
p.y = ynew + pivot.y;
return p;
}
My angle is always 45 degrees.
If I change s or c to actual value of 0.70710666564 (which is a sin / cos value of 45 degrees as well radian value of 0.785398).
Function behaves completely differently.
Am I going crazy or what am I missing?
Does Unity does some voodoo magic under the hood while calculating sin / cos?
Not Unity, but GPUs certainly do. On the GPU, sin and cos are generally calculated using approximations. Nvidia’s CG documentation has a reference example of how it might actually be implemented on the GPU, but the actual implementation is hardware specific so every family of GPUs may behave slightly differently. For example mobile GPUs are notorious for having quite poor approximations compared to desktop hardware (owing to choosing efficiency over accuracy). However if you use a hardcoded value, then the shader compiler is going to do as much of those calculations for you beforehand as it can, and the implementation there is going to be compiler specific.
So yes, it’s sort of expected that fiddling with how you do this function changes the output a little. It shouldn’t be super drastic though.
That all said, 0.70710666564 isn’t sin(45°), 0.70710678118654752440084436210485 is. While a 32 bit float can’t perfectly represent that number, the number it would store, 0.707106769084930419921875, is still a little more accurate than the number you listed (32 bit floats are only going to be accurate for roughly the first 7 numbers of any value). But really this is kind of pedantic, as just using 0.707107 is likely going to be accurate enough most of the time.
Yeah, I’ve compiled the code and checked to what value it is clamped and it is about 7 digits equivalent of 0.707107.
Problem I get is that my code works completely differently when using sin() and cos() vs a const value.
I’ve got no idea why. I guess I need to compare two sets of instructions side by side to figure it out.
To illustrate, this is with sin() / cos():
And this one with constant 0.707107:
Perhaps accuracy does indeed mater.
I wonder if I can somehow prevent compiler from rounding constant value to test if that is the case.
at start I will suggest to use 2d matrix, it’s suitable for any rotation angle too (if you want to change it in future), you can precompute it coeffs and then use whenewer you want
float rotateAround(float2 p, float2 pivot)
{
float angleInRadians = radians(45);
float s = sin(angleInRadians);
float c = cos(angleInRadians);
// Translate point back to origin
p.x -= pivot.x;
p.y -= pivot.y;
// Rotate point
float xnew = p.x * c - p.y * s;
float ynew = p.x * s + p.y * c;
// Translate point back:
p.x = xnew + pivot.x;
p.y = ynew + pivot.y;
return p;
}
With hlsl, you don’t have to worry too much about optimising things related to constant values by hand. If the compiler can tell it’s a constant, it will optimise it for you.
So it seems like the best optimization is to not doing the whole rotation at all.
I’ve removed it completely, no need to optimize sin / cos anymore.
I guess I need to check the whole compiler consts just for scientific reasons, but I’ll leave it be until I’ve got some time.