Calculating 3D tangent space - c++

In order to use normal mapping in GLSL shaders, you need to know the normal, tangent and bitangent vectors of each vertex. RenderMonkey makes this easy by providing it's own predefined variables (rm_tangent and rm_binormal) for this. I am trying to add this functionality to my own 3d engine. Apparently it is possible to calculate the tangent and bi tangent of each vertex in a triangle using each vertex's xyz coordinates, uv texture coordinates and normal vector. After some searching I devised this function to calculate the tangent and bitangent for each vertex in my triangle structure.
void CalculateTangentSpace(void) {
float x1 = m_vertices[1]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0);
float x2 = m_vertices[2]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0);
float y1 = m_vertices[1]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1);
float y2 = m_vertices[2]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1);
float z1 = m_vertices[1]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2);
float z2 = m_vertices[2]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2);
float u1 = m_vertices[1]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0);
float u2 = m_vertices[2]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0);
float v1 = m_vertices[1]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1);
float v2 = m_vertices[2]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1);
float r = 1.0f/(u1 * v2 - u2 * v1);
Vec3<float> udir((v2 * x1 - v1 * x2) * r, (v2 * y1 - v1 * y2) * r, (v2 * z1 - v1 * z2) * r);
Vec3<float> vdir((u1 * x2 - u2 * x1) * r, (u1 * y2 - u2 * y1) * r, (u1 * z2 - u2 * z1) * r);
Vec3<float> tangent[3];
Vec3<float> tempNormal;
tempNormal = *m_vertices[0]->m_normal;
tangent[0]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
m_vertices[0]->m_tangent=&(tangent[0].Normalize());
m_vertices[0]->m_bitangent=Vec3Cross(m_vertices[0]->m_normal, m_vertices[0]->m_tangent);
tempNormal = *m_vertices[1]->m_normal;
tangent[1]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
m_vertices[1]->m_tangent=&(tangent[1].Normalize());
m_vertices[1]->m_bitangent=Vec3Cross(m_vertices[1]->m_normal, m_vertices[1]->m_tangent);
tempNormal = *m_vertices[2]->m_normal;
tangent[2]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
m_vertices[2]->m_tangent=&(tangent[2].Normalize());
m_vertices[2]->m_bitangent=Vec3Cross(m_vertices[2]->m_normal, m_vertices[2]->m_tangent);
}
When I use this function and send the calculated values to my shader, the models look almost like they do in RenderMonkey but they flicker in a very strange way. I traced the problem to the tangent and bitangent I am sending OpenGL. This leads me to suspect that my code is doing something wrong. Can anyone see any problems or have any suggestions for other methods to try?
I should also point out that the above code is very hacky and I have very little understanding of the math behind what is going on.

Found the solution. Much simpler (but still a little hacky) code:
void CalculateTangentSpace(void) {
float x1 = m_vertices[1]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0);
float y1 = m_vertices[1]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1);
float z1 = m_vertices[1]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2);
float u1 = m_vertices[1]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0);
Vec3<float> tangent(x1/u1, y1/u1, z1/u1);
tangent = tangent.Normalize();
m_vertices[0]->m_tangent = new Vec3<float>(tangent);
m_vertices[1]->m_tangent = new Vec3<float>(tangent);
m_vertices[2]->m_tangent = new Vec3<float>(tangent);
m_vertices[0]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[0]->m_normal, m_vertices[0]->m_tangent)->Normalize());
m_vertices[1]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[1]->m_normal, m_vertices[1]->m_tangent)->Normalize());
m_vertices[2]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[2]->m_normal, m_vertices[2]->m_tangent)->Normalize());
}

You will get zero division in your 'r' calculation for certain values of u1, u2, v1, and v2 resulting in unknown behavior for 'r'. You should guard against this. Figure out what 'r' should be if the denominator is zero, and that MIGHT fix your problem. I too have little understanding of the math behind this.
Suggested implementation that sets r = 0, if denominator is zero:
#include <cmath>
...
static float PRECISION = 0.000001f;
...
float denominator = (u1 * v2 - u2 * v1);
float r = 0.f;
if(fabs(denominator) > PRECISION) {
r = 1.0f/denominator;
}
...

Related

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.

Converting a 3x3 matrix to Euler/Tait Bryan angles (pitch yaw roll)

I have the Razer Hydra SDK here, and I want to transform the rotation matrix I get from the hardware, into pitch, yaw and roll.
The documentation states:
rot_mat - A 3x3 matrix describing the rotation of the controller.
My code is currently:
roll = atan2(rot_mat[2][0], rot_mat[2][1]);
pitch = acos(rot_mat[2][2]);
yaw = -atan2(rot_mat[0][2], rot_mat[1][2]);
Yet this seems to give me wrong results.
Would somebody know how I can easily translate this, and what I am doing wrong?
You can calculate pitch, roll and yaw like this.
Based on that:
#include <array>
#include <limits>
typedef std::array<float, 3> float3;
typedef std::array<float3, 3> float3x3;
const float PI = 3.14159265358979323846264f;
bool closeEnough(const float& a, const float& b, const float& epsilon = std::numeric_limits<float>::epsilon()) {
return (epsilon > std::abs(a - b));
}
float3 eulerAngles(const float3x3& R) {
//check for gimbal lock
if (closeEnough(R[0][2], -1.0f)) {
float x = 0; //gimbal lock, value of x doesn't matter
float y = PI / 2;
float z = x + atan2(R[1][0], R[2][0]);
return { x, y, z };
} else if (closeEnough(R[0][2], 1.0f)) {
float x = 0;
float y = -PI / 2;
float z = -x + atan2(-R[1][0], -R[2][0]);
return { x, y, z };
} else { //two solutions exist
float x1 = -asin(R[0][2]);
float x2 = PI - x1;
float y1 = atan2(R[1][2] / cos(x1), R[2][2] / cos(x1));
float y2 = atan2(R[1][2] / cos(x2), R[2][2] / cos(x2));
float z1 = atan2(R[0][1] / cos(x1), R[0][0] / cos(x1));
float z2 = atan2(R[0][1] / cos(x2), R[0][0] / cos(x2));
//choose one solution to return
//for example the "shortest" rotation
if ((std::abs(x1) + std::abs(y1) + std::abs(z1)) <= (std::abs(x2) + std::abs(y2) + std::abs(z2))) {
return { x1, y1, z1 };
} else {
return { x2, y2, z2 };
}
}
}
If you still get wrong angles with this, you may be using a row-major matrix as opposed to column-major, or vice versa - in that case you'll need to flip all R[i][j] instances to R[j][i].
Depending on the coordinate system used (left handed, right handed) x,y,z may not correspond to the same axes, but once you start getting the right numbers, figuring out which axis is which should be easy :)
Alternatively, to convert from a Quaternion to euler angles like shown here:
float3 eulerAngles(float q0, float q1, float q2, float q3)
{
return
{
atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2)),
asin( 2 * (q0*q2 - q3*q1)),
atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3))
};
}
This is the an formula that will do, keep in mind that the higher the precision the more variables in the rotation matrix are important:
roll = atan2(rot_mat[2][1], rot_mat[2][2]);
pitch = asin(rot_mat[2][0]);
yaw = -atan2(rot_mat[1][0], rot_mat[0][0]);
http://nghiaho.com/?page_id=846
This is also used in the point cloud library, function : pcl::getEulerAngles

Finding the distance between 2 3D points

I'm running into a problem where my square of X is always becoming infinite leading to the resulting distance also being infinite, however I can't see anything wrong with my own maths:
// Claculate distance
xSqr = (x1 - x2) * (x1 - x2);
ySqr = (y1 - y2) * (y1 - y2);
zSqr = (z1 - z2) * (z1 - z2);
double mySqr = xSqr + ySqr + zSqr;
double myDistance = sqrt(mySqr);
When I run my program I get user input for each of the co-ordinates and then display the distance after I have run the calulation.
If your inputs are single-precision float, then you should be fine if you force double-precision arithmetic:
xSqr = double(x1 - x2) * (x1 - x2);
// ^^^^^^
If the inputs are already double-precision, and you don't have a larger floating-point type available, then you'll need to rearrange the Euclidean distance calculation to avoid overflow:
r = sqrt(x^2 + y^2 + z^2)
= abs(x) * sqrt(1 + (y/x)^2 + (z/x)^2)
where x is the largest of the three coordinate distances.
In code, that might look something like:
double d[] = {abs(x1-x2), abs(y1-y2), abs(z1-z2)};
if (d[0] < d[1]) swap(d[0],d[1]);
if (d[0] < d[2]) swap(d[0],d[2]);
double distance = d[0] * sqrt(1.0 + d[1]/d[0] + d[2]/d[0]);
or alternatively, use hypot, which uses similar techniques to avoid overflow:
double distance = hypot(hypot(x1-x2,y1-y2),z1-z2);
although this may not be available in pre-2011 C++ libraries.
Try this:
long double myDistance=sqrt(pow(x1-x2,2.0)+pow(y1-y2,2.0)+pow(z1-z2,2.0));
I figured out what was going on, I had copied and pasted the code for setting x1, y1 and z1 and forgot to change it to x2 y2 and z2, It's always the silliest of things with me :P thanks for the help anyway guys

Efficient way to get the angle between two vectors in a single plane?

If I know for a fact that the x and z values of the vectors will be identical,
therefore im only concerned in measuring the 'vertical' angle of from the differences in the y plane, is there a more efficient method to do this compared to computing the dot product?
My current code using the dot product method is as follows:
float a_mag = a.magnitude();
float b_mag = b.magnitude();
float ab_dot = a.dot(b);
float c = ab_dot / (a_mag * b_mag);
// clamp d to from going beyond +/- 1 as acos(+1/-1) results in infinity
if (c > 1.0f) {
c = 1.0;
} else if (c < -1.0) {
c = -1.0;
}
return acos(c);
I would love to be able to get rid of these square roots
Suppose that your two vectors live at u = (x, y1, z) and v = (x, y2, z), and you're interested in the planar angle between the two along the plane spanned by the two vectors. You'd have to compute the dot product and the magnitude, but you can save a few operations:
u.v = x.x + y1.y2 + z.z
u^2 = x.x + y1.y1 + z.z
v^2 = x.x + y2.y2 + z.z
So we should precompute:
float xz = x*x + z*z, y11 = y1*y1, y12 = y1*y2, y22 = y2*y2;
float cosangle = (xz + y12) / sqrt((xz + y11) * (xz + y22));
float angle = acos(cosangle);
If the values of x and z are unchanged, then the calculation is very easy: just use basic trigonometry.
Let the points be (x, y1, z) and (x, y2, z). You can find out the angle a vector makes with the ZX-plane. Let the angles be t1 and t2 respectively. Then:
w = sqrt(x^2 + z^2)
tan(t1) = y1 / w
So t1 = atan(y1 / w)
Similarly t2 = atan(y2 / w)
The angle is (t2 - t1)
There's one pitfall: When both x and z are zero, the tans are undefined... but such a trivial case can easily be handled separately.
Unfortunately, there seems to be no way to avoid the square root.

Rotate a vector about another vector

I am writing a 3d vector class for OpenGL. How do I rotate a vector v1 about another vector v2 by an angle A?
You may find quaternions to be a more elegant and efficient solution.
After seeing this answer bumped recently, I though I'd provide a more robust answer. One that can be used without necessarily understanding the full mathematical implications of quaternions. I'm going to assume (given the C++ tag) that you have something like a Vector3 class with 'obvious' functions like inner, cross, and *= scalar operators, etc...
#include <cfloat>
#include <cmath>
...
void make_quat (float quat[4], const Vector3 & v2, float angle)
{
// BTW: there's no reason you can't use 'doubles' for angle, etc.
// there's not much point in applying a rotation outside of [-PI, +PI];
// as that covers the practical 2.PI range.
// any time graphics / floating point overlap, we have to think hard
// about degenerate cases that can arise quite naturally (think of
// pathological cancellation errors that are *possible* in seemingly
// benign operations like inner products - and other running sums).
Vector3 axis (v2);
float rl = sqrt(inner(axis, axis));
if (rl < FLT_EPSILON) // we'll handle this as no rotation:
{
quat[0] = 0.0, quat[1] = 0.0, quat[2] = 0.0, quat[3] = 1.0;
return; // the 'identity' unit quaternion.
}
float ca = cos(angle);
// we know a maths library is never going to yield a value outside
// of [-1.0, +1.0] right? Well, maybe we're using something else -
// like an approximating polynomial, or a faster hack that's a little
// rough 'around the edge' cases? let's *ensure* a clamped range:
ca = (ca < -1.0f) ? -1.0f : ((ca > +1.0f) ? +1.0f : ca);
// now we find cos / sin of a half-angle. we can use a faster identity
// for this, secure in the knowledge that 'sqrt' will be valid....
float cq = sqrt((1.0f + ca) / 2.0f); // cos(acos(ca) / 2.0);
float sq = sqrt((1.0f - ca) / 2.0f); // sin(acos(ca) / 2.0);
axis *= sq / rl; // i.e., scaling each element, and finally:
quat[0] = axis[0], quat[1] = axis[1], quat[2] = axis[2], quat[3] = cq;
}
Thus float quat[4] holds a unit quaternion that represents the axis and angle of rotation, given the original arguments (, v2, A).
Here's a routine for quaternion multiplication. SSE/SIMD can probably speed this up, but complicated transform & lighting are typically GPU-driven in most scenarios. If you remember complex number multiplication as a little weird, quaternion multiplication is more so. Complex number multiplication is a commutative operation: a*b = b*a. Quaternions don't even preserve this property, i.e., q*p != p*q :
static inline void
qmul (float r[4], const float q[4], const float p[4])
{
// quaternion multiplication: r = q * p
float w0 = q[3], w1 = p[3];
float x0 = q[0], x1 = p[0];
float y0 = q[1], y1 = p[1];
float z0 = q[2], z1 = p[2];
r[3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
r[0] = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1;
r[1] = w0 * y1 + y0 * w1 + z0 * x1 - x0 * z1;
r[2] = w0 * z1 + z0 * w1 + x0 * y1 - y0 * x1;
}
Finally, rotating a 3D 'vector' v (or if you prefer, the 'point' v that the question has named v1, represented as a vector), using the quaternion: float q[4] has a somewhat strange formula: v' = q * v * conjugate(q). Quaternions have conjugates, similar to complex numbers. Here's the routine:
static inline void
qrot (float v[3], const float q[4])
{
// 3D vector rotation: v = q * v * conj(q)
float r[4], p[4];
r[0] = + v[0], r[1] = + v[1], r[2] = + v[2], r[3] = +0.0;
glView__qmul(r, q, r);
p[0] = - q[0], p[1] = - q[1], p[2] = - q[2], p[3] = q[3];
glView__qmul(r, r, p);
v[0] = r[0], v[1] = r[1], v[2] = r[2];
}
Putting it all together. Obviously you can make use of the static keyword where appropriate. Modern optimising compilers may ignore the inline hint depending on their own code generation heuristics. But let's just concentrate on correctness for now:
How do I rotate a vector v1 about another vector v2 by an angle A?
Assuming some sort of Vector3 class, and (A) in radians, we want the quaternion representing the rotation by the angle (A) about the axis v2, and we want to apply that quaternion rotation to v1 for the result:
float q[4]; // we want to find the unit quaternion for `v2` and `A`...
make_quat(q, v2, A);
// what about `v1`? can we access elements with `operator [] (int)` (?)
// if so, let's assume the memory: `v1[0] .. v1[2]` is contiguous.
// you can figure out how you want to store and manage your Vector3 class.
qrot(& v1[0], q);
// `v1` has been rotated by `(A)` radians about the direction vector `v2` ...
Is this the sort of thing that folks would like to see expanded upon in the Beta Documentation site? I'm not altogether clear on its requirements, expected rigour, etc.
This may prove useful:
double c = cos(A);
double s = sin(A);
double C = 1.0 - c;
double Q[3][3];
Q[0][0] = v2[0] * v2[0] * C + c;
Q[0][1] = v2[1] * v2[0] * C + v2[2] * s;
Q[0][2] = v2[2] * v2[0] * C - v2[1] * s;
Q[1][0] = v2[1] * v2[0] * C - v2[2] * s;
Q[1][1] = v2[1] * v2[1] * C + c;
Q[1][2] = v2[2] * v2[1] * C + v2[0] * s;
Q[2][0] = v2[0] * v2[2] * C + v2[1] * s;
Q[2][1] = v2[2] * v2[1] * C - v2[0] * s;
Q[2][2] = v2[2] * v2[2] * C + c;
v1[0] = v1[0] * Q[0][0] + v1[0] * Q[0][1] + v1[0] * Q[0][2];
v1[1] = v1[1] * Q[1][0] + v1[1] * Q[1][1] + v1[1] * Q[1][2];
v1[2] = v1[2] * Q[2][0] + v1[2] * Q[2][1] + v1[2] * Q[2][2];
Use a 3D rotation matrix.
The easiest-to-understand way would be rotating the coordinate axis so that vector v2 aligns with the Z axis, then rotate by A around the Z axis, and rotate back so that the Z axis aligns with v2.
When you have written down the rotation matrices for the three operations, you'll probably notice that you apply three matrices after each other. To reach the same effect, you can multiply the three matrices.
I found this here:
http://steve.hollasch.net/cgindex/math/rotvec.html
let
[v] = [vx, vy, vz] the vector to be rotated.
[l] = [lx, ly, lz] the vector about rotation
| 1 0 0|
[i] = | 0 1 0| the identity matrix
| 0 0 1|
| 0 lz -ly |
[L] = | -lz 0 lx |
| ly -lx 0 |
d = sqrt(lx*lx + ly*ly + lz*lz)
a the angle of rotation
then
matrix operations gives:
[v] = [v]x{[i] + sin(a)/d*[L] + ((1 - cos(a))/(d*d)*([L]x[L]))}
I wrote my own Matrix3 class and Vector3Library that implemented this vector rotation. It works absolutely perfectly. I use it to avoid drawing models outside the field of view of the camera.
I suppose this is the "use a 3d rotation matrix" approach. I took a quick look at quaternions, but have never used them, so stuck to something I could wrap my head around.