透视投影:确定三维空间中点的二维屏幕坐标(x,y),用于IT技术。

16
我希望确定三维空间中点的二维屏幕坐标(x,y)。我希望投影的点是由GPS坐标和海拔表示的真实世界点。
例如: 点(Lat:49.291882, Long:-123.131676, Height: 14m)
摄像机位置和高度也可以确定为一个x、y、z点。我还有相机的朝向(指南针度数),它的倾斜角度(在/下地平线上)和翻滚(围绕z轴)。
我没有3D编程经验,因此我已经阅读了有关透视投影的主题,并学习到它需要矩阵、转换等知识——它们现在都让我完全困惑了。
有人告诉我,使用OpenGL可能有助于构建真实世界点的3D模型,设置相机方向并检索3D点的2D坐标。
但是,我不确定使用OpenGL是否是解决这个问题的最佳方法,即使是,我也不知道如何创建模型、设置相机等。
有人能建议一种最好的方法来解决我的问题吗?如果OpenGL是可行的解决方案,那么我必须使用OpenGL ES,如果这有任何区别的话。哦,无论我选择什么解决方案,它都必须执行得快。

你想画东西还是只想计算一些点? - Tobias Müller
5个回答

20

这里是一个非常普遍的答案。假设相机位于(Xc, Yc, Zc),您要投影的点是P = (X, Y, Z)。从相机到您要投射的二维平面的距离为F(因此平面的方程式为Z-Zc=F)。在平面上投影P得到的二维坐标为(X',Y')。

那么,简单来说:

X'= ((X - Xc) * (F/Z)) + Xc

Y' = ((Y - Yc) * (F/Z)) + Yc

如果您的相机位于原点,则简化为:

X' = X * (F/Z)

Y' = Y * (F/Z)


2
第一和第二个方程式不应该是 X' = ((X - Xc) * (F/(Z-Zc))) + XcY' = ((Y - Yc) * (F/(Z-Zc))) + Yc 吗? - Alec Jacobson
@Alec Jacobson,F = Z - Zc,如果是F /(Z-Zc),那么它将等于1(这意味着X'= X且Y = Y')。 - FaranAiki
对不起,我甚至不知道我刚才说了什么;我字面上认为 F = Z - Zc,所以它会变成1。 - FaranAiki
如果摄像机距离发生变化,还应该有一个缩放因素。可以只是一个比例尺,或者是比例尺加旋转。您如何推导出这个? - moi

5

我之前不知道需要转换为笛卡尔坐标系,谢谢你提醒我。 关于OpenGL,我想尝试使用它来实现解决方案,因为它有设置相机位置的方法,而且我相信还有从3D模型获取2D投影的机制。你知道有什么好的教程吗? - Jason
这些是经典教程转换成多种语言的版本:http://nehe.gamedev.net/ - Judge Maygarden

2
如果只需要计算一些点的坐标,你只需要一些代数技能,而不需要使用openGL进行3D编程。
此外,openGL不能处理地理坐标。
首先获取关于WGS84和大地坐标的文档,你需要将GPS数据转换为笛卡尔坐标系(例如,在WGS84椭球体定义的以地球为中心的笛卡尔坐标系)。
然后可以进行矩阵计算。变换链大致如下:
- WGS84 - 以地球为中心的坐标系 - 某个本地坐标系 - 相机坐标系 - 2D投影
对于第一个转换,请参见这里。最后涉及到一个投影矩阵,其他只是坐标旋转和平移。"某个本地坐标系"是以你的相机位置为原点的切向椭球体的本地笛卡尔坐标系。

2
我建议阅读Eric Lengyel的《3D游戏编程与计算机图形学数学基础》。该书包括矩阵、变换、视锥体、透视投影等内容。
此外,《OpenGL编程指南》(红皮书)也有一章关于视图变换和设置摄像机的内容(包括如何使用gluLookAt)。
如果你不想显示3D场景并且仅限使用OpenGL ES,则最好编写自己的代码来将3D映射到2D窗口坐标。可以从Mesa 3D下载开源的OpenGL实现,了解它们如何实现gluPerspective(设置投影矩阵)、gluLookAt(设置相机变换)和gluProject(将3D点投影到2D窗口坐标)。

0
return [((fol/v[2])*v[0]+x),((fol/v[2])*v[1]+y)];

点[0,0,1]将是x=0和y=0,除非您添加中心屏幕xy-它不是相机xy。fol是焦距,由fov角度和屏幕宽度推导出来的-三角形有多高(正切)。这种方法将无法匹配three.js透视矩阵,这就是为什么我在寻找它的原因。

我不应该寻找它。我在openGL上完美地匹配了xy,就像超级胶水一样!但是我无法使其在java中正常工作。那个完美的匹配如下。

var pmat = [0,0,0,0,0,0,0,0,0,0,
(farclip + nearclip) / (nearclip - farclip),-1,0,0,
2*farclip*nearclip / (nearclip - farclip),0 ];

void setpmat() {
  double fl; // = tan(dtor(90-fovx/aspect/2)); /// UNIT focal length
  fl = 1/tan(dtor(fov/Aspect/2)); ///  same number
  pmat[0]  = fl/Aspect;
  pmat[5]  = fl;
}
void fovmat(double v[],double p[]) {
  int cx = (int)(_Width/2),cy = (int)(_Height/2);
  double pnt2[4], pnt[4] = { 0,0,0,1 } ;
  COPYVECTOR(pnt,p);NORMALIZE(pnt);
  popmatrix4(pnt2,pmat,pnt);
  COPYVECTOR(v,pnt2);
  v[0] *= -cx; v[1] *= -cy;
  v[0] += cx; v[1] += cy;
}  // world to screen matrix
void w2sm(int xy[],double p[]) {
    double v[3]; fovmat(v,p);
    xy[0] = (int)v[0];
    xy[1] = (int)v[1];
}

我有另一种匹配three.js xy的方法,直到我让矩阵工作,只有一个条件。必须以2的宽高比运行。

function w2s(fol,v,x,y) {
    var a = width / height;
    var b =  height/width ;
    /// b = .5 // a = 2
    var f = 1/Math.tan(dtor(_fov/a)) * x * b;
    return [intr((f/v[2])*v[0]+x),intr((f/v[2])*v[1]+y)];
}

使用反转的相机矩阵,需要使用invert_matrix()函数。

    v = orbital(i);
    v = subv(v,campos);
    v3 = popmatrix(wmatrix,v); //inverted mat
    if (v3[2] > 0) {
    xy = w2s(flen,v3,cx,cy);

终于来了,(现在每个人都应该知道),无矩阵匹配,任何方面。

function angle2fol(deg,centerx) {
    var b = width / height;
    var a = dtor(90 - (clamp(deg,0.0001,174.0) / 2));
    return asa_sin(PI_5,centerx,a) / b;
}
function asa_sin(a,s,b) {
    return Math.sin(b) * (s / Math.sin(PI-(a+b)));
} // ASA solve opposing side of angle2 (b)
function w2s(fol,v,x,y) {
    return [intr((fol/v[2])*v[0]+x),intr((fol/v[2])*v[1]+y)];
}

更新了证明的图像。输入_fov会得到1.5倍,"大约"。为了正确查看FOV读数,请使用新的焦距重新绘制三角形。

function afov(deg,centerx) {
    var f = angle2fol(deg,centerx);
    return rtod(2 * sss_cos(f,centerx,sas_cos(f,PI_5,centerx)));
}
function sas_cos(s,a,ss) {
    return Math.sqrt((Math.pow(s,2)+Math.pow(ss,2))-(2*s*ss*Math.cos(a)));
} // Side Angle Side - solve length of missing side
function sss_cos(a,b,c) {
    with (Math) {
        return acos((pow(a,2)+pow(c,2)-pow(b,2))/(2*a*c));
    }
} // SSS solve angle opposite side2 (b)

Star library确认了视角,那么可以测量VIEW!http://innerbeing.epizy.com/cwebgl/perspective.jpg

我可以用一个词“岁差”来解释对月球北极进行90度校正。那么当前的上向量是什么?pnt?radec?

function ininorths() {
    if (0) {
        var c = ctime;
        var v = LunarPos(jdm(c));
        c += secday();
        var vv = LunarPos(jdm(c));
        vv = crossprod(v,vv);
        v = eyeradec(vv);
        echo(v,vv);
        v = [266.86-90,65.64]; //old
    }
    var v = [282.6425,65.8873]; /// new.
    // ...
}

我还没有解释两组向量:Three.milkyway.matrix 和 3D 到 2D 绘图。它们是:

function drawmilkyway() {
  var v2 = radec2pos(dtor(192.8595), dtor(27.1283),75000000);
  // gcenter 266.4168 -29.0078
  var v3 = radec2pos(dtor(266.4168), dtor(-29.0078),75000000);
 // ...
}
function initmwmat() {
    var r,u,e;
    e = radec2pos(dtor(156.35), dtor(12.7),1);
    u = radec2pos(dtor(60.1533), dtor(25.5935),1);
    r = normaliz(crossprod(u,e));
    u = normaliz(crossprod(e,r));
    e = normaliz(crossprod(r,u));
    var m = MilkyWayMatrix;
    m[0]=r[0];m[1]=r[1];m[2]=r[2];m[3]=0.0;
    m[4]=u[0];m[5]=u[1];m[6]=u[2];m[7]=0.0;
    m[8]=e[0];m[9]=e[1];m[10]=e[2];m[11]=0.0;
    m[12]=0.0;m[13]=0.0;m[14]=0.0;m[15]=1.0;
}
/// draw vectors and matrix were the same in C !
void initmwmat(double m[16]) {
  double r[3], u[3], e[3];
  radec2pos(e,dtor(192.8595), dtor(27.1283),1); //up
  radec2pos(u,dtor(266.4051), dtor(-28.9362),-1); //eye
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接