Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting touch position on 3D objects in openGL

I have created a 3D object in opengl for one of my application. The object is something like a human body and can be rotated on touch. How can I detect the position of touch on this 3D object. Means if the user touches the head, I have to detect that it is the head. If touch is on the hand, then that has to be identified. It should work even if the object is rotated to some other direction. I think the coordinates of touch on the 3D object is required.
This is the method where I am getting the position of touch on the view.

- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint location  = [touch locationInView: self];
    m_applicationEngine->OnFingerDown(ivec2(location.x, location.y));
}

Can anyone help? Thanks in advance!

like image 744
iOS Developer Avatar asked Aug 08 '13 12:08

iOS Developer


2 Answers

Forget about RayTracing and other Top Notch Algorithms. We have used a simple trick for one of our applications(Iyan 3D) on App Store. But this technique need one extra render pass everytime you finish rotating the scene to a new angle. Render different objects (head, hand, leg etc) in different colors (not actual colors but unique ones). Read the color in the rendered image corresponding to the screen position. You can find the object based on its color.

In this method you can use change rendered image resolution to balance accuracy and performance.

like image 77
codetiger Avatar answered Nov 15 '22 11:11

codetiger


To determine the 3D location of the object I would suggest ray tracing.

Assuming the model is in worldspace coordinates you'll also need to know the worldspace coordinates of the eye location and the worldspace coordinates of the image plane. Using those two points you can calculate a ray which you will use to intersect with the model, which I assume consists of triangles.

Then you can use the ray triangle test to determine the 3D location of the touch, by finding the triangle that has the closest intersection to the image plane. If you want which triangle is touched you will also want to save that information when you do the intersection tests.

This page gives an example of how to do ray triangle intersection tests: http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-9-ray-triangle-intersection/ray-triangle-intersection-geometric-solution/

Edit:

Updated to have some sample code. Its some slightly modified code I took from a C++ raytracing project I did a while ago so you'll need to modify it a bit to get it working for iOS. Also the code in its current form wouldn't even be useful since it doesn't return the actual intersection point but rather if the ray intersects the triangle or not.

// d is the direction the ray is heading in
// o is the origin of the ray
// verts is the 3 vertices of the triangle
// faceNorm is the normal of the triangle surface
bool
Triangle::intersect(Vector3 d, Vector3 o, Vector3* verts, Vector3 faceNorm)
{
    // Check for line parallel to plane
    float r_dot_n = (dot(d, faceNorm));

    // If r_dot_n == 0, then the line and plane are parallel, but we need to 
    // do the range check due to floating point precision
    if (r_dot_n > -0.001f && r_dot_n < 0.001f)
        return false;

    // Then we calculate the distance of the ray origin to the triangle plane
    float t = ( dot(faceNorm, (verts[0] - o)) / r_dot_n);
    if (t < 0.0)
        return false;

    // We can now calculate the barycentric coords of the intersection
    Vector3 ba_ca = cross(verts[1]-verts[0], verts[2]-verts[0]);
    float denom = dot(-d,  ba_ca);

    dist_out = dot(o-verts[0], ba_ca) / denom;
    float b = dot(-d, cross(r.o-verts[0], verts[2]-verts[0])) / denom;
    float c = dot(-d, cross(verts[1]-verts[0], o-verts[0])) / denom;

    // Check if in tri or if b & c have NaN values
    if (  b < 0 || c < 0 || b+c > 1 || b != b || c != c)
        return false;

    // Use barycentric coordinates to calculate the intersection point
    Vector3 P = (1.f-b-c)*verts[0] + b*verts[1] + c*verts[2];

    return true;
}

The actual intersection point you would be interested in is P.

like image 38
illeyezur Avatar answered Nov 15 '22 11:11

illeyezur