我正在尝试使用鼠标在球面上选择顶点。 我正在使用freeglut和OpenGL 4.5。
渲染
显示函数:
void display(void) {
// Clear display port
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Reset camera
gluLookAt(
0.0, 0.0, 5.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0
);
// Rotate
glRotatef(-theta, 1.0, 0.0, 0.0);
glRotatef(phi, 0.0, 0.0, 1.0);
// Zoom
glScalef(zoom, zoom, zoom);
// Render sphere
glPushMatrix();
glTranslatef(0, 0, 0);
glColor3f(1, 0, 0);
glutWireSphere(1, 32, 32);
glPopMatrix();
.
.
.
// Render selection point
glPushMatrix();
pointIsOnSphere ? glColor3f(0, 1, 0) : glColor3f(0, 0, 1);
pointIsOnSphere ? glPointSize(5.0f) : glPointSize(2.5f);
glBegin(GL_POINTS);
glVertex3f(clickPosition.x, clickPosition.y, clickPosition.z);
glEnd();
glPopMatrix();
// Swap buffers
glutSwapBuffers();
}
重塑函数:
void reshape(GLsizei width, GLsizei height) {
if (height == 0) {
height = 1;
}
float ratio = 1.0 * width / height;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, width, height);
gluPerspective(45, ratio, 0.5, 5);
glMatrixMode(GL_MODELVIEW);
}
鼠标操作
// Handles mouse click for point selection.
void mouse(int button, int state, int x, int y) {
mouse_x = x;
mouse_y = y;
if (!pointIsOnSphere)
return;
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
controlPoints.push_back(clickPosition);
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
RIGHT_BUTTON_DOWN = true;
glutPostRedisplay();
}
// Handles mouse movement for rotation.
void motion(int x, int y) {
if (RIGHT_BUTTON_DOWN) {
phi += (float)(x - mouse_x) / 4.0;
theta += (float)(mouse_y - y) / 4.0;
}
mouse_x = x;
mouse_y = y;
glutPostRedisplay();
}
// Handles mouse movement for point selection.
void passiveMotion(int x, int y) {
// Get position of click.
clickPosition = GetOGLPos(x, y);
// Set click position's z position to camera's z position.
clickPosition.z = 1;
// Create directional vector pointing into the screen.
glm::vec3 into_screen = glm::vec3(0, 0, -1);
// Create ray.
ray r = ray(
clickPosition,
into_screen
);
mousePosition = clickPosition;
float t = hit_sphere(glm::vec3(0), 1, r);
if (t != -1) {
pointIsOnSphere = true;
clickPosition += t * into_screen;
}
else {
pointIsOnSphere = false;
}
glutPostRedisplay();
}
// Handles scroll input for zoom.
void mouseWheel(int button, int state, int x, int y) {
if (state == 1) {
zoom = zoom + zoom_sensitivity >= zoom_max ? zoom_max : zoom + zoom_sensitivity;
}
else if (state == -1) {
zoom = zoom - zoom_sensitivity <= zoom_min ? zoom_min : zoom - zoom_sensitivity;
}
glutPostRedisplay();
}
3D拾取
// Get position of click in 3-d space
glm::vec3 GetOGLPos(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float)x;
winY = (float)viewport[3] - (float)y;
glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return glm::vec3(posX, posY, posZ);
}
float hit_sphere(const glm::vec3& center, float radius, const ray& r) {
glm::vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0 * glm::dot(oc, r.direction());
float c = glm::dot(oc, oc) - radius * radius;
float discriminant = b * b - 4 * a*c;
if (discriminant < 0.0) {
return -1.0;
}
else {
float numerator = -b - sqrt(discriminant);
if (numerator > 0.0) {
return numerator / (2.0 * a);
}
numerator = -b + sqrt(discriminant);
if (numerator > 0.0) {
return numerator / (2.0 * a);
}
else {
return -1;
}
}
}
可视化结果
正如您所见,实际的选点位置与光标的位置偏离。
以下是显示该错误的视频:
https://gfycat.com/graciousantiquechafer
显然,z值出现了问题,我可以看到当您远离中心轴时,错误变得更加严重。但我似乎无法确定代码中确切的问题所在。
新错误
现在,这些点出现在球体表面之外。