1
votes

In openGL, I have a 3D model I'm performing a ray-triangle intersection on, using the code explained in the paper "Fast, Minimum Storage Ray/Triangle Intersection" ( http://jgt.akpeters.com/papers/MollerTrumbore97/ ).

My cursor position is unprojected into world space using the following code:

bool SCamera::unproject(Vector3 input, Vector3 & output){

  GLint viewport[4]; 
  glGetIntegerv(GL_VIEWPORT,viewport); //Grab screen info

  float x = input.mX; 
  float y = input.mY;
  float z = input.mZ;

  Matrix4 P, Mv, res;

  P = getProjection();
  Mv = getCameraTransform();

  Vector3 N; //Cursor point translated to having 0,0 at screen center
  N.mX = ((x-viewport[0]) / viewport[2])*2 - 1;
  N.mY = ((y-viewport[1]) / viewport[3])*2 - 1;
  N.mZ = z*2-1;

  res = P * Mv; //Multiply P * Mv to get transform
  Vector3 w = res.inverse() * N; //Apply transform to N.

  output.mX = w[0];
  output.mY = w[1];
  output.mZ = w[2];
  return true;
}

After that, I form a ray by doing the following:

unproject(Vector3(xClick, yClick,0),resultUnproject)
ray.origin = cameraPosition;
ray.direction = resultUnproject - ray.origin;
ray.direction.normalize();

Now, finally I'm trying to run this ray through the triangle code (linked above), but I can't seem to transform it right. My current attempt is as follows:

Matrix4 mview, T;
mview = getModelview();
T = mview.inverse();
ray.origin = T*ray.origin;
ray.direction = T*ray.direction;
ray.direction.normalize();

For some reason, this doesn't work. Am I forming my ray wrong? Or transforming it wrong?

4

4 Answers

3
votes

One method might be:

Take your ray origin, and work out a point at a unit distance in the direction of the ray in world space. Then, do your transformation to both points - i.e. multiply by the inverse of the transformation matrix. You can then determine the new ray direction from the difference between the two translated points. Checking if this direction is different to the original will quickly tell you whether you are missing a step.

3
votes

My solution to this was to do what sje397 suggested but just use the result as your new ray.

So in my project (which is XNA), given ray unprojected from the cursor:

rayTransform = Matrix.Invert(model_transform);

//Transform ray (or more accurately its defining components into a new ray)
Vector3 v1 = Vector3.Transform(ray.Position, rayTransform);
Vector3 v2 = Vector3.Transform((ray.Position + ray.Direction), rayTransform);
transformedray = new Ray(v1, v2 - v1);

model_transform here is generated the same way as the transform for the shader (i.e. no swapping World/Local multiplications or anything)

This works fine in my project with the target of the picking being rotated, transformed and scaled in all sorts of ways.

Also, you may find this a little impertient but if you are still having trouble are you sure the code to unproject the cursor and generate the picking ray works right? I would guess it would have to if your triangle picking code works, its just it looks to me like its getting the ray in a 'round about' way.

In my project I take two points, one 'on' the viewport 'plane' so Vector3(cursorx,cursory,0) this again but advanced on the Z axis Vector3(cursorx,cursory,1). Unproject both these points, the result of the first is your origin and the difference is your direction.

Good luck!

0
votes

So I finally got this working, though I'm not satisfied with how. In my case individual model rotation is applied as a transform before rendering, so I ended up having to transformed the target triangle by the modelview matrix.

What I was trying to do above was compute a ray that would serve the same purpose, by moving the ray's origin around the object so it's as if the object had been rotated. Unfortunately, I couldn't quite get the math right.

The end result means one transform per triangle, instead of one per ray. Luckily enough it doesn't seem to be that bad of a performance hit, as I'm only using this for picking.

0
votes

If you have some ray (already transformed to world space) and you want to check if it intersects an object/triangle, you need to multiply it by object's inverted matrices. Here's some pseudocode:

NewRayPosition = Vector3.Transform(OldRay.Position, Matrix.Invert(Transformation);
NewRayDirection = Vector3.Transform(OldRay.Direction, Matrix.Invert(Rotation*Scale));
NewRayDirection.Normalize();

Multiply ray position by Translation, Scale and Rotation, but direction only by Scale and Rotation. Doing it the way you did it is unacceptable because of the performance reason. More here: http://www.cl.cam.ac.uk/teaching/1999/AGraphHCI/SMAG/node2.html#SECTION00024100000000000000