简体   繁体   中英

Perspective Projection: determine the 2D screen coordinates (x,y) of points in 3D space (x,y,z)

I wish to determine the 2D screen coordinates (x,y) of points in 3D space (x,y,z).

The points I wish to project are real-world points represented by GPS coordinates and elevation above sea level.

For example: Point (Lat:49.291882, Long:-123.131676, Height: 14m)

The camera position and height can also be determined as ax,y,z point. I also have the heading of the camera (compass degrees), its degree of tilt (above/below horizon) and the roll (around the z axis).

I have no experience of 3D programming, therefore, I have read around the subject of perspective projection and learnt that it requires knowledge of matrices, transformations etc - all of which completely confuse me at present.

I have been told that OpenGL may be of use to construct a 3D model of the real-world points, set up the camera orientation and retrieve the 2D coordinates of the 3D points.

However, I am not sure if using OpenGL is the best solution to this problem and even if it is I have no idea how to create models, set up cameras etc

Could someone suggest the best method to solve my problem? If OpenGL is a feasible solution i'd have to use OpenGL ES if that makes any difference. Oh and whatever solution I choose it must execute quickly.

Here's a very general answer. Say the camera's at (Xc, Yc, Zc) and the point you want to project is P = (X, Y, Z). The distance from the camera to the 2D plane onto which you are projecting is F (so the equation of the plane is Z-Zc=F). The 2D coordinates of P projected onto the plane are (X', Y').

Then, very simply:

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

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

If your camera is the origin, then this simplifies to:

X' = X * (F/Z)

Y' = Y * (F/Z)

You do indeed need a perspective projection and matrix operations greatly simplify doing so. I assume you are already aware that your spherical coordinates must be transformed to Cartesian coordinates for these calculations.

Using OpenGL would likely save you a lot of work over rolling your own software rasterizer . So, I would advise trying it first. You can prototype your system on a PC since OpenGL ES is not too different as long as you keep it simple.

If just need to compute coordinates of some points, you should only need some algebra skills, not 3D programming with openGL.

Moreover openGL does not deal with Geographic coordinates

First get some doc about WGS84 and geodesic coordinates, you have first to convert your GPS data into a cartesian frame ( for instance the earth centric cartesian frame in which is defined the WGS84 ellipsoid ).

Then the computations with matrix can take place. The chain of transformations is roughly:

  • WGS84
  • earth centric coordinates
  • some local frame
  • camera frame
  • 2D projection

For the first conversion see this The last involves a projection matrix The others are only coordinates rotations and translation. The "some local frame" is the local cartesian frame with origin as your camera location tangent to the ellipsoid.

I'd recommend "Mathematics for 3D Game Programming and Computer Graphics" by Eric Lengyel. It covers matrices, transformations, the view frustum, perspective projection and more.

There is also a good chapter in The OpenGL Programming Guide (red book) on viewing transformations and setting up a camera (including how to use gluLookAt).

If you aren't interested in displaying the 3D scene and are limited to using OpenGL ES then it may be better to just write your own code to do the mapping from 3D to 2D window coords. As a starting point you could download Mesa 3D , an open source implementation of OpenGL, to see how they implement gluPerspective (to set a projection matrix), gluLookAt (to set a camera transformation) and gluProject (to project a 3D point to 2D window coords).

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

Point at [0,0,1] will be x=0 and y=0, unless you add center screen xy - it's not camera xy. fol is focal length, derived from fov angle and screen width - how high is the triangle (tangent). This method will not match three.js perspective matrix, which is why am I looking for that.

I should not be looking for it. I matched xy on openGL, perfectly like super glue. But I cannot get it to work right in java. THAT Perfect match follows.

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];
}

I have one more way to match three.js xy, til I get the matrix working, just one condition. must run at Aspect of 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)];
}

Use it with the inverted camera matrix, you will need 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);

Finally here it is, (everyone ought to know by now), the no-matrix match, any aspect.

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

Updated the image for the proof. Input _fov gets you 1.5 that, "approximately." To see the FOV readout correctly, redo the triangle with the new focal length.

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 confirmed the perspective, then possible to measure the VIEW! http://innerbeing.epizy.com/cwebgl/perspective.jpg

I can explain the 90 deg correction to moon's north pole in one word precession. So what is the current up vector. 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.
    // ...
}

I have yet to explain the TWO sets of vectors: Three.milkyway.matrix and the 3D to 2D drawing. They ARE:

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM