图形学基础:屏幕坐标转世界坐标

三维图形学体系中,有两种类型的投影。透视投影(Perspective projection)和正交投影(Orthographic projection)。
图形学基础:屏幕坐标转世界坐标_第1张图片
对于透视投影(Perspective projection),有两个矩阵:透视变换矩阵和相机到世界坐标系变换。透视变换矩阵参考:https://zhuanlan.zhihu.com/p/463027517
图形学基础:屏幕坐标转世界坐标_第2张图片
图形学基础:屏幕坐标转世界坐标_第3张图片
相机到世界坐标系变换由相机的位置、朝向、向上的方向计算而来:

  gp_Dir        myUp;       //!< Camera up direction vector
  gp_Dir        myDirection;//!< Camera view direction (from eye)
  gp_Pnt        myEye;      //!< Camera eye position

OCCT 中的相机世界坐标系矩阵计算:

template <typename Elem_t>
void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
                                        const NCollection_Vec3<Elem_t>& theFwdDir,
                                        const NCollection_Vec3<Elem_t>& theUpDir,
                                        const NCollection_Vec3<Elem_t>& theAxialScale,
                                        NCollection_Mat4<Elem_t>& theOutMx)
{
  NCollection_Vec3<Elem_t> aForward = theFwdDir;
  aForward.Normalize();

  // side = forward x up
  NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
  aSide.Normalize();

  // recompute up as: up = side x forward
  NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);

  NCollection_Mat4<Elem_t> aLookMx;
  aLookMx.SetRow (0, aSide);
  aLookMx.SetRow (1, anUp);
  aLookMx.SetRow (2, -aForward);

  theOutMx.InitIdentity();
  theOutMx.Multiply (aLookMx);
  theOutMx.Translate (-theEye);

  NCollection_Mat4<Elem_t> anAxialScaleMx;
  anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
  anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
  anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();

  theOutMx.Multiply (anAxialScaleMx);
}

也可参考:相机变换矩阵

由此,对于三维点World3dPoint到屏幕点Screen2dPoint的计算:
Screen2dPoint = 透视变换矩阵 X 相机到世界坐标系变换 X World3dPoint;
然而需要注意的是,屏幕上一个点到三维中实际是一条线,而非一个点。
正交投影想对透视投影更简单一些。

如果想要调试弄清楚原理,可以参考 OCCT 例子源码:

// OpenCASCADE-7.6.0-vc14-64\opencascade-7.6.0\samples\mfc\standard\04_Viewer3d\src\Viewer3dView.cpp
gp_Pnt ConvertClickToPoint(Standard_Real x, Standard_Real y, Handle(V3d_View) aView)
{
	Standard_Real XEye,YEye,ZEye,XAt,YAt,ZAt;
	aView->Eye(XEye,YEye,ZEye);
	aView->At(XAt,YAt,ZAt);
	gp_Pnt EyePoint(XEye,YEye,ZEye);
	gp_Pnt AtPoint(XAt,YAt,ZAt);

	gp_Vec EyeVector(EyePoint,AtPoint);
	gp_Dir EyeDir(EyeVector);

	gp_Pln PlaneOfTheView = gp_Pln(AtPoint,EyeDir);
	Standard_Real X,Y,Z;
	aView->Convert(int(x),int(y),X,Y,Z);
	gp_Pnt ConvertedPoint(X,Y,Z);
	gp_Pnt2d ConvertedPointOnPlane = ProjLib::Project(PlaneOfTheView,ConvertedPoint);
	
	gp_Pnt ResultPoint = ElSLib::Value(ConvertedPointOnPlane.X(),
									ConvertedPointOnPlane.Y(),
									PlaneOfTheView);
	return ResultPoint;
}

你可能感兴趣的:(图形学笔记,人工智能)