Picking in 3D through ray tracing method

Hello,

I have been trying to write a code for being able to pick objects or select points in 3D by using a ray tracing method, based on the guidelines I read from this source: http://schabby.de/picking-opengl-ray-tracing/

However, I have no idea how to check intersection between the ray and the object in the scene, and also I am not sure that the code is 100% correct. I am posting the code below. If anyone could help it would be great!

import peasy.*;
import toxi.geom.*;

float x; 
float y;

// -- camera parameters ---------
PeasyCam cam;
Vec3D camTarget;
Vec3D camPosition;
Vec3D view = new Vec3D();
Vec3D camHorizontal = new Vec3D();
Vec3D camVertical = new Vec3D();
Vec3D pos;
Vec3D dir;
float fov = 1; //PI/5
float aspect = float(width)/float(height);
float nearClip = 1;
float farClip = 100000;

Cube cube0;

// ------------------------------

void setup() {
  size(800, 800, OPENGL);
  createCamera();
  Vec3D start = new Vec3D(0, 0, 0);
  cube0 = new Cube(start, 30);
}

// ------------------------------

void draw() {
  smooth();
  background(255);
  cube0.run();
  raytracing();
  testIntersection();
}

// ------------------------------

void createCamera() {
  cam = new PeasyCam(this, 300);
  perspective(fov, aspect, nearClip, farClip);
}

// ------------------------------

void raytracing() {
  x = mouseX;
  y = mouseY;

  // --- get camera target position
  float a[] = cam.getLookAt();
  float a1 = a[0];
  float a2 = a[1];
  float a3 = a[2];
  camTarget = new Vec3D(a1, a2, a3);

  // --- get camera position
  float b[] = cam.getPosition();
  float b1 = b[0];
  float b2 = b[1];
  float b3 = b[2];
  camPosition = new Vec3D(b1, b2, b3);

  // --- get view
  view = camTarget.sub(camPosition);
  view.normalize();

  // -- get cameraUp vector
  Vec3D Zaxis = new Vec3D(0, 0, 1);
  Vec3D rotAxis = view.cross(Zaxis);
  float theta = -PI/2;
  Vec3D camUp = view.getRotatedAroundAxis(rotAxis, theta);

  // -- calculate screenHorizontally and screenVertically
  camHorizontal = view.cross(camUp);
  camHorizontal.normalize();
  camVertical = camHorizontal.cross(view);
  camVertical.normalize();

  float vLength = tan(fov/2)*nearClip;
  float hLength = vLength*aspect;
  camVertical.scaleSelf(vLength);
  camHorizontal.scaleSelf(hLength);

  // translate mouse coordinates so that the origin lies in the center    
  x -= width/2;
  y -= height/2;  
  x /= (width/2);
  y /= (height/2);

  // compute intersection of picking ray with viewport plane
  float posX = camPosition.x + view.x*nearClip + camHorizontal.x*x + camVertical.x*y;
  float posY = camPosition.y + view.y*nearClip + camHorizontal.y*x + camVertical.y*y;
  float posZ = camPosition.z + view.x*nearClip + camHorizontal.z*x + camVertical.z*y;
  pos = new Vec3D(posX, posY, posZ);
  dir = pos.sub(camPosition);
}

// ------------------------------

void testIntersection() {
}

There is also a simple cube class just for displaying and getting the location vector of the cube. Thank you very much for your help! :)

Answers

  • Is this old thread that discusses the same resource relevant?

  • yes it's the same question but the source that he is using as his solution is the one that I cannot fully understand, so it doesn't help me in any way

  • would you like to try exploring this example?

    Probably you can implement Peasy Cam. By the way, peasy cam doesn't have an example to use 3D picking?

    I think the solution mostly used is to check the position within a plane parallel to the camera plane (in the example the plane is not parallel, but fixed at ground)

    I have implemented a method on this code to detect mouseOver function over 3D vertices. But that means looping through a model vertices and check if the 3d position of that vertex in window coordinates matches.

    //from http://d.hatena.ne.jp/kougaku-navi/20160102/p1
    
    void setup() {
      size( 500, 500, P3D);
    }
    
    void draw() {
      background(200);
    
      // camera view
      camera( 1160, -1960, 1730, 890, -1200, 1200, 0, 1, 0 );
    
      // Calculate the coordinates on the floor corresponding to the cursor position
      PVector floorPos = new PVector( 500, 300, 100 ); 
      PVector floorDir = new PVector( 0, -1, 0 );      
      PVector mousePos = getUnProjectedPointOnFloor( mouseX, mouseY, floorPos, floorDir );
    
      // floor
      pushMatrix();
        translate( floorPos.x, floorPos.y, floorPos.z );
        fill(255);
        box( 2000, 1, 2000 );
      popMatrix();
    
      // Draw a cube at the cursor position
      pushMatrix();
        translate( mousePos.x, mousePos.y, mousePos.z );
        fill(255, 0, 0);
        box(200);
      popMatrix();
    }
    
    // Function that calculates the coordinates on the floor surface corresponding to the screen coordinates
    PVector getUnProjectedPointOnFloor(float screen_x, float screen_y, PVector floorPosition, PVector floorDirection) {
    
      PVector f = floorPosition.get(); // Position of the floor
      PVector n = floorDirection.get(); // The direction of the floor ( normal vector )
      PVector w = unProject(screen_x, screen_y, -1.0); // 3 -dimensional coordinate corresponding to a point on the screen
      PVector e = getEyePosition(); // Viewpoint position
    
      // Computing the intersection of  
      f.sub(e);
      w.sub(e);
      w.mult( n.dot(f)/n.dot(w) );
      w.add(e);
    
      return w;
    }
    
    // Function to get the position of the viewpoint in the current coordinate system
    PVector getEyePosition() {
      PMatrix3D mat = (PMatrix3D)getMatrix(); //Get the model view matrix
      mat.invert();
      return new PVector( mat.m03, mat.m13, mat.m23 );
    }
    //Function to perform the conversion to the local coordinate system ( reverse projection ) from the window coordinate system
    PVector unProject(float winX, float winY, float winZ) {
      PMatrix3D mat = getMatrixLocalToWindow();  
      mat.invert();
    
      float[] in = {winX, winY, winZ, 1.0f};
      float[] out = new float[4];
      mat.mult(in, out);  // Do not use PMatrix3D.mult(PVector, PVector)
    
      if (out[3] == 0 ) {
        return null;
      }
    
      PVector result = new PVector(out[0]/out[3], out[1]/out[3], out[2]/out[3]);  
      return result;
    }
    
    //Function to compute the transformation matrix to the window coordinate system from the local coordinate system
    PMatrix3D getMatrixLocalToWindow() {
      PMatrix3D projection = ((PGraphics3D)g).projection; 
      PMatrix3D modelview = ((PGraphics3D)g).modelview;   
    
      // viewport transf matrix
      PMatrix3D viewport = new PMatrix3D();
      viewport.m00 = viewport.m03 = width/2;
      viewport.m11 = -height/2;
      viewport.m13 =  height/2;
    
      // Calculate the transformation matrix to the window coordinate system from the local coordinate system
      viewport.apply(projection);
      viewport.apply(modelview);
      return viewport;
    }
    

    mouseOver check:

    //test of mouse is over a 3D point within tolerance 
    public boolean mouseOver3DPoint(PVector pointPosition) {
      int mouseOverTol = 5; //pixels
      boolean result = false;
      PVector vp = getProjectedCoord (pointPosition, getMatrixLocalToWindow());
      if (vp.x >mouseX-mouseOverTol && vp.x <mouseX+mouseOverTol &&
        vp.y >mouseY-mouseOverTol && vp.y <mouseY+mouseOverTol) {
        result  = true;
      }
      return result;
    }
    
Sign In or Register to comment.