OpenGL坐标系
上面的代码还是不够的。因为需要对虚拟相机进行模型变换。在OpenGL中,模型变换有三种方式,即glTranslateX, glRotateX, glScaleX. 对于单个变换很容易理解,但是当多种变换组合的时候,初学者就很难分析清楚最后的结果了。
首先我们来讲解一种红宝书上介绍的一种经典情况。即先旋转还是先平移的问题。
glTranslatef(0,0,5);
glRotatef(45, 0,0,1);
显然这段代码如果换个顺序执行结果是不同的。因此红宝书中指出为了方便读者理解,提出了从全局坐标系和局部坐标系来理解这段代码。
如果是全局坐标系理解,那么从下往上执行,结果就应该是先绕z轴旋转45°,紧接着沿着z轴平移5个单位。
如果是局部坐标系理解,那么从上往下执行,结果就应该是先沿z轴平移5个单位,紧接着,建立模型的局部坐标系(第一次局部坐标系与场景的坐标系平行),再绕z轴旋转45°。
这样读者在理解层次上就统一了。
再举上面一例。
glScalef(dPerRatio,1.0,1.0);
glRotatef(45,0,0,1);
这段代码的目标是将一个正方形旋转45°后,x轴方向伸缩dPerRatio倍,变成长方形。
如果从全局坐标系理解是很容易的。但是有人认为,从局部坐标系好像不对劲。因为首先是X轴伸缩了dPerRatio倍。再次旋转45°。显然不是长方形了?
这个理解是有问题的。因为既然从局部坐标系理解,要知道,执行了glScalef之后,局部坐标轴其实也被伸缩了。这样再次旋转45°,结果还是长方形。由于glScalef从局部坐标系一般不容易理解,所以专家建议还是这种变换从全局坐标系理解。
旋转轴
了解了OpenGL中的模型变换之后,我们就要尝试将刚才创建的相机进行变换,即镜头需要从原点移到相机位置,并且使它的轴线与视线方向平行。设变换前轴向量为
平移变换比较简单,不做详细解释。我们来看旋转变换。在已知旋转轴和旋转角度的情况下,利用glRotateX这样的一次变换确实可以达到效果。但是我们其实还有个要求,即保证相机的up分量始终与y轴平行,这样的效果才是用户乐于看到的。因此,我们决定在上次旋转R1之后,再加入一次旋转R2来保证up分量的方向。这样的话,我们需要首先计算R1之后的up分量(用四元数计算)。在此基础上,重新计算R2的旋转轴和旋转角度,进行旋转,我们又会惊讶的发现R1之后本来正确的轴线方向现在又与视线方向不平行了。因此我们知道了,利用旋转轴的单次变换是无法达到要求的。实际上,绕旋转轴的单次变换其实可以分解为多个绕坐标轴的变换组合。因此从这里进行突破。
为此,我们加入这样的变换代码:
double dAlpha,dBeta;
dAlpha = acos(vVec.y);
dBeta = atan2(vVec.x, vVec.z);
glPushMatrix();
{
//平移变换
glTranslatef(camPoint.x,camPoint.y,camPoint.z);
// 旋转变换
glRotatef((dBeta) * MathEx::dRadToDeg,0,1,0);
glRotatef((dAlpha) * MathEx::dRadToDeg,1,0,0);
glRotatef(90,1,0,0);
// 绘制虚拟相机轴线
if(pCam->m_bShowAxies)
{
.......
}
}
由于默认情况下,up分量已经与y轴平行,因此只要我们在采用局部坐标系变换的过程中,旋转轴不包含z轴, up分量就不会变化。
评论