三维图形学体系中,有两种类型的投影。透视投影(Perspective projection)和正交投影(Orthographic projection)。
对于透视投影(Perspective projection),有两个矩阵:透视变换矩阵和相机到世界坐标系变换。透视变换矩阵参考:https://zhuanlan.zhihu.com/p/463027517
相机到世界坐标系变换由相机的位置、朝向、向上的方向计算而来:
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;
}
版权声明:本文为weixin_44153630原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。