有一个3D模型物体,想要它在屏幕的某个位置显示。简单的说原3D物体在屏幕的中心显示,现在我可以把他移动到相应的屏幕坐标进行显示。通过鼠标点击屏幕上的某个位置,把物体平移到那个位置上进行显示。如何把二维的平移变成三维物体的平移呢?
原理如下图:下图是一个视景体。投影的平面为近投影平面,物体投影到近投影平面又通过视口变换变换到屏幕坐标,在设备上显示。
1.通过鼠标点击事件,可以获得鼠标点击的屏幕坐标。
2.物体原点坐标值,物体坐标系。(一般情况下,物体坐标原点与世界坐标原点重合)
3.已知照相机的位置,观察方向,视景体的宽高,近平面,远平面。(通过gluLookAt设置照相机坐标系。通过gluPerspective或者glFrustrum设置视景体)
物体在自身的一个垂直于视点的平面上的平移向量。resultVector.
void CBuilding::Project(float modelview[4*4], float project[4*4]){//视口
GLint viewPort[4];GLfloat rayNearPoint[3];GLfloat rayFarPoint[3];GLfloat rayDir[3];//物体上的原点,如果是进行平移了,则是平移后的点
float planPoint[3] = {m_xTrans, m_yTrans, m_zTrans};
float *crossPoint;
float *crossPoint1;
//垂直于视线的平面向量
m3dNormalizeVector(m_eyeVector);glGetIntegerv(GL_VIEWPORT, viewPort);//求出一条射线, 屏幕中心坐标
GLfloat winX = m_centerX;GLfloat winY = m_centerY;GLfloat winZ = 0.0;gluUnProject(winX, winY, winZ, modelview, project, viewPort, &rayNearPoint[0], &rayNearPoint[1], &rayNearPoint[2]);winZ = 1.0;gluUnProject(winX, winY, winZ, modelview, project, viewPort, &rayFarPoint[0], &rayFarPoint[1], &rayFarPoint[2]);//求得射线向量
m3dSubtractVectors3(rayDir, rayFarPoint, rayNearPoint);m3dNormalizeVector(rayDir);//射线与平面的交点
crossPoint = CalPlaneLineIntersectPoint(m_eyeVector, planPoint, rayDir, rayNearPoint);//再求移动后的射线与平面的交点 crossPoint1. 计算两个点之间向量。 以这个向量作为物体的平移
winX = m_screenX;winY = viewPort[3] - m_screenY;winZ = 0.0;gluUnProject(winX, winY, winZ, modelview, project, viewPort, &rayNearPoint[0], &rayNearPoint[1], &rayNearPoint[2]);winZ = 1.0;gluUnProject(winX, winY, winZ, modelview, project, viewPort, &rayFarPoint[0], &rayFarPoint[1], &rayFarPoint[2]);//求得射线向量
m3dSubtractVectors3(rayDir, rayFarPoint, rayNearPoint);m3dNormalizeVector(rayDir);//射线向量与平面的交点
crossPoint1 = CalPlaneLineIntersectPoint(m_eyeVector, planPoint, rayDir, rayNearPoint);//求得移动向量
m_moveVector[0] = crossPoint1[0] - crossPoint[0];m_moveVector[1] = crossPoint1[1] - crossPoint[1];m_moveVector[2] = crossPoint1[2] - crossPoint[2];delete []crossPoint;
delete []crossPoint1;
}/// <summary>
/// 求一条直线与平面的交点
/// </summary>
/// <param name="planeVector">平面的法线向量,长度为3</param>
/// <param name="planePoint">平面经过的一点坐标,长度为3</param>
/// <param name="lineVector">直线的方向向量,长度为3</param>
/// <param name="linePoint">直线经过的一点坐标,长度为3</param>
/// <returns>返回交点坐标,长度为3</returns>
float* CBuilding::CalPlaneLineIntersectPoint(float* planeVector, float* planePoint, float* lineVector, float* linePoint){float* returnResult = new float[3];float vp1, vp2, vp3, n1, n2, n3, v1, v2, v3, m1, m2, m3, t,vpt;
vp1 = planeVector[0];vp2 = planeVector[1];vp3 = planeVector[2];n1 = planePoint[0];n2 = planePoint[1];n3 = planePoint[2];v1 = lineVector[0];v2 = lineVector[1];v3 = lineVector[2];m1 = linePoint[0];m2 = linePoint[1];m3 = linePoint[2];vpt = v1 * vp1 + v2 * vp2 + v3 * vp3;//首先判断直线是否与平面平行
if (vpt == 0)
{returnResult = NULL;}else
{t = ((n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3) / vpt;returnResult[0] = m1 + v1 * t;returnResult[1] = m2 + v2 * t;returnResult[2] = m3 + v3 * t;}return returnResult;
}void CBuilding::DrawData()
{glPushMatrix();....glTranslatef(m_xTrans, m_yTrans, m_zTrans);//物体移动向量
glTranslatef(m_moveVector[0], m_moveVector[1], m_moveVector[2]);绘制物体...glPopMatrix();}void RenderScene()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();glPushMatrix();glFrustum(-g_frum, g_frum, -g_frum/aspect, g_frum / aspect, 1.0, 100000.0);//获得投影矩阵,用于gluUnProject
glGetFloatv(GL_PROJECTION_MATRIX, project);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(m_eye[0], m_eye[1], m_eye[2], m_ref[0], m_ref[1], m_ref[2], m_eyeUp[0], m_eyeUp[1], m_eyeUp[2]);//获得模型视图矩阵用于gluUnProjectio
glGetFloatv(GL_MODELVIEW_MATRIX, modelview);glPushMatrix();DrawVecData();glPopMatrix();glutSwapBuffer();}void DrawVecData()
{GLint viewport[4];glGetIntegerv(GL_VIEWPORT, viewport);//求得观察方向
float vec[3];
vec[0] = m_eye[0] - m_ref[0];vec[1] = m_eye[1] - m_ref[1];vec[2] = m_eye[2] - m_ref[2];//mouseUp为鼠标点击的坐标,viewport[2]/2, viewport[3]/2 为屏幕的中心点
CBuilding building(mouseUpX, mouseUpY, viewport[2]/2, viewport[3]/2);building.LoadData(fileName[0]);//设置物体的偏移值 m_xTrans, m_yTrans, m_zTrans
building.SetTranslation(xTran, yTran, zTran);//设置观察方向 m_eyeVector
building.SetEyeVector(vec);//进行投影计算,使用上面获得的modelviwe和porject矩阵
//需要注意的是,如果物体经过旋转平移等变换,那么相应的modelview也应该是平移和旋转后的。
building.Project(modelview, project);building.DrawData();building.ReleaseData();}
相关讨论http://www.opengpu.org/forum.php?mod=viewthread&tid=16405
关于如何求得空间直线与平面的交点的函数CalPlaneLineIntersectPoint。
参考这里http://blog.csdn.net/abcjennifer/article/details/6688080