OpenGL常用函数详解集锦

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38906523/article/details/75210105

1、       gluLookAt()函数详解

视点转换

函数原型

voidgluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdoublecentery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);

函数定义一个视图矩阵,并与当前矩阵相乘。

第一组eyex, eyey,eyez 相机在世界坐标的位置

第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置

第三组upx,upy,upz 相机向上的方向在世界坐标中的方向

你把相机想象成为你自己的脑袋:

第一组数据就是脑袋的位置

第二组数据就是眼睛看的物体的位置

第三组就是头顶朝向的方向(因为你可以歪着头看同一个物体)。

2、     glScalef()缩放变换函数详解

glScalef是openGL中的模型缩放函数,就是把当前矩阵与一个表示延各个轴对物体进行拉伸、压缩、反射的矩阵相乘。这个物体的每个点的x,y,z坐标与对应的xyz参数相乘。

先看函数定义
void glScalef(GLfloat x,  GLfloat y, GLfloat z);

参数x,y,z分别为模型在x,y,z轴方向的缩放比。

例如:
如glScalef(2.0f,3.0f,4.0f);将模型按x,y,z方向分别拉伸了2,3,4倍。
参数也可取负数,也可以理解为先关于某轴翻转180°,再缩放;
如glScalef(1.0f,1.0f,-1.0f);将模型关于z轴翻转了180°(即关于xy轴所在平面对称);
如glScalef(1.0f,1.0f,-2.0f);将模型关于-z轴拉伸2倍。

3、     glMatrixMode()缩放变换函数详解

glMatrixMode()函数的参数,这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对“什么”进行操作了,这个“什么”在glMatrixMode的“()”里的选项(参数)有3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理.

如果参数是GL_PROJECTION,这个是投影的意思,就是要对投影相关进行操作,也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如glFrustum()或gluPerspective();

在操作投影矩阵以前,需要调用函数:

glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵

然后把矩阵设为单位矩阵:
glLoadIdentity();
然后调用glFrustum()或gluPerspective(),它们生成的矩阵会与当前的矩阵相乘,生成透视的效果;
如果参数是GL_MODELVIEW,这个是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像gluLookAt()这样的函数;
若是GL_TEXTURE,就是对纹理相关进行操作
顺便说下,OpenGL里面的操作,很多是基于对矩阵的操作的,比如位移,旋转,缩放,所以,这里其实说的规范一点就是glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标,GL_PROJECTION是对投影矩阵操作,GL_MODELVIEW是对模型视景矩阵操作,GL_TEXTURE是对纹理矩阵进行随后的操作。
切换当前矩阵.
如.要使用透视(3D).那么先要设置透视投影
glMatrixMode(GL_PROJECTION); //切换到投影矩阵.
//...设置透视投影
设置完成后开始画图,需要切换到 模型视图矩阵 才能正确画图.
glMatrixMode(GL_MODELVIEW);
// 画一个物体A (看起来是3D的),
// 如这时候需画一个 2D效果 的 物体A,那么又需要透视投影
glMatrixMode(GL_PROJECTION); //切换到投影矩阵..
// ..设置正交投影
//..设置完成,切换回模型视图矩阵.....
glMatrixMode(GL_MODELVIEW);
// 再画一个物体A (看起来是2D的)
// 如从头到尾都是画3D/2D, 只需初始化时设置一次.
// 如果有交替,那么就需要glMatrixMode() 切换
// 因这样设置很烦人,所以又有glPushMatrix() 保存当前矩阵

4、      glFinish()glFlush()函数详解

通常情况下,OpenGL指令不是立即执行的。它们首先被送到指令缓冲区,然后才被送到硬件执行。glFinish和glFlush都是强制将命令缓冲区的内容提交给硬件执行。

一、glFinish()函数

glFinish()将缓冲区的指令立即送往硬件执行,但是要一直等到硬件执行完这些指令之后才返回。

如果直接绘制到前缓冲,那么在你想保存屏幕截图之前,就需要调用这个函数,确保绘制完毕。

如果使用双缓冲,则这个函数不会有太大作用。

二、glFlush()

glFlush()清空缓冲区,将指令送往缓硬件立即执行,但是它是将命令传送完毕之后立即返回,不会等待指令执行完毕。这些指令会在有限时间内执行完毕。

如果直接绘制到前缓冲,那么OpenGL的绘制将不会有任何延迟。设想有一个复杂的场景,有很多物体需要绘制。当调用glFlush时,物体会一个一个地出现在屏幕上。但是,如果使用双缓冲,这个函数将不会有什么影响,因为直到交换缓冲区的时候变化才显现出来。

如果你使用的是双缓冲,那么可能这两个函数都不需要用到。缓冲区交换操作会隐式将命令送去执行。

三、glFinish和glFlush的区别

看起来这两个函数很相似,但是仍然是有区别的。

一般,使用glFlush的目的是确保在调用之后,CPU没有OpenGL相关的事情需要做-命令会送到硬件执行。调用glFinish的目的是确保当返回之后,没有相关工作留下需要继续做。

glFinish会造成性能下降

如果调用glFinish,通常会带来性能上的损失。因为它会是的GPU和CPU之间的并行性丧失。

一般,我们提交给驱动的任务被分组,然后被送到硬件上(在缓冲区交换的时候)。如果调用glFinish,就强制驱动将命令送到GPU。然后CPU等待直到被传送的命令全部执行完毕。这样在GPU工作的整个期间内,CPU没有工作(至少在这个线程上)。而在CPU工作时(通常是在对命令分组),GPU没有工作。因此造成性能上的下降。

因此,应该尽量减少使用此函数。此函数的一个应用是:调试bug。如果我传输到硬件的某条命令造成了GPU的崩溃,找出使得GPU崩溃的那条指令的简单方法是在每个绘制操作之后调用这个函数。这样就可以准确找出造成崩溃的命令。

另外,Direct3D不支持Finish概念。

5、glViewport()函数和glOrtho()函数的理解

在OpenGL中有两个比较重要的投影变换函数,glViewport和glOrtho.
glOrtho是创建一个正交平行的视景体。 一般用于物体不会因为离屏幕的远近而产生大小的变换的情况。比如,常用的工程中的制图等。需要比较精确的显示。 而作为它的对立情况,glFrustum则产生一个透视投影。这是一种模拟真是生活中,人们视野观测物体的真实情况。例如:观察两条平行的火车到,在过了很远之后,这两条铁轨是会相交于一处的。还有,离眼睛近的物体看起来大一些,远的物体看起来小一些。
glOrtho(left, right, bottom, top, near, far), left表示视景体左面的坐标,right表示右面的坐标,bottom表示下面的,top表示上面的。这个函数简单理解起来,就是一个物体摆在那里,你怎么去截取他。这里,我们先抛开glViewport函数不看。先单独理解glOrtho的功能。 假设有一个球体,半径为1,圆心在(0,0, 0),那么,我们设定glOrtho(-1.5,1.5, -1.5, 1.5, -10, 10);就表示用一个宽高都是3的框框把这个球体整个都装了进来。  如果设定glOrtho(0.0,1.5, -1.5, 1.5, -10, 10);就表示用一个宽是1.5, 高是3的框框把整个球体的右面装进来;如果设定glOrtho(0.0,1.5, 0.0, 1.5, -10, 10);就表示用一个宽和高都是1.5的框框把球体的右上角装了进来。上述三种情况可以见图:

 

 

从上述三种情况,我们可以大致了解glOrtho函数的用法。

 

---glViewport():
glOrtho函数只是负责使用什么样的视景体来截取图像,并不负责使用某种规则把图像呈现在屏幕上。
glViewport主要完成这样的功能。它负责把视景体截取的图像按照怎样的高和宽显示到屏幕上。
比如:如果我们使用glut库建立一个窗体:glutInitWindowSize(500,500); 然后使用glutReshapeFunc(reshape); reshape代码如下:

void reshape(int width, int height)
{
    glViewport(0, 0, (GLsizei)width, (GLsizei)height);
    glMatrixModel(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10);

   ....
}
这样是可以看到一个正常的球体的。但是,如果我们创建窗体时glutInitWindowSize(800,500),那么看到的图像就是变形的。上述情况见图。


因为我们是用一个正方形截面的视景体截取的图像,但是拉伸到屏幕上显示的时候,就变成了glViewport(0,0, 800, 500);也就是显示屏变宽了, 倒是显示的时候把一个正方形的图像“活生生的给拉宽了”。就会产生变形。这样,就需要我们调整我们的OpenGL显示屏了。我们可以不用800那么宽,因为我们是用的正方形的视景体,所以虽然窗体是800宽,但是我们只用其中的500就够了。修改一下程序。
void reshape(int width, int height)
{
    int dis = width < height ? width : height;
    glViewport(0, 0, dis, dis);   /*这里dis应该是500*/

   glMatrixModel(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10);
    .....
}

OK.如果你能看明白我写的内容。你可能对glViewport函数有个大致的了解。

 

不过,我们采用上面的办法,就是只使用了原来屏幕的一部分(宽度从501到800我们没有用来显示图像)。如果我们想用整个OpenGL屏幕显示图像,但是又不使图像变形怎么办?
那就只能修改glOrtho函数了。也就是说,我们使用一个和窗体一样比例的视景体(而不再是正方形的视景体)来截取图像。例如,对于(800,500)的窗体,我们使用glOrtho(-1.5* 800/500, 1.5 * 800/500, -1.5, 1.5, -10, 10),就是截取的时候,我们就使用一个“扁扁”的视景体截取,那么,显示的到OpenGL屏幕时(800,500),我们只要正常把这个扁扁的截取图像显示(扁扁的截取图像是指整个截取的图像,包括球形四周的黑色部分。 球形还是正常圆形的),就可以了。如:
void reshape(int width , int height)
{
    glViewport(width, height); //按照窗体大小制作OpenGL屏幕
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (width <= height)
        glOrtho(-1.5, 1.5, -1.5 *(GLfloat)height/(GLfloat)width, 1.5 * (GLfloat)height/(GLfloat)width, -10.0,10.0);
    else
       glOrtho(-1.5*(GLfloat)width/(GLfloat)height, 1.5*(GLfloat)w/(GLfloat)h, -1.5,1.5, -10.0, 10.0);

 

   ....
}



另外,关于glViewport()函数,我们还可以用来调整图像的分辨率。例如,保持目前的窗体大小不变,我们如果用这个size来只显示整个物体的一部分,那么图像的分辨率就必然会增大。例如:
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(0, 1.5, 0, 1.5 *(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho(0,1.5*(GLfloat)w/(GLfloat)h, 0, 1.5, -10.0, 10.0);
}
可以把分辨率扩大4倍。

 

 

而如果再修改一下glViewport(0,0, 2 * (GLsizei)w, 2 * (GLsizei)h); 则可以把分辨率扩大16倍。

 

 

 

 

完整的测试程序:

/*Buildon ubuntu 9.04*/

#include<GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

void init(void)
{
    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat mat_shininess[] = {50.0};
    GLfloat light_position[] = {1.0, 1.0f, 1.0, 0.0};
    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
    
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 16);
    glFlush();
}

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-1.5, 1.5, -1.5 *(GLfloat)h/(GLfloat)w, 1.5 * (GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho(-1.5*(GLfloat)w/(GLfloat)h,1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

 

 

 

/*CMakeLists.txt*/

PROJECT(s5)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
ADD_EXECUTABLE(s5 main.cpp)


FIND_PACKAGE(OpenGL)
FIND_PACKAGE(GLUT)

IF(OPENGL_FOUND)
  INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR})
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OPENGL_LIBRARIES})
ELSE(OPENGL_FOUND)
  MESSAGE(FATAL_ERROR "OpenGL not found")
ENDIF(OPENGL_FOUND)

IF(GLUT_FOUND)
  INCLUDE_DIRECTORIES(${GLUT_INCLUDE_DIR})
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${GLUT_LIBRARIES})
ELSE(GLUT_FOUND)
ENDIF(GLUT_FOUND)

 

 

参考:OpenGL编程指南(原书第6版)

6、透视函数glFrustum(), gluPerspective()函数用法和glOrtho()函数用法.

OpenGL中,如果想对模型进行操作,就要对这个模型的状态(当前的矩阵)乘上这个操作对应的一个矩阵.
如果乘以变换矩阵(平移, 缩放, 旋转), 那相乘之后, 模型的位置被变换;
如果乘以投影矩阵(3D物体投影到2D平面), 相乘后, 模型的投影方式被设置;
如果乘以纹理矩阵(), 模型的纹理方式被设置.

而用来指定乘以什么类型的矩阵, 就是glMatriMode(GLenum mode);
glMatrixMode
3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理.

所以,在操作投影矩阵以前,需要调用函数:
glMatrixMode(GL_PROJECTION); //将当前矩阵指定为投影矩阵
然后把矩阵设为单位矩阵:
glLoadIdentity();

然后调用glFrustum()gluPerspective(),它们生成的矩阵会与当前的矩阵相乘,生成透视的效果;

1.glFrustum()


这个函数原型为:
void glFrustum(GLdouble left, GLdouble Right, GLdouble bottom,GLdouble top, GLdouble near, GLdouble far);
创建一个透视型的视景体。其操作是创建一个透视投影的矩阵,并且用这个矩阵乘以当前矩阵。这个函数的参数只定义近裁剪平面的左下角点和右上角点的三维空间坐标,即(leftbottom-near)和(righttop-near;最后一个参数far是远裁剪平面的离视点的距离值,其左下角点和右上角点空间坐标由函数根据透视投影原理自动生成。nearfar表示离视点的远近,它们总为正值(near/far 必须>0)

2.gluPerspective()



这个函数原型为:
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdoublezFar);
  创建一个对称的透视型视景体,但它的参数定义于前面的不同,如图。其操作是创建一个对称的透视投影矩阵,并且用这个矩阵乘以当前矩阵。参数fovy定义视野在Y-Z平面的角度,范围是[0.0, 180.0];参数aspect是投影平面宽度与高度的比率;参数NearFar分别是近远裁剪面到视点(沿Z负轴)的距离,它们总为正值。
  以上两个函数缺省时,视点都在原点,视线沿Z轴指向负方向。

void mydisplay (void)
{
     ......
    glMatrixMode (GL_PROJECTION);
    LoadIdentity ();
    Frustum (left, right, bottom, top, near, far);
    ......
}

3.glOrtho()


这个函数的原型为:
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdoublenear, GLdouble far) 

六个参数, 前两个是x轴最小坐标和最大坐标,中间两个是y,最后两个是z轴值
它创建一个平行视景体(就是一个长方体空间区域)。
实际上这个函数的操作是创建一个正射投影矩阵,并且用这个矩阵乘以当前矩阵。
其中近裁剪平面是一个矩形,矩形左下角点三维空间坐标是(leftbottom-near),
右上角点是(righttop-near);远裁剪平面也是一个矩形,左下角点空间坐标是(leftbottom-far),右上角点是(righttop-far)。
注意,所有的nearfar值同时为正或同时为负, 值不能相同。如果没有其他变换,正射投影的方向平行于Z轴,且视点朝向Z负轴。这意味着物体在视点前面时farnear都为负值,物体在视点后面时farnear都为正值。
只有在视景体里的物体才能显示出来。
如果最后两个值是(00),也就是nearfar值相同了,视景体深度没有了,整个视景体都被压成个平面了,就会显示不正确。

7、OpenGL函数 gluLookAt() glScalef() glTranslatef() glRotatef()glFrustum() glPerspective() 的使用

1.gluLookAt()——视图变换函数

把自己的眼睛当成是照相机,前三个参数表示眼睛的坐标,中间三个参数表示要拍照的物体的中心位置,可以理解成焦点吧,

后三个参数表示头顶的朝向,比如说头可以歪着(哈哈)。但是我测试过,如果歪的不对,原来的正前方现在已经不是正前方

了,那么就看不见物体了。举个例子:

gluLookAt (0.0,0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);//这个就表示头顶是朝着y方向

gluLookAt (0.0,0.0, 2.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0);//这个表示头歪了45度,头顶朝着(1.0,1.0,0.0)这个方向

2.glScalef() ——模型变换函数 缩放

void glScalef(GLfloat  x, GLfloat  y,  GLfloat  z);

模型变换的目的是设置模型的位置和方向,例如可以对模型进行旋转、移动和缩放,或者联合这几种操作。

这个函数表示模型在各轴上是如果进行缩放的。举个例子:

glScalef (1.0,2.0, 1.0);//表示y坐标值扩大两倍,这样原本方的物体就变成长的了。

3.glTranslatef() ——模型变换函数 移动

void glTranslatef(GLfloat  x, GLfloat  y,  GLfloat  z);

这个函数表示模型是怎样移动的。举个例子:

glTranslatef(-1.0,0.0,-2.0);//表示物体沿x负方向移动1.0,沿z轴负方向移动2.0。所以就好像能看见侧面一样

4.glRotatef()——模型变换函数  旋转

void glRotatef(GLfloat  angle, GLfloat  x,  GLfloat  y, GLfloat  z);

angle表示旋转的角度(注意单位不是弧度),(x,y,z)表示转轴。举个例子:

glRotatef(45.0,0.0, 0.0, 1.0);//表示模型沿着(0,0,1)这个轴旋转45°

5.glFrustum() ——投影变换函数 透视投影

前四个参数表示裁剪范围,后两个参数(要求为正数)表示近的面和远的面离眼睛的距离。有点儿像调焦。裁剪完后拉伸铺满整个屏幕。

6.glPerspective() ——投影变换函数 透视投影

void gluPerspective(GLdouble  fovy, GLdouble  aspect,  GLdouble  zNear, GLdouble  zFar);

第一个参数表示在y方向上的视角度数。个人理解:比如45,表示眼睛和模型中心这个条线与y轴的夹角。

第二个参数表示纵横比。x/y。比如2表示xy本来都是1个长度,现在x可以当两个长度用,这样,模型就好像被x方向压缩了一样。

后两个参数glFrustum()中后两个相近。要求都是正数。

8、 glLoadIdentity和glMultMatrix函数详解

1.    glMatrixMode (GL_PROJECTION);//表示把当前矩阵指定为用于投影变换  

2.      //视图矩阵GL_MODELVIEW、投影矩阵:GL_PROJECTION、纹理矩阵GL_TEXTURE  

1.   glLoadIdentity ();//把当前的可修改矩阵设置为4x4单位矩阵  

1.   glLoadMatrixf(const GLfloat *m);//把当前矩阵的16个值设置为m指定的值  

2.   glMultMatrixf(const GLfloat *m);//m指定的16个值作为一个矩阵,与当前矩阵相乘,并把结果存储在当前矩阵中  

1.   glLoadTransposeMatrixf(const GLfloat *m);//把当前矩阵的16个值设置为m指定的值  

2.   glMultTransposeMatrixf(const GLfloat *m);//m指定的16个值作为一个矩阵,与当前矩阵相乘,并把结果存储在当前矩阵中    

 

glLoadIdentity的作用是将当前模型视图矩阵转换为单位矩阵(行数和列数相同的矩阵,并且矩阵的左上角至右下角的连线上的元素都为1,其它元素都为0),这样可以方便矩阵的乘法运算。

glMultMatrix矩阵的乘法.

 如:

glLoadIdentity();

glMultiMatrix(A);

glMultiMatrix(B);

glMultiMatrix(C);

 

针对行向量和当前矩阵,上述代码可等价于:

(vx vy vz 1) *C*B*A

结果是一个1行4列的矩阵。

 

你可能会奇怪,为什么程序和实际矩阵的运算顺序是相反的呢。可以这么想像一下,glMultiMatrix先将矩阵加入到一个栈中保存,当所有的glMultiMatrix操作都完成后,再依次从栈中将缓存的矩阵取出并做相应的乘法操作。由于栈结构是先进后出的数据结构,所以可以猜想glMultiMatrix在执行时并没有直接将矩阵进行计算,而是先将矩阵放在了栈缓存中。

 

 

展开阅读全文
博主设置当前文章不允许评论。

没有更多推荐了,返回首页