Sphere Intersection to world space point

129 views
Skip to first unread message

Wilson Stockman

unread,
Jan 25, 2013, 5:23:47 PM1/25/13
to os...@googlegroups.com
Hi,

I would like to do precise picking on a sphere surface, converting mouse X,Y to world space XYZ point so that I can draw object at that coord.
I have built this simple test snippet below that simply creates a box geo at the point on a scaled size globe where the mouse click ray intersection occurs and its nearly working!  The only thing that I have an issue is that intersection with the Y coordinate is reversed, also strangly I have to swap the Z coord of the mouse ( so ray_start = [x,y,1] and ray_dest = [x,y,0] otherwise its rejected as pointing backwards .  
So, that done clicking in the equator center of the sphere it plots the geo's nicely at the x and y moving orbit the camera left/right it works fine, but plotting above of the center screen creates geo points south or downward and clicking below center screen viceversa plots mirrored north .  Simply swapping the final vector does not work as its the intersection thats matters.  I know I am so close..  Just need a little more light I really appreciate some help if possible.

Thanks!

    var ray_start = [x,y,1];  //had to swap the Z so its cast forward

    var ray_dest  = [x,y,0];

    

    var matrix = osg.Matrix.makeIdentity();

    var w = osg.Matrix.copy(viewer.view.viewport.computeWindowMatrix(), []);

    var p = viewer.view.getProjectionMatrix();

    var m = viewer.view.getViewMatrix();

    osg.Matrix.preMult(matrix, w);

    osg.Matrix.preMult(matrix, p);

    osg.Matrix.preMult(matrix, m);

    

    var inv = [];

    var valid = osg.Matrix.inverse(matrix, inv);

    

    var ns = osg.Matrix.transformVec3(inv, ray_start, new Array(3));

    var ne = osg.Matrix.transformVec3(inv, ray_dest, new Array(3));

    

   //======Ray-Sphere intersection========

    var t0, t1; // parametric solutions for t if the ray intersects

    var radius2 = (6371* 6371);


    var ray_dir = osg.Vec3.normalize(osg.Vec3.sub(ns,ne, []) ,[]);

    var L = osg.Vec3.sub([0,0,0],ns,[]);

    var tca = osg.Vec3.dot(L,ray_dir);

    if (tca < 0) return false;   //wrong direction

    var d2 = osg.Vec3.dot(L,L) - (tca * tca);  //o2=h2-a2

    if (d2 > radius2) return false; //overshoots

    var thc = Math.sqrt(radius2 - d2);  //find the exit point

    t0 = tca - thc;

    t1 = tca + thc;

    //Draw a box at the world point    

    var hitPoint = osg.Vec3.add(ns, osg.Vec3.mult(ray_dir,t0,[]),[]);

    var geometry = osg.createTexturedBox(0, 0, 0, 1000,1000,1000);

    geometry.setNodeMask(0x2);  

    var xform     = new osg.MatrixTransform()

    xform.setMatrix(osg.Matrix.makeTranslate(hitPoint[0],hitPoint[1],hitPoint[2],[]));

    xform.addChild(geometry);

    root.addChild(xform);

Wilson Stockman

unread,
Jan 26, 2013, 6:07:03 AM1/26/13
to os...@googlegroups.com
Hey Guys,
Fixed it  For any interested please read.
Yes as I thought it was the the viewport transformation to NDC (normalized device coordinate ) coordinates
multing computeWindowMatrix() yields inverted coords

Code changed to this fixed it.


ray_start[0] = (x / viewer.view.viewport.width()) * 2 - 1;

ray_start[1] = -(y / viewer.view.viewport.height()) * 2 + 1;

ray_start[2] = 1;

ray_dest[0] = (x / viewer.view.viewport.width()) * 2 - 1;

ray_dest[1] = -(y / viewer.view.viewport.height()) * 2 + 1;

ray_dest[2] = 0;


Cheers,
Wilson

Cedric Pinson

unread,
Jan 26, 2013, 6:09:32 AM1/26/13
to os...@googlegroups.com
Hi,
Could you add a small test case in order avoid regression in test/ ?

Great.

Thank you for the report

Cedric Pinson
Provide OpenGL, WebGL services
+33 659 598 614 - 
http://cedricpinson.com - http://osgjs.org - http://sketchfab.com

--
 
 

Wilson Stockman

unread,
Jan 28, 2013, 8:45:39 AM1/28/13
to os...@googlegroups.com
Hi Cedric,

I extracted the xy->world coords and sphere code, here just for proof of concept, drop this code below into you basic sandbox scene with a camera scale the camera or as needed (or make the radius fit) I used osg.createTexturedSphere(radius, 64, 64); with a 40% alpha to visualise the sphere.

  
osgUtil.IntersectVisitor.prototype.intersectSegmentWithSphere: function(start, end, bsphere) {

    var t0, t1; // parametric solutions for t if the ray intersects

    var radius2 = bsphere.radius2();

    var ray_dir = osg.Vec3.normalize(osg.Vec3.sub(end,start, []) ,[]);

    var L = osg.Vec3.sub(bsphere.center(),start,[]);

    var tca = osg.Vec3.dot(L,ray_dir);

    if (tca < 0) return false;   //wrong direction

    var d2 = osg.Vec3.dot(L,L) - (tca * tca);  //o2=h2-a2

    if (d2 > radius2) return false//overshoots

    var thc = Math.sqrt(radius2 - d2);  //find the exit point

    t0 = tca - thc;

    t1 = tca + thc;

    return {t0:t0,t1:t1};

  }


osgUtil.screenToRay = function(x, y, view) {

  var ray_start = [];

  var ray_dest  = [];

  ray_start[0] =  (x / view.viewport.width())  * 2 - 1;

  ray_start[1] = -(y / view.viewport.height()) * 2 + 1;

  ray_start[2] = 0;

  

  ray_dest[0] =  (x / view.viewport.width())  * 2 - 1;

  ray_dest[1] = -(y / view.viewport.height()) * 2 + 1;

  ray_dest[2] = 1;

  

  var matrix = osg.Matrix.makeIdentity();

  var p = view.getProjectionMatrix();

  var m = view.getViewMatrix();

  osg.Matrix.preMult(matrix, p);

  osg.Matrix.preMult(matrix, m);  

  var inv = [];

  var valid = osg.Matrix.inverse(matrix, inv);

  var start = osg.Matrix.transformVec3(inv, ray_start, new Array(3));

  var end = osg.Matrix.transformVec3(inv, ray_dest, new Array(3));

  return {

    start:start,

    end:end,

    dir:osg.Vec3.normalize(osg.Vec3.sub(end,start, []) ,[])

  }

}

//TEST sphere intersection

var world_ray = osgUtil.screenToRay(x, y, viewer.view);

var ray_dir = world_ray.dir;

var bs = new osg.BoundingSphere();

bs.set([0,0,0],6371000);

var iv = new osgUtil.IntersectVisitor();

var is = iv.intersectSegmentWithSphere(world_ray.start,world_ray.end,bs);

if(is){

var hitPoint = osg.Vec3.add(world_ray.start, osg.Vec3.mult(ray_dir,is.t0,[]),[]);

var geometry = osg.createTexturedSphere(400000, 64, 64);

geometry.setNodeMask(0x2);  

var xform     = new osg.MatrixTransform()

xform.setMatrix(osg.Matrix.makeTranslate(hitPoint[0],hitPoint[1],hitPoint[2],[]));

xform.addChild(geometry);

root.addChild(xform);

}


So thats straight forward sphere intersection code extracted for ease of testing. I have updated osgUtils so it fits into intersectVisitor pipeline and also did some mods to the tri intersection.  If you are interested in that I have pasted as a gist.

https://gist.github.com/4655539

Updated:

- intersectSegmentWithSphere  (returns intersection t, can be used with direct detection)

- compact moller triangle intersection test http://www.cs.lth.se/home/Tomas_Aken...ubs/tritri.pdf

- applyNode (IntersectVisitor) Added pre triangle test a bounding sphere test, Also the fix for the NDC mouse coords. 

Hope this is of use.

Cheers,

Wilson
Reply all
Reply to author
Forward
0 new messages