屏幕上平移物体的实现

原创
2014/04/15 21:23
阅读数 2.6K

目标:

有一个3D模型物体,想要它在屏幕的某个位置显示。简单的说原3D物体在屏幕的中心显示,现在我可以把他移动到相应的屏幕坐标进行显示。通过鼠标点击屏幕上的某个位置,把物体平移到那个位置上进行显示。如何把二维的平移变成三维物体的平移呢?

原理如下图:下图是一个视景体。投影的平面为近投影平面,物体投影到近投影平面又通过视口变换变换到屏幕坐标,在设备上显示。

1395387076_1425

已知条件:

1.通过鼠标点击事件,可以获得鼠标点击的屏幕坐标。

2.物体原点坐标值,物体坐标系。(一般情况下,物体坐标原点与世界坐标原点重合)

3.已知照相机的位置,观察方向,视景体的宽高,近平面,远平面。(通过gluLookAt设置照相机坐标系。通过gluPerspective或者glFrustrum设置视景体)

求解:

物体在自身的一个垂直于视点的平面上的平移向量。resultVector.

2

求解过程:

  • 已知屏幕上的中心点screenCenter和鼠标点击的点mousePos 。还有物体坐标的原点objOrigin,照相机坐标cameraPos.
  • 首先把屏幕上的两个点screenCenter和mousePos转换成对应的三维场景中的视觉坐标。(通过gluUnProject可以达到这个目的。)
  • 通过cameraPos和screenCenter两点相应的视觉坐标, 可以求得第一条射线ray1。cameraPos和mousePos求得第二天射线ray2.
  • 通过照相机的观察方向eyeDir和物体上的原点,得到一个垂直于观察方向的平面plane.
  • 求得这两条射线与这个平面的交点,分别为crossPoint1, crossPoint2.
  • 那么crossPoint2 – crossPoint1 就得到了物体的平移向量moveVector。再调用glTranslatef进行平移。

关键代码如下:

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();
}

image

相关讨论http://www.opengpu.org/forum.php?mod=viewthread&tid=16405

关于如何求得空间直线与平面的交点的函数CalPlaneLineIntersectPoint。

参考这里http://blog.csdn.net/abcjennifer/article/details/6688080

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
3 收藏
0
分享
返回顶部
顶部