OpenGL 和 gluLookAt 函数
OpenGL and gluLookAt function
我有函数 moveCamera() 可以围绕中心球体旋转相机。
void moveCamera()
{
glLoadIdentity();
int vec = ceil(theta / 3.1415);
int y;
if (vec%2)
y = 1;
else
y = -1;
gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}
此函数在 display() 中调用
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
在这种情况下一切正常。但是我注意到了一些我无法解释的事情。
例如,如果我像这样修改 display() 函数以在 X 轴方向绘制红线
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glColor3ub(255, 0, 0);
glLineWidth(5);
glBegin(GL_LINES);
glVertex3i(0, 0, 0);
glVertex3i(1000, 0, 0);
glEnd();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
...什么都没发生。为什么???
另一个,如果我在 glClear() 之后删除 glLoadIdentity(),红线将被绘制,但是当我通过键盘按钮移动相机时,我看到渲染这条线时出现了一些错误(该线改变了它们的绘制坐标) .为了看到它,运行我的代码。
完整代码:
#include <iostream>
#include <glut.h>
#include <math.h>
#include <QDebug>
using namespace std;
#define WIDTH 1024
#define HEIGHT 600
void display();
void reshape(int width, int height);
void keyboard(unsigned char key, int x, int y);
void moveCamera();
void init();
void mkList();
void enableLight();
//double z(const double &x, const double &y);
void printMatrix(double *m)
{
for (int i = 0; i < 4; i++)
qDebug() << QString("%1 %2 %3 %4").arg(m[i*4]).arg(m[i*4+1]).arg(m[i*4+2]).arg(m[i*4+3]);
qDebug() << "\n";
}
GLuint idList = 0;
double e_x = 0;
double e_y = 0;
double e_z = 0;
double r = 300;
double phi = 0;
double theta = 1.5;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(WIDTH, HEIGHT);
glutInitWindowPosition(30, 100);
glutCreateWindow("Lab#2");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glEnable(GL_DEPTH_TEST);
init();
mkList();
enableLight();
glutMainLoop();
return 0;
}
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glColor3ub(255, 0, 0);
glLineWidth(5);
glBegin(GL_LINES);
glVertex3i(0, 0, 0);
glVertex3i(1000, 0, 0);
glEnd();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 2, 100, 2000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
#define ESCAPE '3'
switch (key)
{
case ESCAPE:
exit(0);
break;
case 's':
theta += 0.1;
break;
case 'w':
theta -= 0.1;
break;
case 'a':
phi += 0.1;
break;
case 'd':
phi -= 0.1;
break;
case 'q':
r-=5;
break;
case 'e':
r+=5;
break;
default:
break;
}
e_x = r * sin(theta) * cos(phi);
e_z = r * sin(theta) * sin(phi);
e_y = r * cos(theta);
glutPostRedisplay();
}
void moveCamera()
{
glLoadIdentity();
int vec = ceil(theta / 3.1415);
int y;
if (vec%2)
y = 1;
else
y = -1;
gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}
void init()
{
e_x = r * sin(theta) * cos(phi);
e_z = r * sin(theta) * sin(phi);
e_y = r * cos(theta);
}
void mkList()
{
int idInnerList = glGenLists(1);
glNewList(idInnerList, GL_COMPILE);
glColor3ub(255, 0, 0);
glPushMatrix();
glTranslatef(-200, 0, 0);
glutWireSphere(50, 10, 10);
glPopMatrix();
glColor3ub(0, 255, 0);
glPushMatrix();
glTranslatef(200, 0, 0);
glutSolidCube(100);
glPopMatrix();
glEndList();
idList = glGenLists(1);
glNewList(idList, GL_COMPILE);
glCallList(idInnerList);
glPushMatrix();
glTranslatef(0, 0, 200);
glCallList(idInnerList);
glPopMatrix();
glPushMatrix();
glTranslatef(0, 0, -200);
glCallList(idInnerList);
glPopMatrix();
glEndList();
}
void enableLight()
{
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
GLfloat diffuse[] = {0.7, 0.7, 0.7, 1};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glEnable(GL_LIGHT0);
}
首先,我想说的是:现在是 2016 年 - 不要使用旧版 OpenGL。在您最喜欢的搜索引擎的帮助下阅读现代 OpenGL。
如果 "nothing happened" 你的意思是 "i couldn't see the red line" 并且如果我正确理解你的代码,那只是因为该行将被完全剪裁。让我解释一下。
影响 display()
内部顶点变换的第一件事是调用 glLoadIdentity()
。这会将当前矩阵堆栈的顶部设置为单位矩阵 I
。假设当前堆栈是 MODELVIEW
并且当前投影矩阵 P
是 reshape()
中设置的那个,那么您的行将从 local-space 进行以下转换进入剪辑-space:
Vx_clip = P * I * Vx_local
Vy_clip = P * I * Vy_local
鉴于模型-视图矩阵是恒等式,两个局部坐标将在 world-space 中保持不变,然后再转换为 clip-space。由于您的近平面位于 z=-100
,因此该线将完全超出平截头体,即不在近平面和远平面之间的 space 中,因此会被完全剪裁。这在数学上有点复杂,但在概念上是准确的。
你可以通过这样定义红线来说服自己
glVertex3i(-500, 0, -100);
glVertex3i(500, 0, -100);
现在,即使使用恒等模型-视图矩阵,线的一部分也将位于平截头体内,因为它与近平面部分相交,因此不能完全剪裁。
如果您在 渲染红线之前通过调用moveCamera()
首先应用非身份模型视图矩阵,您还可以部分看到红线。为什么?好吧,假设您选择了极坐标,以便相机仍然朝 (0, 0, -z)
方向看,然后使用给定的半径,您将相机定义为在世界中的 (0, 0, 300)-space,其中 gluLookAt()
将映射到一个变换矩阵,该矩阵将简单地向两个顶点添加 (0, 0, -300) 的平移。
给定直线的初始定义和新的模型视图矩阵,在左乘模型视图矩阵后,直线的顶点将位于 (0, 0, -300)
和 (1000, 0, -300)
。通过左乘投影矩阵转换为 clip-space 后,该线将仅被部分剪裁,因此可见。
正如我所说,这要复杂得多,事实上,这只是完整顶点变换管道的一部分。阅读 this and this 以获取有关顶点变换主题的数学论文。这是计算机图形学 101。请注意:专注于数学,而不是您不幸在那里看到的遗留物 API 的使用。
我有函数 moveCamera() 可以围绕中心球体旋转相机。
void moveCamera()
{
glLoadIdentity();
int vec = ceil(theta / 3.1415);
int y;
if (vec%2)
y = 1;
else
y = -1;
gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}
此函数在 display() 中调用
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
在这种情况下一切正常。但是我注意到了一些我无法解释的事情。
例如,如果我像这样修改 display() 函数以在 X 轴方向绘制红线
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glColor3ub(255, 0, 0);
glLineWidth(5);
glBegin(GL_LINES);
glVertex3i(0, 0, 0);
glVertex3i(1000, 0, 0);
glEnd();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
...什么都没发生。为什么???
另一个,如果我在 glClear() 之后删除 glLoadIdentity(),红线将被绘制,但是当我通过键盘按钮移动相机时,我看到渲染这条线时出现了一些错误(该线改变了它们的绘制坐标) .为了看到它,运行我的代码。
完整代码:
#include <iostream>
#include <glut.h>
#include <math.h>
#include <QDebug>
using namespace std;
#define WIDTH 1024
#define HEIGHT 600
void display();
void reshape(int width, int height);
void keyboard(unsigned char key, int x, int y);
void moveCamera();
void init();
void mkList();
void enableLight();
//double z(const double &x, const double &y);
void printMatrix(double *m)
{
for (int i = 0; i < 4; i++)
qDebug() << QString("%1 %2 %3 %4").arg(m[i*4]).arg(m[i*4+1]).arg(m[i*4+2]).arg(m[i*4+3]);
qDebug() << "\n";
}
GLuint idList = 0;
double e_x = 0;
double e_y = 0;
double e_z = 0;
double r = 300;
double phi = 0;
double theta = 1.5;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(WIDTH, HEIGHT);
glutInitWindowPosition(30, 100);
glutCreateWindow("Lab#2");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glEnable(GL_DEPTH_TEST);
init();
mkList();
enableLight();
glutMainLoop();
return 0;
}
void display()
{
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glColor3ub(255, 0, 0);
glLineWidth(5);
glBegin(GL_LINES);
glVertex3i(0, 0, 0);
glVertex3i(1000, 0, 0);
glEnd();
moveCamera();
glCallList(idList);
GLfloat pos[] = {0, 0, 0, 1};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glFinish();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 2, 100, 2000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
#define ESCAPE '3'
switch (key)
{
case ESCAPE:
exit(0);
break;
case 's':
theta += 0.1;
break;
case 'w':
theta -= 0.1;
break;
case 'a':
phi += 0.1;
break;
case 'd':
phi -= 0.1;
break;
case 'q':
r-=5;
break;
case 'e':
r+=5;
break;
default:
break;
}
e_x = r * sin(theta) * cos(phi);
e_z = r * sin(theta) * sin(phi);
e_y = r * cos(theta);
glutPostRedisplay();
}
void moveCamera()
{
glLoadIdentity();
int vec = ceil(theta / 3.1415);
int y;
if (vec%2)
y = 1;
else
y = -1;
gluLookAt(e_x, e_y, e_z, 0, 0, 0, 0, y, 0);
}
void init()
{
e_x = r * sin(theta) * cos(phi);
e_z = r * sin(theta) * sin(phi);
e_y = r * cos(theta);
}
void mkList()
{
int idInnerList = glGenLists(1);
glNewList(idInnerList, GL_COMPILE);
glColor3ub(255, 0, 0);
glPushMatrix();
glTranslatef(-200, 0, 0);
glutWireSphere(50, 10, 10);
glPopMatrix();
glColor3ub(0, 255, 0);
glPushMatrix();
glTranslatef(200, 0, 0);
glutSolidCube(100);
glPopMatrix();
glEndList();
idList = glGenLists(1);
glNewList(idList, GL_COMPILE);
glCallList(idInnerList);
glPushMatrix();
glTranslatef(0, 0, 200);
glCallList(idInnerList);
glPopMatrix();
glPushMatrix();
glTranslatef(0, 0, -200);
glCallList(idInnerList);
glPopMatrix();
glEndList();
}
void enableLight()
{
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
GLfloat diffuse[] = {0.7, 0.7, 0.7, 1};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glEnable(GL_LIGHT0);
}
首先,我想说的是:现在是 2016 年 - 不要使用旧版 OpenGL。在您最喜欢的搜索引擎的帮助下阅读现代 OpenGL。
如果 "nothing happened" 你的意思是 "i couldn't see the red line" 并且如果我正确理解你的代码,那只是因为该行将被完全剪裁。让我解释一下。
影响 display()
内部顶点变换的第一件事是调用 glLoadIdentity()
。这会将当前矩阵堆栈的顶部设置为单位矩阵 I
。假设当前堆栈是 MODELVIEW
并且当前投影矩阵 P
是 reshape()
中设置的那个,那么您的行将从 local-space 进行以下转换进入剪辑-space:
Vx_clip = P * I * Vx_local
Vy_clip = P * I * Vy_local
鉴于模型-视图矩阵是恒等式,两个局部坐标将在 world-space 中保持不变,然后再转换为 clip-space。由于您的近平面位于 z=-100
,因此该线将完全超出平截头体,即不在近平面和远平面之间的 space 中,因此会被完全剪裁。这在数学上有点复杂,但在概念上是准确的。
你可以通过这样定义红线来说服自己
glVertex3i(-500, 0, -100);
glVertex3i(500, 0, -100);
现在,即使使用恒等模型-视图矩阵,线的一部分也将位于平截头体内,因为它与近平面部分相交,因此不能完全剪裁。
如果您在 渲染红线之前通过调用moveCamera()
首先应用非身份模型视图矩阵,您还可以部分看到红线。为什么?好吧,假设您选择了极坐标,以便相机仍然朝 (0, 0, -z)
方向看,然后使用给定的半径,您将相机定义为在世界中的 (0, 0, 300)-space,其中 gluLookAt()
将映射到一个变换矩阵,该矩阵将简单地向两个顶点添加 (0, 0, -300) 的平移。
给定直线的初始定义和新的模型视图矩阵,在左乘模型视图矩阵后,直线的顶点将位于 (0, 0, -300)
和 (1000, 0, -300)
。通过左乘投影矩阵转换为 clip-space 后,该线将仅被部分剪裁,因此可见。
正如我所说,这要复杂得多,事实上,这只是完整顶点变换管道的一部分。阅读 this and this 以获取有关顶点变换主题的数学论文。这是计算机图形学 101。请注意:专注于数学,而不是您不幸在那里看到的遗留物 API 的使用。