math - 3D 点到 2D 功能无法正常工作
问题描述
我已经研究这个话题一段时间了,我终于尝试自己实现它;但是,由于某种原因,3d 点没有正确转换为 2d 点,这意味着我的函数返回了错误的值。这可能来自两种不同的情况:
1)我的数学不正确
2)我的矩阵有不正确的值
由于我正在反转和使用地址,因此我不确定矩阵。有人可以检查我的数学,看看是不是数学不正确吗?对此功能的任何帮助将不胜感激。提前感谢您的任何建议。
private bool ConvertToScreen(Vector3 position3D, ref Point screenPoint)
{
// r is the right rotation (x-axis)
// u is the up rotation (y-axis)
// f is the forward rotation (z-axis)
// p is the position (transform)
Point returnPoint = new Point(300, 400);
// Set Values of Matrix
Matrix matrix = GetMatrix();
// Do the math calculations here
float xPrime = matrix.rX * position3D.x + matrix.rY * position3D.y + matrix.rZ * position3D.z + matrix.rW;
float yPrime = matrix.uX * position3D.x + matrix.uY * position3D.y + matrix.uZ * position3D.z + matrix.uW;
// Dont need zPrime
float wPrime = matrix.pX * position3D.x + matrix.pY * position3D.y + matrix.pZ * position3D.z + matrix.pW;
// If wPrime > 0 we can see the point
if (wPrime <= 0)
{
return false;
}
xPrime *= 1 / wPrime;
yPrime *= 1 / wPrime;
// Relative To Screen Center
xPrime = rect.left + 0.5f * xPrime * (rect.right - rect.left) + 0.5f;
yPrime = rect.top + 0.5f * yPrime * (rect.bottom - rect.top) + 0.5f;
returnPoint = new Point((int)xPrime, (int)yPrime);
screenPoint = returnPoint;
return true;
}
解决方案
这个计算有几个步骤,我建议把它分成相应的部分并测试每个部分。
模型到相机坐标- 这是
position = (x,y,z)
点从模型坐标到相机坐标的旋转。这里我们假设相机的目标在原点。local = rot * (position - target) | x' | | rx ry rz | | x | | rx*x + ry*y + rz*z | | y' | = | ux uy yz | * | y | = | ux*x + uy*y + uz*z | | z' | | fx fy fz | | z | | fx*x + fy*y + fz*z |
透视投影- 您需要定义目标和相机之间的距离,以及覆盖视图的模型大小。让我们称之为,
distance
和size
。0..1
结果是在x 和 y之间变化的视图坐标。| vx | | (distance/size)*(x'/(distance+z')) | | | = | | | | vy | | (distance/size)*(y'/(distance+z')) |
这来自相似的三角形。如果
x'=size
然后。z'=0
_vx=1
越大z'
越小vx
。像素坐标
在这里,您将视图坐标映射到像素。您的视口有 a
width
和 aheight
并且您希望[0,0]
像素位于左上角和[width-1,height-1]
右下角。width +-------------------+ |(-1,1) : (1,1)| | : | | : (0,0) | +- - - - -+- - - - -+ height | : | | : | |(-1,-1) : (1,-1)| +-------------------+ px = (width-1)*(vx+1.0)/2.0 py = (height-1)*(1.0-vy)/2.0
最后,我建议使用 OOP 编程(如果可能)将向量/矩阵数学与意图分开。考虑以下c#中的示例。
public static Vector3 ThroughCamera(Vector3 point, Vector3 target, Matrix3 camera)
{
return camera.Transpose()*(point-target);
}
public static Vector2 Perspective(Vector3 point, double distance, double size=1)
{
return new Vector2(
(distance/size)*(point.X/(distance+point.Z)),
(distance/size)*(point.Y/(distance+point.Z)) );
}
public static PointF Pixel(Vector2 point, int width, int height)
{
return new PointF(
(float) ((width-1)*(point.X+1)/2),
(float) ((height-1)*(1-point.Y)/2) );
}
static void dlg_Paint(object sender, PaintEventArgs e)
{
Form dlg=sender as Form;
// Set camera rotation
Matrix3 camera=Matrix3.Ry(0.67);
double distance=25;
double size=20;
for(int i=0; i<10; i++)
{
for(int j=0; j<10; j++)
{
// this is the model points
Vector3 pt=new Vector3(5*(i-5), 5*(j-5), 0);
// these are the points through the camera
Vector3 pt_local=ThroughCamera(pt, Vector3.O, camera);
// these are the view coordinates
Vector2 pt_view=Perspective(pt_local, distance, size);
// these are the pixel coordinates
PointF px=Pixel(pt_view, dlg.ClientSize.Width, dlg.ClientSize.Height);
e.Graphics.DrawRectangle(Pens.Blue, px.X, px.Y, 1f, 1f);
}
}
}
推荐阅读
- django - 在 Vue 3 中设置全局 Axios 标头
- c++ - TBB:不能使用数组类型?
- python - 从 flaks api 发送字节流给出 unicode 错误
- php - 如何使用 web.config 将 PHP 文件添加到每个页面?
- javascript - 您如何使 Bootstrap 导航栏徽标和菜单分别在左侧和右侧具有“填充”?
- jupyter-notebook - ipywidgets 下拉小部件:如何根据 onchange 事件上的选定下拉选项填充嵌套小部件?
- c++ - 在 mbstowcs 中的 Windows 上“retsize <= sizeInWords”
- python - 我是否在此代码中正确考虑了夏令时?
- django - Thinkific和多重登录预防
- javascript - 如何显示过渡,当我将鼠标悬停在按钮上时,主 div 将展开显示每个按钮