Ray Picking

Started by Daslee, January 02, 2013, 18:45:08

Previous topic - Next topic

abcdef

You are correct

return farVector.subtractVector(nearVector).normalise();


returns a vector (x,y,z). This is essentially describes a line with the line going through (0,0,0) and (x,y,z)

With each object you render you need to do a line intersection test with the line being the one above. Your object may have a simple boundary shape (Cube or Sphere) in which case it's just an intersection test with that.

Some example intersection logic for a sphere can be found

http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection

And for triangles

http://geomalgorithms.com/a06-_intersect-2.html

There are lots of pages on the web to help you here, just find what best suits what you are doing

Daslee

This also not working correctly. Maybe my line - box intersection code wrong, or your code giving bad positions. getPickingRay gives this coordinates:
0.004095578758544765, -0.7583613829642083, -0.6518214779088259
0.004164626369625344, -0.7487740152381042, -0.6628122886544945


They will never be more than 1, just X sometimes going to 9. And I'm using this code for intersection: http://www.3dkingdoms.com/weekly/weekly.php?a=3
And also tried this one: http://www.3dkingdoms.com/weekly/weekly.php?a=21 Both picks same objects.

Also maybe I converted intersection code wrong, but I think that problem is in getPickingRay, because coordinates will never be more than 1 or never equals to 1.

abcdef

They are never more that one because they are normalised, your "ray" is t*(vector) where t can be anything. The vector is just a point on the line and as the line passes through (0,0,0) any multiple of the vector is also on the line.

Daslee

If it will never be more than 1, so it picks objects which x, y, z is around 1. So how to do that it can pick all objects?

abcdef


Daslee

So okay, I readed that article and tried to translate code to Java. This is what I got:
public boolean rayIntersection(Ray r, Object obj){		
		float tmin = (obj.min.x - r.begin.x()) / r.dest.x();
	    float tmax = (obj.max.x - r.begin.x()) / r.dest.x();
	    if(tmin > tmax){
	    	float temp = tmin;
	    	tmin = tmax;
	    	tmax = temp;
	    }
	    float tymin = (obj.min.y - r.begin.y()) / r.dest.y();
	    float tymax = (obj.max.y - r.begin.y()) / r.dest.y();
	    if(tymin > tymax){
	    	float temp = tymin;
	    	tymin = tymax;
	    	tymax = temp;
	    }
	    if((tmin > tymax) || (tymin > tmax))
	    	return false;
	    if(tymin > tmin)
	    	tmin = tymin;
	    if(tymax < tmax)
	        tmax = tymax;
	    float tzmin = (obj.min.z - r.begin.z()) / r.dest.z();
	    float tzmax = (obj.max.z - r.begin.z()) / r.dest.z();
	    if(tzmin > tzmax){
	    	float temp = tzmin;
	    	tzmin = tzmax;
	    	tzmax = temp;
	    }
	    if((tmin > tzmax) || (tzmin > tmax))
	    	return false;
	    if(tzmin > tmin)
	    	tmin = tzmin;
	    if(tzmax < tmax)
	    	tmax = tzmax;
	    
	    if((tmin > r.tmax) || (tmax < r.tmin)) return false;
	    if(r.tmin < tmin) r.tmin = tmin;
	    if(r.tmax > tmax) r.tmax = tmax;
	    return true;
	}


And how I'm using it:
for(Object obj : world.objects) obj.selected = false;
Vector3 destination = getPickingRay(Display.getWidth() / 2, Display.getHeight() / 2);
		Ray rayPick = new Ray(new Vector3(control.position.x, control.position.y, control.position.z), destination);
		for(Object obj : world.objects){
			if(rayIntersection(rayPick, obj)){
				obj.selected = true;
				break;
			}
		}


Now it picks objects, but again inaccuracy.  :-\

Also I didn't know how to actually calculate box min and max values, so this is how I'm calculating:
float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
		if(x + width < minx) minx = x + width;
		if(x < minx) minx = x;
		if(y + height < miny) miny = y + height;
		if(y < miny) miny = y;
		if(z + depth < minz) minz = z + depth;
		if(z < minz) minz = z;
		
		float maxx = 0, maxy = 0, maxz = 0;
		if(x > maxx) maxx = x;
		if(x + width > maxx) maxx = x + width;
		if(y > maxy) maxy = y;
		if(y + height > maxy) maxy = y + depth;
		if(z > maxz) maxz = z;
		if(z + depth > maxz) maxz = z + depth;
		
		min = new Vector3f(minx, miny, minz);
		max = new Vector3f(maxx, maxy, maxz);


Maybe this is wrong?

abcdef

Don't really know whay its not working

But in

Ray rayPick = new Ray(new Vector3(control.position.x, control.position.y, control.position.z), destination);

shouldn't you use 0,0,0 as the origin?

Also the article is for simple boxes that align with axes, you need to make sure that occurs

Daslee

No, if I set origin to 0, 0, 0 then it doesn't works because it's from where ray must be casted, so from camera x,y,z to mouse x,y,z. And in my game mouse is always grabbed (Mouse.setGrabbed(true);), so instead of getting mouse x,y,z I'm using to get x,y,z from crosshair (screen center).

EDIT: Again checked at rotation of yaw at > 90 and < 270, so it works almost good, there is just inaccurate per 1 cube. If many getPickingRay methods give almost same results, so maybe problem in my camera class or somewhere other?

Daslee

No more ideas how to fix this problem?  :-[

abcdef

I have verified the ray creation method by implementing it in a different way (not using any opengl calls), your problem is probably in your ray intersection method. You also need to work out if you are only checking positive distance items from camera as negative distances will be behind you

Daslee

Quote from: abcdef on January 21, 2013, 11:10:25
I have verified the ray creation method by implementing it in a different way (not using any opengl calls), your problem is probably in your ray intersection method. You also need to work out if you are only checking positive distance items from camera as negative distances will be behind you

Ray picking is a bit hard to me as for beginner. So now I'm using color picking, it's way easier.  :)

Daslee

Damn.. I think I'll get back to ray picking, because color picking has too low fps.

abcdef,
Now as you said:
QuoteYou also need to work out if you are only checking positive distance items from camera as negative distances will be behind you

I noticed that when I looking to objects which x/z position are negative, then ray picking goes wrong. But with yaw rotation picking is good, only rotating pitch it goes wrong. Maybe in making picking ray I must multiply some vectors with pitch rotation or something like that?

EDIT: I was playing with ray picking in morning and as I said it works good at 90-180 of yaw rotation, so now found way how to fix between 270-360 yaw rotation, just when it gets picking ray subtract begin vector's Y axis by 8 and works good. But now problem in yaw rotation between 0-90 and 180-270

Here is code how I'm doing now:
Vector3 destination = getPickingRay(Display.getWidth() / 2, Display.getHeight() / 2);
				Ray rayPick = new Ray(new Vector3(control.position.x, control.position.y, control.position.z), destination);
				if(control.yaw >= 270 && control.yaw <= 360) rayPick.begin.xyz[1] -= 8; //This is what about I'm talking.


Maybe this is not effective way to fix it, but I can't found any other solutions. :D

abcdef

One thing you should note, if you move your camera (+x,0,0) in the x directions, your actual position in the real world is (-x,0,0) in the x direction as moving all the objects forward is the same as moving yourself backwards. This is the same for the y and z direction too.

Also when you rotate your camera 75 degrees around the x axis your actual rotation is -75 degrees around the xaxis as rotating objects around the x direction is the same as roatating the camera in the opposite direction.

This is important when keeping a log of your current position for which the ray starts.

Something you should try and do is to draw a line from your current position in the ray direction, this ay you can test your ray is correct

Assuming its correct and you aren't getting a successful hit on your ray object collision testing, then there is some thing wrong with your collission testing.

You should check out the lighthouse3d tutorial on ray picking as it gives you sample tests for different shapes.

You having issues with it working on some angles and not others tells me your ray is not 100% correct

Daslee

Quote from: abcdef on January 28, 2013, 09:47:37
Something you should try and do is to draw a line from your current position in the ray direction, this ay you can test your ray is correct

I tried this, but it's hard to see where line starts, where line ends.. Here is image how it looks: http://img651.imageshack.us/img651/548/screenshot20130128.png

And code how I drawing:
Vector3 destination = getPickingRay(Display.getWidth() / 2, Display.getHeight() / 2);
				Ray rayPick = new Ray(new Vector3(control.position.x, control.position.y, control.position.z), destination);
				glDisable(GL_TEXTURE_2D);
					glBegin(GL_LINES);
						glColor3f(1f, 1f, 1f);
						glVertex3f(rayPick.begin.x(), rayPick.begin.y(), rayPick.begin.z());
						glColor3f(.3f, .3f, .3f);
						glVertex3f(rayPick.dest.x(), rayPick.dest.y(), rayPick.dest.z());
					glEnd();
				glEnable(GL_TEXTURE_2D);

abcdef

I can't see your picture at the moment as that site is blocked at work.

Quick question though, how do you work out your destination?

The definition of a line can be thought of as

p + t * d

p = vector of start point
d = direction vector of ray
t = scalar

if you choose a value for t, say 10,000 then you can draw a line from p to p+(10000*d). That will represent your ray.