Tangent space to World Space (TBN Matrix) - c++

I'm building a renderer using rasterization and depth-buffering in the CPU and now I've included normals maps. Everything works like you can see in the next image:
The issue is that, even that it works, I don't understand WHY! The implementation is against what I think. This is the code to get the normal at each fragment:
const Vector3D TexturedMaterial::getNormal(const Triangle3D& triangle_world, const Vector2D& text_coords) const {
Vector3D tangent, bitangent;
calculateTangentSpace(tangent, bitangent, triangle_world);
// Gets the normal from a RGB Texture [0,1] and maps it to [-1, 1]
const Vector3D normal_tangent = (Vector3D) getTextureColor(m_texture_normal, m_texture_normal_width, m_texture_normal_height, text_coords);
const Vector3D normal_world = TangentToWorld(normal_tangent, tangent, bitangent, normal_tangent);
return normal_world;
}
void TexturedMaterial::calculateTangentSpace(Vector3D& tangent, Vector3D& bitangent, const Triangle3D& triangle_world) const {
const Vector3D q1 = triangle_world.v2.position - triangle_world.v1.position;
const Vector3D q2 = triangle_world.v3.position - triangle_world.v2.position;
const double s1 = triangle_world.v2.texture_coords.x - triangle_world.v1.texture_coords.x;
const double s2 = triangle_world.v3.texture_coords.x - triangle_world.v2.texture_coords.x;
const double t1 = triangle_world.v2.texture_coords.y - triangle_world.v1.texture_coords.y;
const double t2 = triangle_world.v3.texture_coords.y - triangle_world.v2.texture_coords.y;
tangent = t2 * q1 - t1 * q2;
bitangent = -s2 * q1 + s1 * q2;
tangent.normalize();
bitangent.normalize();
}
My confusion is here:
const Vector3D TexturedMaterial::TangentToWorld(const Vector3D& v, const Vector3D& tangent, const Vector3D& bitangent, const Vector3D& normal) const {
const int handness = -1; // Left coordinate system
// Vworld = Vtangent * TBN
Vector3D v_world = {
v.x * tangent.x + v.y * bitangent.x + v.z * normal.x,
v.x * tangent.y + v.y * bitangent.y + v.z * normal.y,
v.x * tangent.z + v.y * bitangent.z + v.z * normal.z,
};
// Vworld = Vtangent * TBN(-1) = V * TBN(T)
Vector3D v_world2 = {
v.x * tangent.x + v.y * tangent.y + v.z * tangent.z,
v.x * bitangent.x + v.y * bitangent.y + v.z * bitangent.z,
v.x * normal.x + v.y * normal.y + v.z * normal.z,
};
v_world2.normalize();
// return handness * v_world; --> DOES NOT WORK
return handness * v_world2; --> WORKS
}
Assuming that I'm working with row vectors:
V = (Vx, Vy, Vz)
[Tx Ty Tz]
TBN = [Bx By Bz]
[Nx Ny Nz]
[Tx Bx Nx]
TBN(-1) = [Ty By Ny] // Assume basis are orthogonal TBN(-1) = TBN(T)
[Tz Bz Nz]
Then, if T, B and N are the basis vectors of the TBN expressed in the world coordinate system the transformations should be:
Vworld = Vtangent * TBN
Vtangent = Vworld * TBN(-1)
But, in my code I am doing exactly the opposite. To transform the normal in tangent space to world space I am multiplying by the inverse of the TBN.
What I am missing or misunderstanding? Is the assumption that T, B and N are expressed in the world coordinate system wrong?
Thank you!

Your reasoning is correct - the second version is wrong. A more intuitive way to see that is analyzing what happens when the tangent space normal is (0, 0, 1). In this case, you obviously want to use the triangle normal, which is exactly what the first version should do.
However, you are feeding a wrong parameter:
const Vector3D normal_world = TangentToWorld(normal_tangent,
tangent, bitangent, normal_tangent);
The last parameter needs to be the triangle normal, not the normal you fetch from the texture.

Related

Quaternion rotation works fine with y/z rotation but gets messed up when I add x rotation

So I've been learning about quaternions recently and decided to make my own implementation. I tried to make it simple but I still can't pinpoint my error. x/y/z axis rotation works fine on it's own and y/z rotation work as well, but the second I add x axis to any of the others I get a strange stretching output. I'll attach the important code for the rotations below:(Be warned I'm quite new to cpp).
Here is how I describe a quaternion (as I understand since they are unit quaternions imaginary numbers aren't required):
struct Quaternion {
float w, x, y, z;
};
The multiplication rules of quaternions:
Quaternion operator* (Quaternion n, Quaternion p) {
Quaternion o;
// implements quaternion multiplication rules:
o.w = n.w * p.w - n.x * p.x - n.y * p.y - n.z * p.z;
o.x = n.w * p.x + n.x * p.w + n.y * p.z - n.z * p.y;
o.y = n.w * p.y - n.x * p.z + n.y * p.w + n.z * p.x;
o.z = n.w * p.z + n.x * p.y - n.y * p.x + n.z * p.w;
return o;
}
Generating the rotation quaternion to multiply the total rotation by:
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
n.w = cosf(w/2);
n.x = x * sinf(w/2);
n.y = y * sinf(w/2);
n.z = z * sinf(w/2);
return n;
}
And finally, the matrix calculations which turn the quaternion into an x/y/z position:
inline vector<float> quaternion_matrix(Quaternion total, vector<float> vec) {
float x = vec[0], y = vec[1], z = vec[2];
// implementation of 3x3 quaternion rotation matrix:
vec[0] = (1 - 2 * pow(total.y, 2) - 2 * pow(total.z, 2))*x + (2 * total.x * total.y - 2 * total.w * total.z)*y + (2 * total.x * total.z + 2 * total.w * total.y)*z;
vec[1] = (2 * total.x * total.y + 2 * total.w * total.z)*x + (1 - 2 * pow(total.x, 2) - 2 * pow(total.z, 2))*y + (2 * total.y * total.z + 2 * total.w * total.x)*z;
vec[2] = (2 * total.x * total.z - 2 * total.w * total.y)*x + (2 * total.y * total.z - 2 * total.w * total.x)*y + (1 - 2 * pow(total.x, 2) - 2 * pow(total.y, 2))*z;
return vec;
}
That's pretty much it (I also have a normalize function to deal with floating point errors), I initialize all objects quaternion to: w = 1, x = 0, y = 0, z = 0. I rotate a quaternion using an expression like this:
obj.rotation = rotate(angle, x-axis, y-axis, z-axis) * obj.rotation
where obj.rotation is the objects total quaternion rotation value.
I appreciate any help I can get on this issue, if anyone knows what's wrong or has also experienced this issue before. Thanks
EDIT: multiplying total by these quaternions output the expected rotation:
rotate(angle,1,0,0)
rotate(angle,0,1,0)
rotate(angle,0,0,1)
rotate(angle,0,1,1)
However, any rotations such as these make the model stretch oddly:
rotate(angle,1,1,0)
rotate(angle,1,0,1)
EDIT2: here is the normalize function I use to normalize the quaternions:
Quaternion normalize(Quaternion n, double tolerance) {
// adds all squares of quaternion values, if normalized, total will be 1:
double total = pow(n.w, 2) + pow(n.x, 2) + pow(n.y, 2) + pow(n.z, 2);
if (total > 1 + tolerance || total < 1 - tolerance) {
// normalizes value of quaternion if it exceeds a certain tolerance value:
n.w /= (float) sqrt(total);
n.x /= (float) sqrt(total);
n.y /= (float) sqrt(total);
n.z /= (float) sqrt(total);
}
return n;
}
To implement two rotations in sequence you need the quaternion product of the two elementary rotations. Each elementary rotation is specified by an axis and an angle. But in your code you did not make sure you have a unit vector (direction vector) for the axis.
Do the following modification
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
float f = 1/sqrtf(x*x+y*y+z*z)
n.w = cosf(w/2);
n.x = f * x * sinf(w/2);
n.y = f * y * sinf(w/2);
n.z = f * z * sinf(w/2);
return n;
}
and then use it as follows
Quaternion n = rotate(angle1,1,0,0) * rotate(angle2,0,1,0)
for the combined rotation of angle1 about the x-axis, and angle2 about the y-axis.
As pointed out in comments, you are not initializing your quaternions correctly.
The following quaternions are not rotations:
rotate(angle,0,1,1)
rotate(angle,1,1,0)
rotate(angle,1,0,1)
The reason is the axis is not normalized e.g., the vector (0,1,1) is not normalized. Also make sure your angles are in radians.

Calculating 3D Coordinate

I have recently been trying to calculate a 3D point out of a mouse position. So far I have this:
const D3DXMATRIX* pmatProj = g_Camera.GetProjMatrix();
POINT ptCursor;
GetCursorPos( &ptCursor );
ScreenToClient( DXUTGetHWND(), &ptCursor );
// Compute the vector of the pick ray in screen space
D3DXVECTOR3 v;
v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;
v.z = 1.0f;
// Get the inverse view matrix
const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
D3DXMATRIX mWorldView = matWorld * matView;
D3DXMATRIX m;
D3DXMatrixInverse( &m, NULL, &mWorldView );
// Transform the screen space pick ray into 3D space
vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
vPickRayOrig.x = m._41;
vPickRayOrig.y = m._42;
vPickRayOrig.z = m._43;
However, as my mathematical skills are lacklustre, I am unsure how to utilise the direction and origin to produce a position. What calculations/formulas do I need to perform to produce the desired results?
It's just like a * x + b, except three times.
For any distance d (positive or negative) from vPickRayOrig:
newPos.x = d * vPickRayDir.x + vPickRayOrig.x;
newPos.y = d * vPickRayDir.y + vPickRayOrig.y;
newPos.z = d * vPickRayDir.z + vPickRayOrig.z;

Making a 3D graphics engine, my Translation matrix doesn't work for positions equal to 0

Hi I'm making a 3D graphics engine for an assignment that is due later tonight, it's going smoothly at the moment except I'm loading a cube model from an .obj file, the positions start at 0.
My transformation matrix works for numbers that don't = 0. I mean if X = 0 and I try to translate it by 10 on the X Axis, it returns 0.
Matrix * Vector:
Vec4 Mat4::operator*(const Vec4& v) const
{
Vec4 tmp(0, 0, 0, 0, 255, 255, 255, 255);
tmp.x = (this->data[0] * v.x) + (this->data[4] * v.y) + (this->data[8] * v.z) + (this->data[12] * v.w);
tmp.y = (this->data[1] * v.x) + (this->data[5] * v.y) + (this->data[9] * v.z) + (this->data[13] * v.w);
tmp.z = (this->data[2] * v.x) + (this->data[6] * v.y) + (this->data[10] * v.z) + (this->data[14] * v.w);
tmp.w = (this->data[3] * v.x) + (this->data[7] * v.y) + (this->data[11] * v.z) + (this->data[15] * v.w);
return tmp;
}
Translate Matrix:
Mat4 Mat4::translate(float x, float y, float z)
{
Mat4 tmp;
tmp.data[12] = x;
tmp.data[13] = y;
tmp.data[14] = z;
return tmp;
}
A Mat4 class by default is an identity matrix.
It is too late now, but... it might be helpful to know the following:
A vector strictly equal to 0.0 (e.g. <0,0,0,0>) cannot be translated using matrix multiplication and technically should not be considered a position in this context. In fact, such a vector is not even representative of a direction because it has 0 length. It is simply zero; there are not a whole lot of uses for a vector that cannot be rotated or translated.
You can rotate vectors with 0.0 for the W coordinate, but the value 0.0 for W prevents translation.
Generally you want a W coordinate of 1.0 for spatial (e.g. position) vectors and 0.0 for directional (e.g. normal).
If you want to understand this better, you need to consider how your 4x4 matrix is setup. The first 3 rows or columns (depending on which convention you use) store rotation, and the 4th stores translation.
Consider how translation is applied when you multiply your matrix and vector:
x = ... + (this->data[12] * v.w);
y = ... + (this->data[13] * v.w);
z = ... + (this->data[14] * v.w);
w = ... + (this->data[15] * v.w);
If v.w is 0.0, then translation evaluates to 0.0 for all coordinates.

Faster quaternion vector multiplication doesn't work

I need a faster quaternion-vector multiplication routine for my math library. Right now I'm using the canonical v' = qv(q^-1), which produces the same result as multiplying the vector by a matrix made from the quaternion, so I'm confident in it's correctness.
So far I've implemented 3 alternative "faster" methods:
#1, I have no idea where I got this one from:
v' = (q.xyz * 2 * dot(q.xyz, v)) + (v * (q.w*q.w - dot(q.xyz, q.zyx))) + (cross(q.xyz, v) * q.w * w)
Implemented as:
vec3 rotateVector(const quat& q, const vec3& v)
{
vec3 u(q.x, q.y, q.z);
float s = q.w;
return vec3(u * 2.0f * vec3::dot(u, v))
+ (v * (s*s - vec3::dot(u, u)))
+ (vec3::cross(u, v) * s * 2.0f);
}
#2, courtesy of this fine blog
t = 2 * cross(q.xyz, v);
v' = v + q.w * t + cross(q.xyz, t);
Implemented as:
__m128 rotateVector(__m128 q, __m128 v)
{
__m128 temp = _mm_mul_ps(vec4::cross(q, v), _mm_set1_ps(2.0f));
return _mm_add_ps(
_mm_add_ps(v, _mm_mul_ps(_mm_shuffle_ps(q, q, _MM_SHUFFLE(3, 3, 3, 3)), temp)),
vec4::cross(q, temp));
}
And #3, from numerous sources,
v' = v + 2.0 * cross(cross(v, q.xyz) + q.w * v, q.xyz);
implemented as:
__m128 rotateVector(__m128 q, __m128 v)
{
//return v + 2.0 * cross(cross(v, q.xyz) + q.w * v, q.xyz);
return _mm_add_ps(v,
_mm_mul_ps(_mm_set1_ps(2.0f),
vec4::cross(
_mm_add_ps(
_mm_mul_ps(_mm_shuffle_ps(q, q, _MM_SHUFFLE(3, 3, 3, 3)), v),
vec4::cross(v, q)),
q)));
}
All 3 of these produce incorrect results. I have, however, noticed some interesting patterns. First of all, #1 and #2 produce the same result. #3 produces the same result that I get from multiplying the vector by a derived matrix if said matrix is transposed (I discovered this by accident, previously my quat-to-matrix code assumed row-major matrices, which was incorrect).
The data storage of my quaternions are defined as:
union
{
__m128 data;
struct { float x, y, z, w; };
float f[4];
};
Are my implementations flawed, or am I missing something here?
Main issue, if you want to rotate the 3d vector by quaternion, you require to calculate all 9 scalars of rotation matrix. In your examples, calculation of rotation matrix is IMPLICIT. The order of calculation can be not optimal.
If you generate 3x3 matrix from quaternion and multiply vector, you should have same number of arithmetic operations (#see code at bottom).
What i recommend.
Try to generate matrix 3x3 and multiply your vector, measure the speed and compare with previous.
Analyze the explicit solution, and try to optimize for custom architecture.
try to implement alternative quaternion multiplication, and derived multiplication from equation q*v*q'.
//============
alternative multiplication pseudocode
/**
alternative way of quaternion multiplication,
can speedup multiplication for some systems (PDA for example)
http://mathforum.org/library/drmath/view/51464.html
http://www.lboro.ac.uk/departments/ma/gallery/quat/src/quat.ps
in provided code by url's many bugs, have to be rewriten.
*/
inline xxquaternion mul_alt( const xxquaternion& q) const {
float t0 = (x-y)*(q.y-q.x);
float t1 = (w+z)*(q.w+q.z);
float t2 = (w-z)*(q.y+q.x);
float t3 = (x+y)*(q.w-q.z);
float t4 = (x-z)*(q.z-q.y);
float t5 = (x+z)*(q.z+q.y);
float t6 = (w+y)*(q.w-q.x);
float t7 = (w-y)*(q.w+q.x);
float t8 = t5 + t6 + t7;
float t9 = (t4 + t8)*0.5;
return xxquaternion ( t3+t9-t6,
t2+t9-t7,
t1+t9-t8,
t0+t9-t5 );
// 9 multiplications 27 addidtions 8 variables
// but of couse we can clean 4 variables
/*
float r = w, i = z, j = y, k =x;
float br = q.w, bi = q.z, bj = q.y, bk =q.x;
float t0 = (k-j)*(bj-bk);
float t1 = (r+i)*(br+bi);
float t2 = (r-i)*(bj+bk);
float t3 = (k+j)*(br-bi);
float t4 = (k-i)*(bi-bj);
float t5 = (k+i)*(bi+bj);
float t6 = (r+j)*(br-bk);
float t7 = (r-j)*(br+bk);
float t8 = t5 + t6 + t7;
float t9 = (t4 + t8)*0.5;
float rr = t0+t9-t5;
float ri = t1+t9-t8;
float rj = t2+t9-t7;
float rk = t3+t9-t6;
return xxquaternion ( rk, rj, ri, rr );
*/
}
//============
explicit vector rotation variants
/**
rotate vector by quaternion
*/
inline vector3 rotate(const vector3& v)const{
xxquaternion q(v.x * w + v.z * y - v.y * z,
v.y * w + v.x * z - v.z * x,
v.z * w + v.y * x - v.x * y,
v.x * x + v.y * y + v.z * z);
return vector3(w * q.x + x * q.w + y * q.z - z * q.y,
w * q.y + y * q.w + z * q.x - x * q.z,
w * q.z + z * q.w + x * q.y - y * q.x)*( 1.0f/norm() );
// 29 multiplications, 20 addidtions, 4 variables
// 5
/*
// refrence implementation
xxquaternion r = (*this)*xxquaternion(v.x, v.y, v.z, 0)*this->inverted();
return vector3( r.x, r.y, r.z );
*/
/*
// alternative implementation
float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
x2 = q.x + q.x; y2 = q.y + q.y; z2 = q.z + q.z;
xx = q.x * x2; xy = q.x * y2; xz = q.x * z2;
yy = q.y * y2; yz = q.y * z2; zz = q.z * z2;
wx = q.w * x2; wy = q.w * y2; wz = q.w * z2;
return vector3( v.x - v.x * (yy + zz) + v.y * (xy - wz) + v.z * (xz + wy),
v.y + v.x * (xy + wz) - v.y * (xx + zz) + v.z * (yz - wx),
v.z + v.x * (xz - wy) + v.y * (yz + wx) - v.z * (xx + yy) )*( 1.0f/norm() );
// 18 multiplications, 21 addidtions, 12 variables
*/
};
Good luck.

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.