这几天在研究Impostor,按照传统的方法可以做出来不错的效果,采用GamaStura的方法很快就可以做出来不错的效果,但是因为要扩展一下,在原先的质量上提高,而且用Instance来画,这样只需要一个quad mesh,别的信息都放在Instance Buffer上。因此需要有个Billboard矩阵,也就是说需要将一个quad mesh转换到世界坐标系。
关于Billboard矩阵如何推导,NeHe和FlipCode都有相应的文章来讲解,NeHe讲解的更透彻一点。这虽然算是个很古老的技术,但是用到的时候还是要费一些脑细胞来挣扎一下,顺便就记录下来吧,正好可以理清思路。
http://www.flipcode.com/archives/Billboarding-Excerpt_From_iReal-Time_Renderingi_2E.shtml
http://nehe.gamedev.net/article/billboarding_how_to/18011/
计算BillBoard矩阵之前我们需要知道CameraPos和BillBoard的中心位置CenterPos,然后可以计算出来LookAt向量look=normalize(CameraPos-CenterPos);然后我们需要计算Billboard矩阵的right向量,计算right需要Billboard矩阵的up向量,这时候我们还没有办法知道up向量,但是有一点事确定的Billboard的up位于look和主Camera的up构成平面上,而right是垂直于这个平面的,所以right=look x 主camera up;然后就可以计算出来Billboard矩阵的up。
有了look,up,right在加上Billboar的中心位置p就可以构建出Billboard矩阵了
上面推导的方法适合于所有类型的Billboard,按x,y,z轴或者任意轴旋转的Billboard矩阵都可以按照上面的思路来推导。NeHe的教程中也给出了各种不同类型的Billboard矩阵的计算方法。
//*****************************************************************************
// This only needs to be done once for all the particles, because the view
// matrix does not change any between them. If you were to modify the view
// matrix at all, you would need to recalculate the camera pos and up vector.
//*****************************************************************************
void GetCameraPosAndUp(Vertex3f &camPos, Vector3f &camUp)
{
Matrix44f view;
glGetFloatv(GL_MODELVIEW_MATRIX, view.matrix);
// The values in the view matrix for the camera are the negative values of
// the camera. This is because of the way gluLookAt works; I'm don't fully
// understand why gluLookAt does what it does, but I know doing this works:)
// I know that gluLookAt creates a the look vector as (eye - center), the
// resulting direction vector is a vector from center to eye (the oposite
// of what are view direction really is).
camPos = Vertex3f(-view.matrix[12], -view.matrix[13], -view.matrix[14]);
camUp = Vector3f(view.matrix[1], view.matrix[5], view.matrix[9]);
// zero the translation in the matrix, so we can use the matrix to transform
// camera postion to world coordinates using the view matrix
view.matrix[12] = view.matrix[13] = view.matrix[14] = 0;
// the view matrix is how to get to the gluLookAt pos from what we gave as
// input for the camera position, so to go the other way we need to reverse
// the rotation. Transposing the matrix will do this.
view.TransposeRotation();
// get the correct position of the camera in world space
camPos = view * camPos;
}
//*****************************************************************************
// Create the billboard matrix: a rotation matrix created from an arbitrary set
// of axis. Store those axis values in the first 3 columns of the matrix. Col
// 1 is the X axis, col 2 is the Y axis, and col 3 is the Z axis. We are
// rotating right into X, up into Y, and look into Z. The rotation matrix
// created from the rows will translate the arbitrary axis set to the global
// axis set. Lastly, OpenGl stores the matrices by columns, so enter the data
// into the array columns first.
//*****************************************************************************
void CreateBillboardMatrix(Matrix44f &bbmat, const Vector3f &right, const Vector3f &up, const Vector3f &look, const Vertex3f &pos)
{
bbmat.matrix[0] = right.x;
bbmat.matrix[1] = right.y;
bbmat.matrix[2] = right.z;
bbmat.matrix[3] = 0;
bbmat.matrix[4] = up.x;
bbmat.matrix[5] = up.y;
bbmat.matrix[6] = up.z;
bbmat.matrix[7] = 0;
bbmat.matrix[8] = look.x;
bbmat.matrix[9] = look.y;
bbmat.matrix[10] = look.z;
bbmat.matrix[11] = 0;
// Add the translation in as well.
bbmat.matrix[12] = pos.x;
bbmat.matrix[13] = pos.y;
bbmat.matrix[14] = pos.z;
bbmat.matrix[15] = 1;
}
//*****************************************************************************
//*****************************************************************************
void BillboardPoint(const Vertex3f &pos, const Vertex3f &camPos, const Vector3f &camUp)
{ // create the look vector: pos -> camPos
Vector3f look = camPos - pos;
look.Normalize();
// right hand rule cross products
Vector3f right = camUp.Cross(look);
Vector3f up = look.Cross(right);
Matrix44f bbmat;
CreateBillboardMatrix(bbmat, right, up, look, pos);
// apply the billboard
glMultMatrixf(bbmat.matrix);
};
//*****************************************************************************
//*****************************************************************************
void BillboardAxisX(const Vertex3f &pos, const Vertex3f &camPos)
{ // create the look vector: pos -> camPos
Vector3f look = camPos - pos;
// we are billboarding along the X axis - zero the look value for x
look.x = 0;
look.Normalize();
// right hand rule cross products - the up vector is the +x Axis
Vector3f up = Vector3f(1,0,0);
Vector3f right = up.Cross(look);
Matrix44f bbmat;
CreateBillboardMatrix(bbmat, right, up, look, pos);
// apply the billboard
glMultMatrixf(bbmat.matrix);
};
//*****************************************************************************
//*****************************************************************************
void BillboardAxisY(const Vertex3f &pos, const Vertex3f &camPos)
{ // create the look vector: pos -> camPos
Vector3f look = camPos - pos;
// we are billboarding along the Y axis - zero the look value for y
look.y = 0;
look.Normalize();
// right hand rule cross products - the up vector is the +y Axis
Vector3f up = Vector3f(0,1,0);
Vector3f right = up.Cross(look);
Matrix44f bbmat;
CreateBillboardMatrix(bbmat, right, up, look, pos);
// apply the billboard
glMultMatrixf(bbmat.matrix);
};
//*****************************************************************************
//*****************************************************************************
void BillboardAxisZ(const Vertex3f &pos, const Vertex3f &camPos)
{ // create the look vector: pos -> camPos
Vector3f look = camPos - pos;
// we are billboarding along the Z axis - zero the look value for z
look.z = 0;
look.Normalize();
// right hand rule cross products - the up vector is the +z Axis
Vector3f up = Vector3f(0,0,1);
Vector3f right = up.Cross(look);
Matrix44f bbmat;
CreateBillboardMatrix(bbmat, right, up, look, pos);
// apply the billboard
glMultMatrixf(bbmat.matrix);
};
//*****************************************************************************
//*****************************************************************************
void BillboardAxis(const Vertex3f &pos, const Vector3f &axis, const Vertex3f &camPos)
{ // create the look vector: pos -> camPos
Vector3f look = camPos - pos;
look.Normalize();
// billboard about the direction vector
Vector3f up = axis;
Vector3f right = up.Cross(look);
// watch out when the look vector is almost equal to the up vector the right
// vector gets close to zeroed, normalize it
right.Normalize();
// the billboard won't actually face the direction of the look vector we
// created earlier, that was just used as a tempory vector to create the
// right vector so we could calculate the correct look vector from that.
look = right.Cross(up);
Matrix44f bbmat;
CreateBillboardMatrix(bbmat, right, up, look, pos);
// apply the billboard
glMultMatrixf(bbmat.matrix);
};