Jump to content
  • Advertisement
Sign in to follow this  
mako_5

Trouble with pick rays in OpenGL ES

This topic is 2807 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm using OpenGL ES and it looks like I need to write my own pick rays. I'm taking the pick ray and trying to intersect with the xz-plane.

I'm not sure if I'm not making the pick ray correctly or doing the intersect test correctly, or both are messed up. I'd really appreciate anyone who would look at this. Please let me know if there is any more code you need to see.

Here's my algorithm idea for making the pick ray:
  1. Take picked screen coordinates and convert to normalized device coordinates (-1,-1 to 1,1)?.
  2. Use the frustum right and top bounds and the camera's left and up vectors (parallel to the view plane normal) to get an actual coordinate on the near plane.
  3. Draw the ray from the camera's eye through this position.
  4. Multiply the ray by the inverse camera transform to convert to world coordinates.

This is the test which doesn't work correctly
ray3 pickRay = camera.makePickRay(screenX, screenY);
// Try to intersect with y-plane
yIntersect = pickRay.intersectPlanePoint(vec3(0,1,0),0);




For making the pick ray
ray3
S4Camera::makePickRay(int iScreenPixelX_, int iScreenPixelY_)
{
// n is away from the center...
// u is left vector

// Y-pixels are inverted;
float pw = iScreenPixelX_;
float ph = m_iFilmHeight - iScreenPixelY_;


// Convert to NDC?
// The screen dimensions are film dimensions
float x = 2 * pw / m_iFilmWidth - 1.0f;
float y = 2 * ph / m_iFilmHeight - 1.0f;


// Convert to world coordinates
vec3 pickDirection = -m_frustum.right*x*u + m_frustum.top*y*v + -m_frustum.near*n;
ray3 pickRay(m_eye, pickDirection);

// Multiply by inverse camera transform
pickRay.multiply(m_matrix);
return pickRay;
}




Here's how I'm making the camera transform:
void
S4Camera::makeMatrix()
{
// n = viewplane normal
// v = up
// u = left

n = m_eye - m_center;
u = m_up.cross(n);
v = n.cross(u);

n.normalize();
u.normalize();
v.normalize();

mat4 &M = m_matrix;
M[0] = u.x;
M[4] = u.y;
M[8] = u.z;
M[12] = 0.0f;

M[1] = v.x;
M[5] = v.y;
M[9] = v.z;
M[13] = 0.0f;

M[2] = n.x;
M[6] = n.y;
M[10] = n.z;
M[14] = 0.0f;

M[3] = 0.0f;
M[7] = 0.0f;
M[11] = 0.0f;
M[15] = 1.0f;

mat4 translation = mat4::translation(-m_eye);
M *= translation;
}




How I'm doing ray multiplication:

void ray3::multiply(const mat4& transform_)
{
origin.multiplyAsPoint(transform_); // multiplies as (x,y,z,1)
direction.multiplyAsVector(transform_); // multiplies as (x,y,z,0)
}



And how I'm doing plane testing:

vec3 intersectPlanePoint(const vec3 &planeNormal_, float planeConstant_)
{
assert(intersectsPlane(planeNormal_, planeConstant_));

float t = (-planeConstant_ - planeNormal_.dot(origin)) /
(planeNormal_.dot(direction));

return at(t);
}

Share this post


Link to post
Share on other sites
Advertisement
Solved the problem. I've posted the correct code for anyone else trying to make pick rays.

Multiplying by camera inverse transform takes to eye coordinates, but the pick ray needs to stay in world coordinates. Also, I mistook u for the camera's left vector, when it was the camera's right vector.
The crucial error was that I also forgot the absolute value conversion in myintersectsPlane function:

bool intersectsPlane(const vec3 &planeNormal_, float planeConstant_)
{
return fabs(planeNormal_.dot(direction)) >= S4_RAY_PRECISION;
}



Real algorithm:
  1. Take picked screen coordinates and convert to normalized device coordinates (-1,-1 to 1,1).
  2. Use the frustum right and top bounds and the camera's left and up vectors (parallel to the view plane normal) to get an actual coordinate on the near plane.
  3. Draw the ray from the camera's eye through this position.

For making the pick ray:
ray3
S4Camera::makePickRay(int iScreenPixelX_, int iScreenPixelY_)
{
// n is away from the center...
// u is right vector

// Y-pixels are inverted;
float pw = iScreenPixelX_;
float ph = m_iFilmHeight - iScreenPixelY_;

float x = 2 * pw / m_iFilmWidth - 1.0f;
float y = 2 * ph / m_iFilmHeight - 1.0f;

vec3 pickDirection = m_frustum.right*x*u + m_frustum.top*y*v + -m_frustum.near*n;
ray3 pickRay(m_eye, pickDirection);
return pickRay;
}



Here's how I'm making the camera transform:
void
S4Camera::makeMatrix()
{
// n = viewplane normal
// v = up
// u = right

n = m_eye - m_center;
u = m_up.cross(n);
v = n.cross(u);

n.normalize();
u.normalize();
v.normalize();

mat4 &M = m_matrix;
M[0] = u.x;
M[4] = u.y;
M[8] = u.z;
M[12] = 0.0f;

M[1] = v.x;
M[5] = v.y;
M[9] = v.z;
M[13] = 0.0f;

M[2] = n.x;
M[6] = n.y;
M[10] = n.z;
M[14] = 0.0f;

M[3] = 0.0f;
M[7] = 0.0f;
M[11] = 0.0f;
M[15] = 1.0f;

mat4 translation = mat4::translation(-m_eye);
M *= translation;
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!