True cylindrical billboarding constraints the objects rotation to an axis, as in the cheating version, but the look at vector from the object will be rotated in the camera's direction, restricted to the plane defined by the up vector, and the center of the local origin. The following image shows what happens.
真正的广告牌技术就像上文具有视觉欺骗性质的广告牌一样限制物体绕某个坐标轴旋转,但是物体的LookAt向量将被旋转到照相机的方向,而这个LookAt向量的旋转被限制在一个由up向量和局部坐标系原点定义的平面内。下面的图片显示了所发生的事情。
In the previous example, a user walking in the ground, the trees would correctly face the user.
在先前的例子中,一个用户在地面上行走,树木会正确的朝向用户。
The method presented in here requires that you know where the object is in world coordinates, as well as the targets position, also in world coordinates. Usually the target is the camera's position which known. However the same may not be true for the object. If the object is positioned in the world through geometric transformations, such as translations and rotations, then keeping track of its position in world coordinates can be a hard task. We'll come back to this issue in alater section.
这里所探讨的方法要求你知道物体和目标的位置在世界坐标系中的坐标值。通常目标是在照相机的位置而且位置已知。然而物体可能并不是这个情况,他的位置不能直接获得。如果物体通过几何变换而被放置在世界坐标系中,比如通过平移和旋转,那么视图跟踪他在世界坐标系中位置变换的轨迹可能会是一个艰难的工作。我们将会在后期的话题中讨论这个问题。
Assuming that both the objects and target positions are known in world coordinates, then billboarding becomes a simple geometrical operation.
假设物体和目标的世界坐标已知,那么广告牌技术将会是一个简单的几何操作。
For simplicity reasons it is assumed that the object has the following vectors:
为了简化处理的原因我们假设物体有如下的向量:
In practice this means that the object is drawn in the local origin, and it is looking along the positive Z axis.
事实上这意味着物体在局部坐标空间绘制,并且物体的正面朝向z轴正方向。//译者注:z轴正方向垂直屏幕指向观察者
In order to orientate the object a rotation is performed around the up vector, by the angle between the lookAt vector and the vector from the object to the camera.
为了使这个物体特殊方向化,一个绕up向量的旋转操作被执行,旋转的角度是lookAt向量和物体到相机形成的向量之间的夹角。
The vector from the object to the camera can be defined as
从物体到相机的向量被定义为如下值
物体到相机向量=相机位置-物体位置
The vector objToCamProj is the projection of objToCam in the XZ plane. Therefore its Y componente should be set to zero.
向量objToCamProj是objToCam向量在XZ平面上的投影向量。因此Y方向的值应该被设置为0。
If objToCamProj is normalized then the inner product between lookAt andobjToCam will allow the computation of the cosine of the angle. However knowing the cosine alone is not enough, since the cos(a) = cos(-a). Computing the cross product as well allows us to uniquely determine the angle. The cross product vector will have the same direction as the up vector if the angle is positive. For negative angles theup vector's direction will opposed to the up vector, effectively reversing the rotation.
如果向量objToCamProj已经被标准化了,那么向量lookAt和向量objToCam的内积就是角度A的cos(A)值。然而光是知道cosine值还是不够的,因为cos(a)=cos(-a)。与此同时计算那两个向量的叉乘值将会帮助我们唯一确定那个角度值。如果角度是正值的话,那么叉乘的结果向量将会拥有和up向量相同的方向。对于负的角度值计算出来的那个叉乘结果向量的方向将和up向量的方向相反,从而实际上相相反的方向旋转物体。
The required steps after computing objToCamProj are:
在计算完objToCamProj向量后还要执行以下步骤:
As in the previous billboards a function can be defined to setup the billboard. The code is as follows:
正如先前的广告牌一样,可以定义一个函数来建立广告牌。代码如下。
void billboardCylindricalBegin(float camX, float camY, float camZ,float objPosX, float objPosY, float objPosZ) { float lookAt[3],objToCamProj[3],upAux[3]; float modelview[16],angleCosine; glPushMatrix(); // objToCamProj is the vector in world coordinates from the // local origin to the camera projected in the XZ plane objToCamProj[0] = camX - objPosX ; objToCamProj[1] = 0; objToCamProj[2] = camZ - objPosZ ; // This is the original lookAt vector for the object // in world coordinates lookAt[0] = 0; lookAt[1] = 0; lookAt[2] = 1; // normalize both vectors to get the cosine directly afterwards mathsNormalize(objToCamProj); // easy fix to determine wether the angle is negative or positive // for positive angles upAux will be a vector pointing in the // positive y direction, otherwise upAux will point downwards // effectively reversing the rotation. mathsCrossProduct(upAux,lookAt,objToCamProj); // compute the angle angleCosine = mathsInnerProduct(lookAt,objToCamProj); // perform the rotation. The if statement is used for stability reasons // if the lookAt and objToCamProj vectors are too close together then // |angleCosine| could be bigger than 1 due to lack of precision if ((angleCosine < 0.99990) && (angleCosine > -0.9999)) glRotatef(acos(angleCosine)*180/3.14,upAux[0], upAux[1], upAux[2]); }
The function billboardEnd() should be called after rendering the object as shown in the following example.
函数billboardEnd()应该在渲染物体之后被调用,如下代码所示。
billboardCylindricalBegin(cx,cy,cz,ox,oy,oz); drawObject(); billboardEnd();