OpenGL picking tutorial
OpenGL picking tutorial
This tutorial will show you how to determine the point in 3D space that a selected pixel represents. The process is called picking, and in games it’s most often used for selecting 3D objects, determining at which point on the terrain the user has clicked in RTS game, and some lower quality games even use this technique for targeting in FPS games.
If you are using glew then this code should be copy-paste ready.
// Glew here is not used to initialize OpenGL device nor extensions. // The only thing needed from it are just some OpenGL definitions and core functions. #include "Libs\glew.h" /* Some simple math used in this tutorial. Any good game engine should have a separate file for math typedefs and functions. Since this is a short tutorial I have just copied these. */ struct Vector2f { // Take values upon construction. Vector2f(float x, float y){ this->x = x; this->y = y; } float x,y; }; struct Vector3f { // Take values upon construction. Vector3f(float x, float y, float z){ this->x = x; this->y = y; this->z = z; } float x,y,z; }; typedef float Matrix4f[4][4]; /* This is the main function that the engine calls. As arguments it takes in the position of selected pixel in the framebuffer and ModelView and Projection matrices. The position is probably just going to be the cursor position in most uses.*/ Vector3f Project2D_point_to_Terrain(const Vector2f Position, const float* ModelView, const float* Projection); /* This function will acquire the z coordinate of selected pixel(Position) by using the depth buffer. */ float GetZ_fromDepthBuffer(const Vector2f Position); Vector3f Project2D_point_to_Terrain(const Vector2f Position, const float* ModelView, const float* Projection) { // Here we will put the resulting point in 3d space. double result[3]; // We will need the matrices to contain double precision numbers for UnProject function. double m_ModelMat[16]; for(int i=0;i<=15;i++) m_ModelMat[i] = ModelView[i]; double m_ProjMat[16]; for(int i=0;i<=15;i++) m_ProjMat[i] = Projection [i]; // First, calculate the z coordinate of the selected pixel. float Distance = GetZ_fromDepthBuffer(Position); // This is my Viewport. You should have this value memorized and accessible in the engine. // But for the simplicity's sake, I will ask OpenGL to tell me it's size in this tutorial. GLint lViewPort[4]; glGetIntegerv( GL_VIEWPORT, lViewPort ); // You can either use the GLU function called gluUnProject, or write it yourself. // Another tutorial will cover coding the UnProject function. gluUnProject( (double)Position.x, (double)Position.y, (double)Distance, m_ModelMat, m_ProjMat, lViewPort, &result[0], &result[1], &result[2]); return Vector3f(( float )result[0], ( float )result[1], ( float )result[2]); } float GetZ_fromDepthBuffer(const Vector2f Position) { // First, acquire the value of depth buffer for the selected pixel. // Use an unsigned integer. It's a 32 bit number and we don't need negative numbers since screen is the zero depth. GLuint lDepth; // This OpenGL function lets us read various properties of selected pixels in current Framebuffer. glReadPixels( (GLuint) Position.x, (GLuint)Position.y, // This is the spot, the pixel in Framebuffer. 1, 1, // Just one pixel, horizontally and vertically. GL_DEPTH_COMPONENT, // We don't need it's color, nor stencil, only the depth bits. GL_UNSIGNED_INT, // In which form we want the result to be retreived. Same as GLuint. &lDepth // What we retrieved is put here. ); // We need to know how many bits the Depth Buffer of current Framebuffer has. int Depth_bits; glGetIntegerv( GL_DEPTH_BITS, &Depth_bits ); // It's size will tell us what is the maximum depth float Terminal_depth; switch ( Depth_bits ) { case 16 : Terminal_depth = (float) 1.5259018967e-5; // 65535, or 2^16. break; default :// It's the same for 24 and 32 bit depth buffers. Terminal_depth = (float) 2.32830643708e-10; // 4294967295, 2^32. Even if depth buffer uses 24 bits this is still the terminal depth value. break; } // This is how you get the real depth of the selected pixel in you 3D universe. float real_depth = (float)lDepth * Terminal_depth; return real_depth; } int main(){ return 0; }
Download zip: