Maximising precision in glsl - opengl

Related to this question.
I am having suffering due to lack of precision in the following function in glsl:
double LinearizeDepth(double depth)
{
double z = depth * 2.0 - 1.0; // back to NDC
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}
I want to threshold on a depth value like so
abs(linearised_depth - threshold_value) < epsilon and my code works as expected when epsilon >= 0.5. I want to reduce epsilon to ~0.1. Are there any tricks to help with this?

Related

Need help expanding particle system spread / divergence from 2 to 3 dimensions

I need help. I've been struggling with this for a week now and getting nowhere. I am building a 3D particle system mainly for learning and I am currently working on particle spread / divergence. In specific, introducing random direction to the particle direction so as to create something that looks more like a fountain as opposed to a solid stream.
I have been successful in getting this to work in one axis but no matter what I do, I cannot get it to work in 3 dimensions.
Here is what I am doing:
// Compute a random angle between -180 to +180 for velocity angle x, y and z. spreadAmount is a float from 0.0 to 1.0 to control degree of spread.
float velangrndx = spreadAmount * ((((double)(rand() % RAND_MAX) / (RAND_MAX)) - 0.5) * 360.0 * 3.14159265359 / 180.0);
float velangrndy = spreadAmount * ((((double)(rand() % RAND_MAX) / (RAND_MAX)) - 0.5) * 360.0 * 3.14159265359 / 180.0);
float velangrndz = spreadAmount * ((((double)(rand() % RAND_MAX) / (RAND_MAX)) - 0.5) * 360.0 * 3.14159265359 / 180.0);
// Compute Angles
float vsin_anglex_dir = -PF_SIN(velangrndx);
float vcos_anglex_dir = -PF_COS(velangrndx);
float vsin_angley_dir = -PF_SIN(velangrndy);
float vcos_angley_dir = -PF_COS(velangrndy);
float vsin_anglez_dir = -PF_SIN(velangrndz);
float vcos_anglez_dir = -PF_COS(velangrndz);
// Assign initial velocity to velocity x, y, z. vel is a float ranging from 0.0 - 0.1 specified by user. velx, vely, and velz are also floats.
velx = vel; vely = vel; velz = vel;
And finally, we get to the particle spread / divergence function below. If I use only the first X axis (comment out the Y and Z) it works as it should (see images), but if I use the Y and Z axis, it works totally incorrectly. px0, py0, and pz0 are temporary float variables so as to preserve the velocity variables.
// X Divergence
px0 = (velx * vsin_anglex_dir);
py0 = (velx * vcos_anglex_dir);
pz0 = velz;
velx = px0; vely = py0; velz = pz0;
// Y Divergence
py0 = (vely * vsin_angley_dir);
pz0 = (vely * vcos_angley_dir);
px0 = velx;
velx = px0; vely = py0; velz = pz0;
// Z Divergence
pz0 = (velz * vsin_anglez_dir);
px0 = (velz * vcos_anglez_dir);
py0 = vely;
velx = px0; vely = py0; velz = pz0;
The velx, vely, and velz are then used to calculate for particle screen position.
This is what the particle spread looks like at 25%, 75% and 100% for the X axis only (if I comment out the Y and Z code). This works as it should and in theory, if the rest of my code was working correctly, I should get this same result for the Y and Z axis. But I don't.
I could really use some help here. Any suggestions on what I am doing wrong and how to correctly expand the currently working spread function from 2 dimensions to 3?
Thanks,
-Richard
Likely it is because the values of velx, vely and velz are getting overwritten on subsequent calculations. See whether the below works the way you are expecting.
// X Divergence
float velxXD = (velx * vsin_anglex_dir);
float velyXD = (velx * vcos_anglex_dir);
float velzXD = velz;
// Y Divergence
float velxYD = velx;
float velyYD = (vely * vsin_angley_dir);
float velzYD = (vely * vcos_angley_dir);
// Z Divergence
float velxZD = (velz * vcos_anglez_dir);
float velyZD = vely;
float velzZD = (velz * vsin_anglez_dir);
velx=velxXD+velxYD+velxZD;
vely=velyXD+velyYD+velyZD;
velz=velzXD+velzYD+velzZD;

Perlin Noise algorithm does not seem to produce gradient noise

I am attempting to implement Perlin Noise in c++.
Firstly, the problem (I think) is that the output is not what I expect. Currently I simply use the generated Perlin Noise values in a greyscaled image, and this is the results I get:
However, from my understanding, it's supposed to look more along the lines of:
That is, the noise I am producing currently seems to be more along the lines of "standard" irregular noise.
This is the Perlin Noise Algorithm I have implemented so far:
float perlinNoise2D(float x, float y)
{
// Find grid cell coordinates
int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
int x1 = x0 + 1;
int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
int y1 = y0 + 1;
float s = calculateInfluence(x0, y0, x, y);
float t = calculateInfluence(x1, y0, x, y);
float u = calculateInfluence(x0, y1, x, y);
float v = calculateInfluence(x1, y1, x, y);
// Local position in the grid cell
float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));
float a = s + localPosX * (t - s);
float b = u + localPosX * (v - u);
return lerp(a, b, localPosY);
}
The function calculateInfluence has the job of generating the random gradient vector and distance vector for one of the corner points of the current grid cell and returning the dot product of these. It is implemented as:
float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
// Calculate gradient vector
float gradientXComponent = dist(rdEngine);
float gradientYComponent = dist(rdEngine);
// Normalize gradient vector
float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
gradientXComponent = gradientXComponent / magnitude;
gradientYComponent = gradientYComponent / magnitude;
magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));
// Calculate distance vectors
float dx = x - (float)xGrid;
float dy = y - (float)yGrid;
// Compute dot product
return (dx * gradientXComponent + dy * gradientYComponent);
}
Here, dist is a random number generator from C++11:
std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);
And lerp is simply implemented as:
float lerp(float v0, float v1, float t)
{
return ( 1.0f - t ) * v0 + t * v1;
}
To implement the algorithm, I primarily made use of the following two resources:
Perlin Noise FAQ
Perlin Noise Pseudo Code
It's difficult for me to pinpoint exactly where I seem to be messing up. It could be that I am generating the gradient vectors incorrectly, as I'm not quite sure what type of distribution they should have. I have tried with a uniform distribution, however this seemed to generate repeating patterns in the texture!
Likewise, it could be that I am averaging the influence values incorrectly. It has been a bit difficult to discern exactly how it should be done from from the Perlin Noise FAQ article.
Does anyone have any hints as to what might be wrong with the code? :)
It seems like you are only generating a single octave of Perlin Noise. To get a result like the one shown, you need to generate multiple octaves and add them together. In a series of octaves, each octave should have a grid cell size double that of the last.
To generate multi-octave noise, use something similar to this:
float multiOctavePerlinNoise2D(float x, float y, int octaves)
{
float v = 0.0f;
float scale = 1.0f;
float weight = 1.0f;
float weightTotal = 0.0f;
for(int i = 0; i < octaves; i++)
{
v += perlinNoise2D(x * scale, y * scale) * weight;
weightTotal += weight;
// "ever-increasing frequencies and ever-decreasing amplitudes"
// (or conversely decreasing freqs and increasing amplitudes)
scale *= 0.5f;
weight *= 2.0f;
}
return v / weightTotal;
}
For extra randomness you could use a differently seeded random generator for each octave. Also, the weights given to each octave can be varied to adjust the aesthetic quality of the noise. If the weight variable is not adjusted each iteration, then the example above is "pink noise" (each doubling of frequency carries the same weight).
Also, you need to use a random number generator that returns the same value each time for a given xGrid, yGrid pair.

How can I get resistance from temperature?

I have a value in temperature and I want to get a resistance value from this temperature.
I use Steinhart-Hart method but this equation always returns 7,39 regardless of temperatures and coefficients.
My second implementation based on steinhart method (see below) doesn't work with negative coefficients.
Do you know what's the problem with my code ?
double WSensor::temperatureToResistance(double _temp)
{
double Temp = _temp + 273.15;
double X = ((this->therm->getA() - (1 / Temp)) / this->therm->getC());
double Y = this->therm->getB() / this->therm->getC();
double argExpo = pow(-(X / 2) + pow((X*X) / 4 + (Y*Y*Y) / 27, 1.0 / 2.), 1.0 / 3.0) - pow((-(X / 2) - pow((X*X) / 4 + (Y*Y*Y) / 27, 1.0 / 2.0)) * (-1), 1.0 / 3.0);
return exp(argExpo);
}
After 3 days of work, I know why this equation does not work on arduino : overflowing.
Some parts of the equation create float too large for this board model (Arduino Uno).
One of the solutions is to rephrase the equation to prevent bigger results. But this solution takes too much time and need good mathematical skills, that's why I decided to move the equation in an external API.

sum of weights should be exactly 1.0 no matter on which platform it is

I have such a function that calculates weights according to Gaussian distribution:
const float dx = 1.0f / static_cast<float>(points - 1);
const float sigma = 1.0f / 3.0f;
const float norm = 1.0f / (sqrtf(2.0f * static_cast<float>(M_PI)) * sigma);
const float divsigma2 = 0.5f / (sigma * sigma);
m_weights[0] = 1.0f;
for (int i = 1; i < points; i++)
{
float x = static_cast<float>(i)* dx;
m_weights[i] = norm * expf(-x * x * divsigma2) * dx;
m_weights[0] -= 2.0f * m_weights[i];
}
In all the calc above the number does not matter. The only thing matters is that m_weights[0] = 1.0f; and each time I calculate m_weights[i] I subtract it twice from m_weights[0] like this:
m_weights[0] -= 2.0f * m_weights[i];
to ensure that w[0] + 2 * w[i] (1..N) will sum to exactly 1.0f. But it does not. This assert fails:
float wSum = 0.0f;
for (size_t i = 0; i < m_weights.size(); ++i)
{
float w = m_weights[i];
if (i == 0) {
wSum += w;
} else {
wSum += (w + w);
}
}
assert(wSum == 1.0 && "Weights sum is not 1.");
How can I ensure the sum to be 1.0f on all platforms?
You can't. Floating point isn't like that. Even adding the same values can produce different results according to the cpu used.
All you can do is define some accuracy value and ensure that you end up with 1.0 +/- that value.
See: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Because the precision of float is only 23 bits (see e.g. https://en.wikipedia.org/wiki/Single-precision_floating-point_format ), rounding error quickly accumulates therefore even if the rest of code is correct, your sum becomes something like 1.0000001 or 0.9999999 (have you watched it in the debugger or tried to print it to console, by the way?). To improve precision you can replace float with double, but still the sum will not be exactly 1.0: the error will just be smaller, something like 1e-16 instead of 1e-7.
The second thing to do is to replace strict comparison to 1.0 with a range comparison, like:
assert(fabs(wSum - 1.0) <= 1e-13 && "Weights sum is not 1.");
Here 1e-13 is the epsilon within which you consider two floating-point numbers equal. If you choose to go with float (not double), you may need epsilon like 1e-6 .
Depending on how large your weights are and how many points there are, accumulated error can become larger than that epsilon. In that case you would need special algorithms for keeping the precision higher, such as sorting the numbers by their absolute values prior to summing them up starting with the smallest numbers.
How can I ensure the sum to be 1.0f on all platforms?
As the other answers (and comments) have stated, you can't achieve this, due to the inexactness of floating point calculations.
One solution is that, instead of using double, use a fixed point or multi-precision library such as GMP, Boost Multiprecision Library, or one of the many others out there.

UV mapping for a dome?

I am trying to understand how can I change UV mapping of a dome, I need a different texture map projection than this one coded below:
protected final void createDome(final float radius) {
int lats=16;
int longs=16;
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textures2x4[0].getTextureID());
int i, j;
int halfLats = lats / 2;
for(i = 0; i <= halfLats; i++)
{
double lat0 = MathUtils.PI * (-0.5 + (double) (i - 1) / lats);
double z0 = Math.sin(lat0)* radius;
double zr0 = Math.cos(lat0)* radius;
double lat1 = MathUtils.PI * (-0.5 + (double) i / lats);
double z1 = Math.sin(lat1)* radius;
double zr1 = Math.cos(lat1)* radius;
GL11.glBegin(GL11.GL_QUAD_STRIP);
for(j = 0; j <= longs; j++)
{
double lng = 2 * MathUtils.PI * (double) (j - 1) / longs;
double x = Math.cos(lng);
double y = Math.sin(lng);
double s1, s2, t;
s1 = ((double) i) / halfLats;
s2 = ((double) i + 1) / halfLats;
t = ((double) j) / longs;
// HERE: I don't know how to calculate the UV mapping
GL11.glTexCoord2d(s1, t);
GL11.glNormal3d(x * zr0, y * zr0, z0);
GL11.glVertex3d(x * zr0, y * zr0, z0);
GL11.glTexCoord2d(s2, t);
GL11.glNormal3d(x * zr1, y * zr1, z1);
GL11.glVertex3d(x * zr1, y * zr1, z1);
}
GL11.glEnd();
}
}
I linked the output image and the original map. Pratically I need a UV mapping which places the Artic at the zenith/top of the dome, and the Antartic streched on the bottom side of the dome... the Artic/Antartic map is only used to figure out what I mean, my need it's not to fit a globe emisphere
Output image http://img831.imageshack.us/img831/3481/lwjgl.png
Source map http://img203.imageshack.us/img203/4930/earthc.png
Take a look at this function calls (disclaimer: untested - I haven't used LWJGL, but the concept should be identical):
GL11.glMatrixMode(GL11.GL_TEXTURE);
GL11.glRotate(90, 0, 0, 1); // (1) Here you transform texture space
GL11.glMatrixMode(GL11.GL_MODELVIEW);
// and so on
Basically, you need to rotate texture on object. And that's the way you do it - transform texture projection matrix. The line (1) rotates texture 90 degrees along Z axis (perpendicular to texture plane). It's Z axis, because the last argument is 1. Last three arguments denote X, Y and Z respectively (I'll leave the whole explanation for later if you're interested).
The best You can do is to grasp all the basic stuff (projection, texture space, normal vectors, triangulation, continuity, particle systems and a lot more) is to download some trial version of a 3d package and play with it. I learned a lot just out of playing with 3D Studio Max (trial version available, and many more for free). If you have some free time and will to learn something new I strongly advise to look into it. In the end, if You're really interested in 3D graphics You'll end up using one any way - be it 3d package or game engine level editor.
EDIT: After more reading I recognized my own code... Basically you could only swap some of the coordinates to reflect symmetrically along diagonal. You might end up upside down, but that can also be fixed with additional tweaking (or transforming the view axis). Here is my untested guess:
// tweaked to get pole right
s1 = ((double) j) / longs;
s2 = ((double) j + 1) / longs;
t = ((double) i) / halfLats;
Try swapping s1 with s2 if it's not right.