PDA

View Full Version : picking and camera projection.



gafferuk
10-04-2009, 06:23 PM
hi,
im trying to merge a shadow example and a picking example but when I click on the screen it only picks at the center of the screen even though theres nothing at the center.

It's for a music appication im developing. Light-Track Studio!

I know it has something to do with camera projection matrixes. something i have yet to learn.

heres my code, help would be great!


#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdio.h>
#include "GLee/GLee.h" //GL header file, including extensions
#include <GL/glut.h>
#include "Maths/Maths.h"
#include "TIMER.h"
#include "FPS_COUNTER.h"
#include "main.h"

#include <iostream>

using namespace std;

#define EARTH 1
#define MARS 2
#define MOON1 3
#define MOON2 4
#define BUFFER_LENGTH 512

//Timer used for frame rate independent movement
TIMER timer;

//Frames per second counter
FPS_COUNTER fpsCounter;

//Camera & light positions
VECTOR3D cameraPosition(-2.5f, 3.5f,-2.5f);
VECTOR3D lightPosition(2.0f, 3.0f,-2.0f);

//Size of shadow map
const int shadowMapSize=512;

//Textures
GLuint shadowMapTexture;

//window size
int windowWidth, windowHeight;

//Matrices
MATRIX4X4 lightProjectionMatrix, lightViewMatrix;
MATRIX4X4 cameraProjectionMatrix, cameraViewMatrix;

//Called for initiation
bool Init(void)
{
//Check for necessary extensions
if (!GLEE_ARB_depth_texture || !GLEE_ARB_shadow)
{
printf("I require ARB_depth_texture and ARB_shadow extensionsn\n");
return false;
}

//Load identity modelview
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//Shading states
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

//Depth states
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);

glEnable(GL_CULL_FACE);

//We use glScale when drawing the scene
glEnable(GL_NORMALIZE);

//Create the shadow map texture
glGenTextures(1, &amp;shadowMapTexture);
glBindTexture(GL_TEXTURE_2D, shadowMapTexture);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowMapSize, shadowMapSize, 0,
GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

//Use the color as the ambient and diffuse material
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);

//White specular material color, shininess 16
glMaterialfv(GL_FRONT, GL_SPECULAR, white);
glMaterialf(GL_FRONT, GL_SHININESS, 16.0f);

//Calculate & save matrices
glPushMatrix();

glLoadIdentity();
gluPerspective(45.0f, 1.0f, 1.0f, 100.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, cameraProjectionMatrix);

glLoadIdentity();
gluLookAt(cameraPosition.x, cameraPosition.y, cameraPosition.z,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, cameraViewMatrix);

glLoadIdentity();
gluPerspective(45.0f, 1.0f, 2.0f, 8.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, lightProjectionMatrix);

glLoadIdentity();
gluLookAt( lightPosition.x, lightPosition.y, lightPosition.z,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, lightViewMatrix);

glPopMatrix();

//Reset timer
timer.Reset();

return true;
}

void DrawScene()
{
glColor3f(0.0f, 1.0f, 0.0f);

glInitNames();
glPushName(0);
glLoadName(2);
glTranslatef(0.45f, 1.0f, 0.45f);
glutSolidSphere(0.2, 24, 24);

glTranslatef(-0.9f, 0.0f, 0.0f);
glutSolidSphere(0.2, 24, 24);

glTranslatef(0.0f, 0.0f,-0.9f);
glutSolidSphere(0.2, 24, 24);

glTranslatef(0.9f, 0.0f, 0.0f);
glutSolidSphere(0.2, 24, 24);
glPopName();
}

//Called to draw scene

//Called to draw scene
void Display(void)
{
//angle of spheres in scene. Calculate from time
float angle=timer.GetTime()/10;

//First pass - from light's point of view
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(lightProjectionMatrix);

glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(lightViewMatrix);

//Use viewport the same size as the shadow map
glViewport(0, 0, shadowMapSize, shadowMapSize);

//Draw back faces into the shadow map
glCullFace(GL_FRONT);

//Disable color writes, and use flat shading for speed
glShadeModel(GL_FLAT);
glColorMask(0, 0, 0, 0);

//Draw the scene
DrawScene();

//Read the depth buffer into the shadow map texture
glBindTexture(GL_TEXTURE_2D, shadowMapTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, shadowMapSize, shadowMapSize);

//restore states
glCullFace(GL_BACK);
glShadeModel(GL_SMOOTH);
glColorMask(1, 1, 1, 1);

//2nd pass - Draw from camera's point of view
glClear(GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_PROJECTION);
glLoadMatrixf(cameraProjectionMatrix);

glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(cameraViewMatrix);

glViewport(0, 0, windowWidth, windowHeight);

//Use dim light to represent shadowed areas
glLightfv(GL_LIGHT1, GL_POSITION, VECTOR4D(lightPosition));
glLightfv(GL_LIGHT1, GL_AMBIENT, white*0.2f);
glLightfv(GL_LIGHT1, GL_DIFFUSE, white*0.2f);
glLightfv(GL_LIGHT1, GL_SPECULAR, black);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);

DrawScene();

//3rd pass
//Draw with bright light
glLightfv(GL_LIGHT1, GL_DIFFUSE, white);
glLightfv(GL_LIGHT1, GL_SPECULAR, white);

//Calculate texture matrix for projection
//This matrix takes us from eye space to the light's clip space
//It is postmultiplied by the inverse of the current view matrix when specifying texgen
static MATRIX4X4 biasMatrix(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f); //bias from [-1, 1] to [0, 1]
MATRIX4X4 textureMatrix=biasMatrix*lightProjectionMatrix*lig htViewMatrix;

//Set up texture coordinate generation.
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, textureMatrix.GetRow(0));
glEnable(GL_TEXTURE_GEN_S);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_T, GL_EYE_PLANE, textureMatrix.GetRow(1));
glEnable(GL_TEXTURE_GEN_T);

glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_R, GL_EYE_PLANE, textureMatrix.GetRow(2));
glEnable(GL_TEXTURE_GEN_R);

glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_Q, GL_EYE_PLANE, textureMatrix.GetRow(3));
glEnable(GL_TEXTURE_GEN_Q);

//Bind & enable shadow map texture
glBindTexture(GL_TEXTURE_2D, shadowMapTexture);
glEnable(GL_TEXTURE_2D);

//Enable shadow comparison
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);

//Shadow comparison should be true (ie not in shadow) if r<=texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);

//Shadow comparison should generate an INTENSITY result
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);

//Set alpha test to discard false comparisons
glAlphaFunc(GL_GEQUAL, 0.99f);
glEnable(GL_ALPHA_TEST);

DrawScene();

//Disable textures and texgen
glDisable(GL_TEXTURE_2D);

glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);

//Restore other states
glDisable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);

//Update frames per second counter
fpsCounter.Update();

//Print fps
static char fpsString[32];
sprintf(fpsString, "%.2f", fpsCounter.GetFps());

//Set matrices for ortho
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(-1.0f, 1.0f, -1.0f, 1.0f);

glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

//Print text
glRasterPos2f(-1.0f, 0.9f);
for (unsigned int i=0; i<strlen(fpsString); ++i)
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, fpsString[i]);

//reset matrices
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

glFinish();
glutSwapBuffers();
glutPostRedisplay();
}

//Called on window resize
void Reshape(int w, int h)
{
//Save new window size
windowWidth=w, windowHeight=h;

//Update the camera's projection matrix
glPushMatrix();
glLoadIdentity();
gluPerspective(45.0f, 1.0f, 1.0f, 100.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, cameraProjectionMatrix);
glPopMatrix();
}

//Called when a key is pressed
void Keyboard(unsigned char key, int x, int y)
{
//If escape is pressed, exit
//if(key==27)
// exit(0);

//Use P to pause the animation and U to unpause
if (key=='P' || key=='p')
timer.Pause();

if (key=='U' || key=='u')
timer.Unpause();
}

void pointerMotion(int x, int y)
{
// Space for selection buffer
GLuint selectBuff[BUFFER_LENGTH];

// Hit counter and viewport storage
GLint hits, viewport[4];

// Get the viewport
glGetIntegerv(GL_VIEWPORT, viewport);

// Set up selection buffer
glSelectBuffer(BUFFER_LENGTH, selectBuff);

// Switch to projection and save the matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();

// Change render mode
glRenderMode(GL_SELECT);

// Establish new clipping volume to be unit cube around
// mouse cursor point (xPos, yPos) and extending two pixels
// in the vertical and horizontal direction
glLoadIdentity();
gluPickMatrix(x, 512 - y, 2,2, viewport);

// Apply matrix
gluPerspective(45.0f, 1.0f, 1.0f, 100.0f);

glViewport(0, 0, windowWidth, windowHeight);

DrawScene();

// Collect the hits
hits = glRenderMode(GL_RENDER);

// If a single hit occurred, display the info.
if (hits >= 1)
{
int id,count;
char cMessage[64];

// How many names on the name stack
count = selectBuff[0];

// Bottom of the name stack
id = selectBuff[3];

// Select on earth or mars, whichever was picked
switch (id)
{
case EARTH:
cout << "You clicked Earth." << std::endl;
break;

case MARS:
cout << "You clicked Mars." << std::endl;

break;

// If nothing was clicked we shouldn't be here!
default:
cout << "Error - Nothing was clicked on!" << std::endl;
break;
}
}

// Restore the projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();

// Go back to modelview for normal rendering
glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv)
{
glutInit(&amp;argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(640, 512);
glutCreateWindow("Light Track Studio");

if (!Init())
return 0;

glutDisplayFunc(Display);
glutReshapeFunc(Reshape);
glutKeyboardFunc(Keyboard);
glutMotionFunc(pointerMotion);
glutMainLoop();

return 0;
}

scratt
10-04-2009, 11:57 PM
I would suggest that you take a look at the Ray Picking method which is detailed here..

http://www.opengl.org/resources/faq/technical/selection.htm

The method you are using is deprecated AFAIK in more recent OpenGL implementations, although it may be supported for some time to come.

But in any case doing selection on the CPU side is a better approach these days, and less complex really.

Whatever you decide the notes on the page linked above should lead you to your answer.

gafferuk
10-05-2009, 01:43 AM
Thanks, looking now.

knackered
10-05-2009, 08:45 AM
if your application is anything like mine, the CPU will only have access to approximations of the rendered geometry (bounding volumes), while the actual geometry will be stored on the GPU, or in fact procedurally generated on the GPU (displacement mapping, for example, or any kind of non-standard vertex/geometry shader).
Therefore ray picking on the CPU simply won't be an option (especially these days). I'm hard pressed to think of an application where CPU ray picking would be an option, to be honest. You store your geometry twice, scratt?! You run vertex shaders on the CPU to get the triangles for the picking?!
Collision detection is another thing entirely, and rarely requires the same resolution of geometry as drawn to the screen. Decent picking *always* requires the exact geometry as drawn to the screen. Therefore you should use something like the colour buffer encoding solution, where you do a render pass into a small viewport around the mouse position encoding the batch number into the colour of the batch, then read back the pixel under the mouse position.

scratt
10-05-2009, 07:32 PM
Therefore ray picking on the CPU simply won't be an option (especially these days). I'm hard pressed to think of an application where CPU ray picking would be an option, to be honest. You store your geometry twice, scratt?!

The OP's project looked sufficiently simple to me for ray picking to be more than adequate. It's certainly a step forward from deprecated GL picking name stacks.

As regards your question. I store geometry wherever it is best suited to be stored on a case by case basis. CPU side memory is cheap. Super cheap, and bounty-full. So a duplicate on the CPU side is not a problem at all. Of course if the geometry is on the GPU only then there are other methods. I didn't really see the point of going into that detail and offering that many options here. :)

But as knackered suggests, using the colour buffer is another option (assuming you are not on a device which has problems reading back from the colour buffer with any efficiency).

gafferuk
10-07-2009, 03:20 AM
I can't use color indexing for picking because of the amount of objects in my scene.

Does anyone know of a clean simple demo of shadow mapping and mouse picking?

Thanks.
Paul.

ZbuffeR
10-07-2009, 05:11 AM
You really need to pick among more that 16 million objects ?

knackered
10-07-2009, 08:51 AM
16 million objects visible in a 1x1 pixel viewport, too??!
You only need to store the batch number in the colour, not the....oh I don't know what you're thinking....the pointer to the scene object? Is that what you're thinking? (even if you are, then it's most likely a 32bit pointer, which will obviously fit into RGBA)
So you do your culling, with the frustum for that 1x1 pixel viewport (i.e. freekin tiny frustum), then go through each object that passed the cull test in a loop (for i=...) and the colour you use should just be that integer (i) cast to RGBA/UNSIGNED BYTE.
You'll be happy to discover, by the way, that using this method also gives you the correct picking through objects that have been alpha-tested out - i.e. a polygon with a net texture would allow the user to click on the object behind the net, through the net...in a natural way. To get that functionality on the CPU picking, you'd have to also have the textures stored in system memory, and go through the hassle of calculating the uv intersection etc.

Aleksandar
10-09-2009, 01:02 PM
You really need to pick among more that 16 million objects ?

16 million objects visible in a 1x1 pixel viewport, too??!
Lost of the GL_SELECTION render mode is one of the greatest lost of the new GL model. So far I didn't find enough efficient replacement (although I must admit that I didn't spend much time in the exploration, especially when realized that I didn't get any acceleration after turning one of my applications to GL 3.2 Core ... , and I lost a lot of functionality...) :(

A mechanism of picking using GL_SELECTION mode and name-stack enables hierarchical selection on a very simple and elegant way. Coloring does not!

knackered
10-11-2009, 03:48 PM
GL_SELECTION mode can easily be emulated with the colour buffer method, and also works with alpha-tested pixels - which GL_SELECTION does not.
If something can easily be emulated with existing API (or even improved) then it doesn't belong in the core. You should really be arguing for an update of GLU, not GL itself.

Aleksandar
10-13-2009, 02:20 AM
GL_SELECTION mode can easily be emulated with the colour buffer method...
May I ask you how it can be emulated?


You should really be arguing for an update of GLU, not GL itself.
As far as I know, GLU relies on GL (although I didn't have an opportunity to take a look at the source code). I'm not sure that GLU can offer some functionality that is not exposed in GL, but on the higher level.