Advertisement

ray tracing image plane

Started by June 05, 2015 06:05 PM
8 comments, last by taby 9 years, 8 months ago

I'm trying to do ray tracing but I'm having no luck with the code that converts an x,y pixel into an x,y,z world position. The image plane is at z = 0, so the distance from the camera to the image plane is 1. I'm trying to avoid using glUnProject

My code is in void mouse_func, near the top of the snippet below:


#include <cstdlib>
#include <cmath>
#include <GLUT/glut.h>  /* Header File For The GLut Library*/
 
#include <string>
using std::string;
 
#include <sstream>
using std::ostringstream;
 
#include <iostream>
using std::cout;
using std::endl;
 
 
GLint win_id = 0;
GLint win_x = 800, win_y = 600;
 
int mouse_x = 0;
int mouse_y = 0;
bool lmb_down = false;
bool mmb_down = false;
bool rmb_down = false;
 
float last_click_float_x = 0;
float last_click_float_y = 0;
 
 
 
void mouse_func(int button, int state, int x, int y)
{
    if(GLUT_LEFT_BUTTON == button)
    {
        if(GLUT_DOWN == state)
            lmb_down = true;
        else
        {
            lmb_down = false;
            
            
            
            float aspect = static_cast<float>(win_x)/static_cast<float>(win_y);
            
            float fx = 2 * (float(x) / win_x) - 1;
            float fy = 2 * (float(y) / win_y) - 1;
            
            
            float tangent = tan(0.785398163  / 2);
            last_click_float_x = aspect * tangent * fx;
            last_click_float_y = tangent * fy;
            
            
            
            
        }
    }
    else if(GLUT_MIDDLE_BUTTON == button)
    {
        if(GLUT_DOWN == state)
            mmb_down = true;
        else
            mmb_down = false;
    }
    else if(GLUT_RIGHT_BUTTON == button)
    {
        if(GLUT_DOWN == state)
            rmb_down = true;
        else
            rmb_down = false;
    }
    
}
 
 
void draw_objects(void)
{
    glPushMatrix();
    
    
    glDisable(GL_LIGHTING);
    
    glPointSize(4.0);
    glColor3f(1, 1, 1);
 
    
    glBegin(GL_POINTS);
        glVertex3f(last_click_float_x, last_click_float_y, 0);
    glEnd();
    
    
    
 
    glPopMatrix();
}
 
 
 
 
void display_func(void)
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
    // Draw the model's components using OpenGL/GLUT primitives.
    draw_objects();
    
    glFlush();
    glutSwapBuffers();
}
 
 
void init_opengl(const int &width, const int &height)
{
    win_x = width;
    win_y = height;
    
    if(win_x < 1)
        win_x = 1;
    
    if(win_y < 1)
        win_y = 1;
    
    glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(win_x, win_y);
    win_id = glutCreateWindow("GLUT Window");
    
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glDepthMask(GL_TRUE);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    
    glClearColor(0, 0, 0, 1);
    glClearDepth(1.0f);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    
    gluPerspective(45.0,
                   static_cast<GLfloat>(win_x)/static_cast<GLfloat>(win_y),
                   0.01, 20.0);
    
    gluLookAt(0,0,1, // Eye position.
              0,0,-1, // Look at position (not direction).
              0,1,0); // Up direction vector.
}
 
void reshape_func(int width, int height)
{
    win_x = width;
    win_y = height;
    
    if(win_x < 1)
        win_x = 1;
    
    if(win_y < 1)
        win_y = 1;
    
    glutSetWindow(win_id);
    glutReshapeWindow(win_x, win_y);
    glViewport(0, 0, win_x, win_y);
    
    gluPerspective(45.0,
                   static_cast<GLfloat>(win_x)/static_cast<GLfloat>(win_y),
                   0.01, 20.0);
    
    gluLookAt(0,0,1, // Eye position.
              0,0,-1, // Look at position (not direction).
              0,1,0); // Up direction vector.
    
}
 
void idle_func(void)
{
    glutPostRedisplay();
}
 
void keyboard_func(unsigned char key, int x, int y)
{
    switch(tolower(key))
    {
            
        default:
            break;
    }
}
 
 
 
void motion_func(int x, int y)
{
    int prev_mouse_x = mouse_x;
    int prev_mouse_y = mouse_y;
    
    mouse_x = x;
    mouse_y = y;
    
    int mouse_delta_x = mouse_x - prev_mouse_x;
    int mouse_delta_y = prev_mouse_y - mouse_y;
}
 
void passive_motion_func(int x, int y)
{
    mouse_x = x;
    mouse_y = y;
}
 
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    
    init_opengl(win_x, win_y);
    
    glutReshapeFunc(reshape_func);
    glutDisplayFunc(display_func);
    glutIdleFunc(idle_func);
    glutKeyboardFunc(keyboard_func);
    glutMouseFunc(mouse_func);
    glutMotionFunc(motion_func);
    glutPassiveMotionFunc(passive_motion_func);
    glutMainLoop();
 
    return 0;
}
 

The title mentions "ray tracing." Your post mentions "mouse picking." You will likely get better help if you describe a bit more clearly what you're trying to do, rather than how you're trying to do it.

Depending on what you're trying to do, this thread may provide with the answer. Also, depending on what you want to do,


glRenderMode(GL_SELECT);

(here) may work better.

Is there a specific reason why you want to avoid gluUnProject? It will likely be faster than code you write yourself.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Advertisement

Sorry for the confusion. I'm basically looking to convert an x,y mouse position into a position on the image plane. Thanks for the link.

The reason I'm avoiding the glUnProject function is that it's not available on OpenGL ES 2, AFAIK. I'm trying to minimize the amount of code that I'm going to have to rewrite when I make switch over to the iPhone... already the GLUT code has to be rewritten, and that's enough.

i still dont understand that, you want to find 3d position on the image plane or the 3d position on the screen?

i think you want to find a 3d position on given plane Ax+By+Cz+D=0 so

if you can get the pixel you clicked you are almost ready to go:

take a look at this: https://www.opengl.org/wiki/GluProject_and_gluUnProject_code its really easy to implement. if you know the math behind that

i can also recommend diffrent apporach to the problem like: when you click on the screen you draw that plane full green

then you glReadPixels that pixel you clicked and if its green that means you hit the plane

since i bet its not infinite plane (but its something like quad) you could draw it two times.

lets say you use red color:

so top left and bottom left vertices will have pure black color and top right and bottom right color will have full red color

then glReadPixels and you get the color

same for y axis but you start from up or down whatever you want lets say you start from bottom.

since we start from left and bottom we know that (lets say first vertex of quad is in the same position left/bottom lest call it A

B is the TOP left vertex

C is top right vertex

D is bottom right vertex then

you normalize two vectors AB and AD

you multiple the color of first color pick by the width_of_quad * AD = lets call it resx

and then mutliple second color pick by height_of_quad * AB = lets call it resy

then your position is: pos = A + resx + resy;

~~~~~~~~~~~~

theres also another way that you need to calculate the nearplane vector right and up then you can make a ray from the hit point to the depth value of the image plane (but ogl es2.0 supports only 16 bit zbuff from what i remember so you will obviously have to expand/extrude/make longer ray and then perform ray plane collision detection, i think the color algorithm is the easiest one. but not that precise (you could then pass a ray from point given in direction of reversed normal - anyway you will have to extrude on both sides)

same for using your own gluunproject you will have to pass a ray/plane intersection to get proper results coz depth buffer isnt that good as you think : ) <- that means every approach given here will need ray plane intersection step

but i still dont understand the question be more specific by what do you mean ' I'm basically looking to convert an x,y mouse position into a position on the image plane.'

besides when rewriting glut and glulookat functions you will have to provide the math behind that same for matrix * matrix or matrix * vertex

heres some code i use on android






template <class T> void mglFrustum(Matrix44<T> & matrix, T l, T r, T b, T t, T n, T f)
{

matrix.m[0] = 	(2.0*n)/(r-l);
matrix.m[1] = 	0.0;
matrix.m[2] =  	(r + l) / (r - l);
matrix.m[3] =	0.0;

matrix.m[4] = 	0.0;
matrix.m[5] = 	(2.0*n) / (t - b);
matrix.m[6] =   (t + b) / (t - b);
matrix.m[7] =   0.0;

matrix.m[8] = 	0.0;
matrix.m[9] =  	0.0;
matrix.m[10] =	-(f + n) / (f-n);
matrix.m[11] =  (-2.0*f*n) / (f-n);

matrix.m[12] =  0.0;
matrix.m[13] =  0.0;
matrix.m[14] =  -1.0;
matrix.m[15] =  0.0;

}

template <class T> void
glLookAt(Matrix44<T> &matrix, t3dpoint<T> eyePosition3D, t3dpoint<T> center3D, t3dpoint<T> upVector3D )
{
   t3dpoint<T>  forward, side, up;
   forward = Normalize( vectorAB(eyePosition3D, center3D) );
   side = Normalize( forward * upVector3D );
   up = side * forward;
  matrix.LoadIdentity();

	matrix.m[0] = side.x;
	matrix.m[1] = side.y;
	matrix.m[2] = side.z;

	matrix.m[4] = up.x;
	matrix.m[5] = up.y;
	matrix.m[6] = up.z;

	matrix.m[8] 	= -forward.x;
	matrix.m[9] 	= -forward.y;
	matrix.m[10] 	= -forward.z;



Matrix44<T> transgender;
transgender.Translate(-eyePosition3D.x, -eyePosition3D.y, -eyePosition3D.z);


matrix = transgender * matrix;
}



template <class T>
Matrix44<T> ReturnTranslateMatP(t3dpoint<T> p)
{
	Matrix44<T> tran( 1, 0, 0, p.x,
					  0, 1, 0, p.y,
					  0, 0, 1, p.z,
					  0, 0, 0, 1);
	return tran;
}


template <class T>
void gluPerspectiveA(Matrix44<T> & matrix, T fovy, T aspect, T zmin, T zmax)
{
 T xmin, xmax, ymin, ymax;
 ymax = zmin * tan(fovy * M_PI / 360.0);
 ymin = -ymax;
 xmin = ymin * aspect;
 xmax = ymax * aspect;
 //mglFrustum(m, xmin, xmax, ymin, ymax, zmin, zmax);
 //mglFrustum(Matrix44<T> & matrix, T l, T r, T b, T t, T n, T f)


matrix.m[0] = 	(2.0*zmin)/(xmax-xmin);
matrix.m[1] = 	0.0;
matrix.m[2] =  	(xmax + xmin) / (xmax - xmin);
matrix.m[3] =	0.0;

matrix.m[4] = 	0.0;
matrix.m[5] = 	(2.0*zmin) / (ymax - ymin);
matrix.m[6] =   (ymax + ymin) / (ymax - ymin);
matrix.m[7] =   0.0;

matrix.m[8] = 	0.0;
matrix.m[9] =  	0.0;
matrix.m[10] =	-(zmax + zmin) / (zmax-zmin);
matrix.m[11] =  (-2.0*zmax*zmin) / (zmax-zmin);

matrix.m[12] =  0.0;
matrix.m[13] =  0.0;
matrix.m[14] =  -1.0;
matrix.m[15] =  0.0;


}



template <class T>
void gluOrthoA(Matrix44<T> & matrix, T Left, T Right, T Bottom, T Top, T znear, T zfar)
{

matrix.m[0] = 	2.0 / (Right - Left);
matrix.m[1] = 	0.0;
matrix.m[2] =  	0.0;
matrix.m[3] =	(Right + Left) / (Right - Left);

matrix.m[4] = 	0.0;
matrix.m[5] = 	2.0 / (Top - Bottom);
matrix.m[6] =   0.0;
matrix.m[7] =   (Top + Bottom) / (Top - Bottom);

matrix.m[8] = 	0.0;
matrix.m[9] =  	0.0;
matrix.m[10] =	-2.0 / (zfar - znear);
matrix.m[11] =  (zfar + znear) / (zfar - znear);

matrix.m[12] =  0.0;
matrix.m[13] =  0.0;
matrix.m[14] =  0.0;
matrix.m[15] =  1.0;
}



if you still dont trust me that you will need ray plane intersection point after getting 3d coord, i done full scale earth editor (well not earth editor but you were able to place objects on the surface, i used gluunproject + ray plane intersection because object appeared even 6 km above or below surface

some images that this works :P

4.jpg

ownaa.jpg

Thanks for the reply.

Basically I'm calculating on the image plane (the table top) where I click based on the x,y pixel coordinates.

I've attached a PNG file of the executable running. The camera is at 0,0,0 and the table top (image plane) is the plane where z = -1.

[attachment=27614:poker.png]

Advertisement

you could simplify that to calculate the rectangles areas by using mvp * vertex multiplication that will give you output from -1..1 you could convert that with foruma ((mvp * vertex)*0.5 + 0.5) * screen_width or screen_height that will give you 2d coordinate related to screen and then you could just simply check if mouse point is within rectange if you only want to hit those squares and/or cards

but drawing colored quad and getting positions will do the work you dont even need to perfor ray-plane intersection.

The magic code that did the trick:

            const float pi = 4.0f*atanf(1.0f);
            const float aspect = static_cast<float>(win_x) / static_cast<float>(win_y);
            const float fx = 2.0f * (static_cast<float>(x) / static_cast<float>(win_x)) - 1.0f;
            const float fy = 2.0f * (static_cast<float>(y) / static_cast<float>(win_y)) - 1.0f;
            const float y_fov = pi/4; // pi/4 radians = 45 degrees
            const float tangent = tan(y_fov / 2);
            last_click_float_x = aspect * tangent * fx;
            last_click_float_y = -tangent * fy;

i dont know what that code does but if the plane is at z = -1 the z component will stay it everyt time so you just need to find this rectangle boundary relative to the view anyway i dont believe that code will work for every case

My camera doesn't move at all, and it works.

This topic is closed to new replies.

Advertisement