OpenGl 之学习笔记

1.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() 保存当前矩阵

视图相关知识

OpenGL中的视图可以利用照相机来进行比拟。产生目标的场景的变过过程类似于相机拍照。此步骤大概分为三个:

1)把相机固定在三角架上,并让它对准场景(视图变换)

2)对场景安排,使得各个物体在招片中的位置是我们所希望的(模型变换)

3)选择照相机的镜头,并调整放大的倍数(投影变换)

4)确定最终照片的大小,放大(视口变换)

完成这些步骤就可以进行场景的绘制了。

在这个过程中,我们可以把场景长中的物体的坐标变换关系表示为如下情况:

物体坐标(顶点)——模型视图矩阵(视觉坐标)——投影矩阵(剪裁坐标)——透视除法(规范化设备坐标)——视口变换(窗口坐标)


视图变换

视图变化相当于对照相机进行操作。首先利用glLoadIdentity()函数对矩阵进行初始化,将当前的矩阵设定为单位矩阵。然后再把它与视图变换矩阵相乘。视图变换矩阵利用函数gluLookAt()函数进行指定。

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,
                                   GLdouble centerx,GLdouble centery,GLdouble centerz,
                                   GLdouble upx,GLdouble upy,GLdouble upz);
该函数定义一个视图矩阵,并与当前矩阵相乘。
第一组eyex, eyey,eyez 相机在世界坐标的位置
第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置
第三组upx, upy, upz 相机向上的方向在世界坐标中的方向。
通过此函数的设定,能够把相机的位置确定下来。详细的介绍见链接:http://blog.csdn.net/wangqinghao/article/details/14002077
视图变换函数必须要在调用任何模型变换函数之前调用,以确保首先作用于模型的是模型变换。可以这里利用矩阵的结合性质来理解。
视图变换矩阵A,模型变换矩阵B,视景体的坐标系统矩阵C。(A*B)*C=A*(B*C).
模型变化

有三个函数能够设定模型变换矩阵。glTranslate(),glRotate(),glScale().这些函数通过移动,旋转,拉伸,收缩或者反射对物体进行变换。

glMatrixMode()函数的参数有3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理。

GL_PROJECTION 投影:

投影变换:相当于为照相机选择镜头,这种变换的目的是为照相机选择视野(或者视景体)。因此确定哪些物体位于视野之内以及他们能够被看到的程度。投影变换决定了物体如何被投影到屏幕上。有两种投影。透视投影(perspective projection),它使得远处的物体看上去更小一些。透视投影有两个操作函数,glFrustum()或gluPerspective()。

通过它们生成的矩阵与当前的矩阵相乘生成相应的透视效果。


1.glFrustum()


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

2.gluPerspective()

这个函数原型为:
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);
  创建一个对称的透视型视景体,但它的参数定义于前面的不同,如图。其操作是创建一个对称的透视投影矩阵,并且用这个矩阵乘以当前矩阵。参数fovy定义视野在Y-Z平面的角度,范围是[0.0, 180.0],可以理解为眼睛上下睁开的幅度,角度值,值越小,视野范围越狭小(眯眼),值越大,视野范围越宽阔(睁开铜铃般的大眼);参数aspect是投影平面宽度与高度的比率,这个影响到视野的截面有多大;参数Near和Far分别是近远裁剪面到视点(沿Z负轴)的距离,它们总为正值。
  以上两个函数缺省时,视点都在原点,视线沿Z轴指向负方向。

另一类投影叫做正投影(orthographic projection),它把物体直接映射到屏幕上而不影响它们的大小。和透视投影不同,正投影的物体的两端的大小没有不同,物体和照相机的距离并不影响它们看上去的大小。这种类型的投影用于建筑蓝图和计算机辅助设计的应用程序。

glOrtho()函数创建一个正交平行的视景体。

glOrtho(left, right, bottom, top, near, far), left表示视景体左面的坐标,right表示右面的坐标,bottom表示下面的,top表示上面的。

关于此函数可以理解为通过设定参数,对视景体进行剪裁。

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主要完成这样的功能。它负责把视景体截取的图像按照怎样的高和宽显示到屏幕上。

2. glTexCoord2f() 函数

原型:glTexCoord2f(GLfloat s,GLfloat t);
s代表x坐标,t代表y坐标;
s∈[0.0,1.0],t∈[0.0,1.0];
一张位图的4个坐标顶点分别为:左下角(0.0,0.0),右下角(1.0,0.0)右上角(1.0,1.0),左上角(0.0,1.0)。0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。

用法:通常配合glVertex3fv使用,glTexCoord2f用来定义纹理左边,glVertex3fv用来定义几何定点坐标。

注意:opengl2.0之前载入的位图大小要求是2的n次幂(即1,2,4,8,16,32等),否则纹理将无法显示。虽然opengl2.0之后不要求纹理是2的n次幂了,但这不保证性能。考虑性能的话,一般还是把纹理做成2的n次幂。

如果左右搞反了:为了将纹理正确的映射到四边形上,必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。

简单示例

glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, 1.0 );

纹理相关知识

概述

     概括的说, 纹理映射机制允许你将一个图像关联到一个多边形上,从而呈现出真实视觉效果。例如, 你可以将书的封面图像应用到一个方形上, 这样这个方形看起来就像是一本书了。 你可以将地球的地图通过纹理映射应用到一个球体上, 那么这个球体就是一个3D的具真实感的地球了。纹理映射在当今的3D图形应用上处处皆是。游戏都是通过纹理映射来作为虚拟真实的第一个步骤。然而纹理加载的过程可能会影响程序运行速度,当纹理图像非常大时,这种情况尤为明显。如何妥善的管理纹理,减少不必要的开销,是系统优化时必须考虑的一个问题。其中OpenGL提供了纹理对象对象管理技术来解决上述问题。与显示列表一样,纹理对象通过一个单独的数字来标识。这允许OpenGL硬件能够在内存中保存多个纹理,而不是每次使用的时候再加载它们,从而减少了运算量,提高了速度。

    纹理映射是一个二维的数组。数组中的每一项称之为纹理点( texel )。 虽然这个数组是二维的, 但是可以映射到非二维的对象上, 如球体或者其他的 3D 对象模型上。

    比较常见的是, 开发者在他们的图形应用中运用二维纹理, 当然一维或者三维的纹理也并非未闻。二维纹理有宽度和高度决定二维。一维纹理也有宽度和高度, 只是高度被设为值 1(单位:像素 pixel). 而三维纹理不仅具有宽度和高度, 还有深度, 所以三维为纹理又称为立体纹理。我们讨论的主要是二维纹理。

大概步骤:

1.创建纹理对象,并为他指定一个纹理.

2.确定纹理如何应用到每个像素上.

3.启用纹理贴图

4.绘制场景,提供纹理和几何坐标

过滤: 由于我们提供的纹理图像很少能和最终的屏幕坐标形成对应,大小不同,所以需要设置过滤项目.允许我们进行插值或者匀和,指定放大缩小的函数.glTexParameter*(),使用过滤模式GL_NEAREST那么纹理单位最邻近的将被使用,GL_LINEAR那么就用2*2的包含纹理数据的数组加权组作为纹理;

命名纹理对象: glGenTexures(GLSize n,Gluint *textureNames); n为产生n个未使用的对象值,textureNames为纹理名字数组,你可能有几个纹理需要使用,这个数组来区分.

1.你需要载入图片时候的纹理定义

先看glTexImage2D()函数定义:

void glTexImage2D(GLenum target,
          GLint level,
          GLint internalFormat,
          GLsizei width,
          GLsizei height,
          GLint border,
          GLenum format,
          GLenum type,
          const GLvoid * data);

定义一个二维纹理映射。target是常数 GL_TEXTURE_2D, level表示多级分辨率的纹理图象的级数。若只有一种分辨率,level为0。components是从1到4的整数,1:选择R;2:选择R A;3:选择R G B;

再看glTexImage2D()用法举例:

glTexImage2D(GL_TEXTURE_2D,  //此纹理是一个2D纹理
        0,          //代表图像的详细程度, 默认为0即可
        3,          //颜色成分R(红色分量)、G(绿色分量)、B(蓝色分量)三部分,若为4则是R(红色分量)、G(绿色分量)、B(蓝色分量)、Alpha
        TextureImage[0]->sizeX,  //纹理的宽度
        TextureImage[0]->sizeY,  //纹理的高度
        0,          //边框的值
        GL_RGB,      //告诉OpenGL图像数据由红、绿、蓝三色数据组成
        GL_UNSIGNED_BYTE,    //组成图像的数据是无符号字节类型
        TextureImage[0]->data);  //告诉OpenGL纹理数据的来源,此例中指向存放在TextureImage[0]记录中的数据

先看gluBuild2DMipmaps()函数定义:

int gluBuild2DMipmaps (GLenum target,
            GLint components,
            GLint width,
            GLint height,
            GLenum format,
            GLenum type,
            const void *data);

再看gluBuild2DMipmaps()用法举例:
gluBuild2DMipmaps(GL_TEXTURE_2D,  //此纹理是一个2D纹理
          3,            //颜色成分R(红色分量)、G(绿色分量)、B(蓝色分量)三部分,若为4则是R(红色分量)、G(绿色分量)、B(蓝色分量)、Alpha
          TextureImage[0]->sizeX,  //纹理的宽度
          TextureImage[0]->sizeY,  //纹理的高度
          GL_RGB,       //告诉OpenGL图像数据由红、绿、蓝三色数据组成
          GL_UNSIGNED_BYTE,    //组成图像的数据是无符号字节类型
          TextureImage[0]->data);  //告诉OpenGL纹理数据的来源,此例中指向存放在TextureImage[0]记录中的数据

glTexImage2D()和gluBuild2DMipmaps()使用注意事项:

使用glTexImage2D()时所采用的位图文件分辨率必须为:64×64、128×128、256×256三种格式,如果其他大小则会出现绘制不正常。

gluBuild2DMipmaps()支持任意分辨率位图文件。

绑定纹理对象: glBindTexture(Glenum target,Gluint,glTexImage*),将把数据存储到这个纹理对象中,如果需要纹理图像的颜色和物体表面的颜色进行组合,不是直接贴图,那么就需要glTexEvn*()函数.

确定纹理坐标: glTexCoord2f(1.of,1.Of);glVertex3f(1.Of,1.Of,0.Of);比如这一句话.对于设置纹理贴图的坐标和绘图坐标的确定问题.一般的情况假设纹理和图片都是正方形的,那么我们希望纹理映射到整个物体上面,这个时候纹理坐标按照逆时针放心依次(0,0),(1,0),(1,1),(0,1),其中的四个坐标只是代表的向量,并不是真实的坐标,如果是要一半贴到物体上就应该是0.5的值了,假如你给的纹理坐标大于1,那么将会贴多个纹理,比如值为2的时候,会有4个纹理贴图.

基本函数

纹理映射 glEnable() 和 glDisable() 来开闭;

纹理对象  glGenTexture(Glsizei n, GLuint *texture);

纹理绑定 glBindTexture(GLenum target, GLuint texture);

删除纹理对象  glDeleteTexure(Glsizei n, Gluint *texture);

驻留纹理 GLboolean glAreTexturesResident (GLsizei n, GLuint *textures, GLboolean *residents);

纹理优先级 glPrioritizeTextures (GLsizei n, GLuint *textures, GLclampf *priorities);

指定纹理  glTexImage1D(), glTexImage2D(), glTexImage3D(). 这三个版本用于相应维数的纹理;

纹理过滤 :

glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S/T,GL_REPEAT/GL_CLAMP); 

  • GL_REPEAT:当纹理比表面小时重复使用纹理以填满每个点。
  • GL_CLAMP:比1大的当作1,比0小的当作0。

glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MAG/MIN_FILTER,GL_NEAREST/GL_LINEAR)

  • GL_NEAREST:取比较接近的那个像素。
  • GL_LINEAR:以周围四个像素的平均值做为纹理。
  • bilinear:二次插值,精度更高,但需要自己动手计算。

若是mip 贴图模式 

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST/LINEAR);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST/LINEAR);

3. glNormal3f 函数

void glNormal3f( GLfloat nx, GLfloat ny, GLfloat nz )
void glNormal3fv( const GLfloat *v )

但是第一个的参数就是浮点型的三个数,分别代表法向量的x、y、z。
第二个的参数是一个三元素数组的首地址,这个三元素数组分别代表法向量的x、y、z。

使用光源相关知识

使用光源

要模拟真实世界,仅有环境光是不够的,需要指定更多的光源来提升真实感。OpenGL至少提供8种光源,可以在场景中的任意位置甚至是可视区域之外。你可以指定光源位于无限远处以获得平行光束,或者光源位于近处向外发射光束。还可以制造出聚光灯的效果。

选择哪种方式?

你可以指定一个光源位于哪里朝哪个方向发射光线。通常光源朝所有方向发射光线,但也可以为它指定方向。在指定的方向的光源环境下,并非每个多边形都需要被照射到。我们可以制造物体的阴影效果。OpenGL可以计算光线和物体的角度。

如下图:光源发射出的光线照射到四边形上,形成一个入射角,再反射到观察者眼中形成一个反射角。根据这个入射角和反射角结合光源和材料的属性我们可以计算出该点应呈现出什么样的颜色。

image

在编程的角度上看,每一个多边形由一系列的顶点创建的。多边形就是由点组成的,那么如何计算光线与点之间的角度。我们无法在3D空间中找到一个切确的线与点之间的角度,因此我们为每一个点设置一个法线。三维平面的法线是垂直于该平面的三维向量。曲面在某点P处的法线为垂直于该点切平面的向量。(wiki)

平面法线

法线向量是一条垂直于真实平面或虚拟平面的线。下图是2D和3D中的法线向量。

image

为什么需要为每个顶点设置法线向量,而不是为一个多边形指定一个法线向量。其中的原因是,并不是所有的物体的表面都是平的,有时法线向量并不需要精确地垂直于物体的表面。通过扭曲平面的法线向量可以制造出光滑的曲面的视觉效果。

指定法线

image

如上图:为多边形的顶点(1,1,0)处指定一个法线向量,我们可以选定一另个点(1,10,0),并以(1,1,0)为起点连接第二个点(1,10,0),所形成的线就是法线向量。第二个点说明了法线向量的方向是y轴向上。法线向量的方向还可以用于表明多边形的正面和反面。法线是由正面指向外面的(或者说从法线的箭头方向往下看到的就是多边形的正面)。

用法线的第二个点减去第一个顶点,所得的结果就是相对于x、y和z的单位距离。

(1,10,0) - (1,1,0) = (0,9,0)

如果把这个顶点移动到原点,上面两点相减得出的点指定了和表面呈90度角的方向。如下图:

Image[1]

向量的方向告诉OpenGL顶点所在多边形的面朝哪个方向的。下面的例子演示如何指定法线向量

glBegin(GL_TRIANGLES);

    glNormal3f(0.0f, -1.0f, 0.0f);

    glVertex3f(0.0f, 0.0f, 60.0f);

    glVertex3f(-15.0f, 0.0f, 30.0f);

    glVertex3f(15.0f, 0.0f, 30.0f);

glEnd();

glNormal3f接受3个表示坐标的值,指定一条垂直于三角形表面的法线向量。上面的例子的三个顶点的法线具有相同的方向。

单位法线

单位法线即长度为1的法线向量。已知法线向量(x,y,z)求单位法线,先求出法线向量的长度即image,再用(x,y,z)除以这个长度,就得到单位法线。这个过程叫归一化(normalization)。在光照计算的中所有的法线向量都会归一化。

可以让OpenGL自动把法线向量转换为单位法线,通过调用glEnable接受一个参数GL_NORMALIZE;

glEnable(GL_NORMALIZE);

这种做法在某些实现上,会带来性能的开销。最好的方式在指定法线向量的时候,就直接指定为单位法线,而不是依靠OpenGL来帮你做转换。

PS:glScale缩放函数也会缩放法线的长度。如果一起使用glScale和光照,有可能会得到不是你想要的光照效果。

如果每个顶点指定的法线都是单位法线,并且glScale使用相同的比例进行缩放的情况下,有一个替代方案是使用GL_RESCALE_NORMALS替代GL_NORMALIZE.

glEnable(GL_RESCALE_NORMALS);

这样就告诉了OpenGL,你的法线向量不是单位长度的,但是可以通过相同比例因子的缩放来把它转化为单位长度(单位法线)。这样OpenGL会检查你的模型视图变换矩阵来逆转换。这样在每个顶点所做的数学操作要少一些。

个人理解:

一个单位法线向量(1.0,1.0,1.0),在进行过glScalef(2.0, 2.0, 2.0)之后,就变为了(2.0,2.0,2.0),那么在启用了GL_RESCALE_NORMALS之后,OpenGL会检查模型视图矩阵得知,可以通过反向的缩放把法线向量转换会单位法线即等比例的缩小2倍乘以0.5.又得到了(1.0, 1.0, 1.0)。

PS:最佳实践是在一开始为顶点指定法线时,就指定为单位法线。

寻找法线

当一个多边形不平行于任何一个轴面时,通过简单的观察来指定一个法线会比较困难。所以需要一个简单的方法来计算3D空间中任意多边形的法线。

image

可以通过多边形上的任意三个点来计算法线。

image

如上图,知道了P1,P2,P3点之后。由P1和P2可以求出向量V1,P1和P3可以求出向量V2,再用V1 X V2(叉乘)计算得出正交于V1和V2的法线向量。

image

math3d代码示例:

void m3dFindNormal(M3DVector3f vNormal, const M3DVector3f vP1, const M3DVector3f vP2, const M3DVector3f vP3)
{
    M3DVector3f v1, v2;
    v1[0] = vP2[0] - vP1[0];
    v1[1] = vP2[1] - vP1[1];
    v1[2] = vP2[2] - vP1[2];

    v2[0] = vP3[0] - vP1[0];
    v2[1] = vP3[1] - vP1[1];
    v2[2] = vP3[2] - vP1[2];

    m3dCrossProduct(vNormal, v1, v2);
}

设置光源

创建一个位于左上角的温和的白光光源。

GLfloat ambientLight[] = {0.3f, 0.3f, 0.3f, 1.0f};
GLfloat diffuseLight[] = {0.7f, 0.7f, 0.7f, 1.0f};

//设置和开启光源light0
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);

//开启光照
glEnable(GL_LIGHT0);

//设置光源位置
GLfloat lightPos[] = {-50.f, 50.f, 100.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

lightPos中最后一个值1.0说明这个lightPos指定了光源的位置,如果这个值是0.0,则指定了光源在无限远处。

设置材料属性

开启颜色追踪

glEnable(GL_COLOR_MATERIAL);

glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

设置多边形和法线向量

...
{
    M3DVector3f vPoints[3] = {{ 15.0f, 0.0f,  30.0f},
    { 0.0f,  15.0f, 30.0f},
    { 0.0f,  0.0f,  60.0f}};

    // 计算法线向量
    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }   
...
喷气式飞机的完整代码:
// LitJet.cpp
// OpenGL SuperBible
// Demonstrates OpenGL Lighting
// Program by Richard S. Wright Jr.

#include "gltools.h"       // gltools library
#include "math3d.h"        // 3D Math Library

// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;


// Called to draw scene
void RenderScene(void)
{
  M3DVector3f vNormal;    // Storeage for calculated surface normal

  // Clear the window with current clearing color
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Save the matrix state and do the rotations
  glPushMatrix();
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);


  // Nose Cone - Points straight down
  // Set material color
  glColor3ub(128, 128, 128);
  glBegin(GL_TRIANGLES);
  glNormal3f(0.0f, -1.0f, 0.0f);
  glNormal3f(0.0f, -1.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 60.0f);
  glVertex3f(-15.0f, 0.0f, 30.0f);
  glVertex3f(15.0f,0.0f,30.0f);


  // Verticies for this panel
  {
    M3DVector3f vPoints[3] = {{ 15.0f, 0.0f,  30.0f},
    { 0.0f,  15.0f, 30.0f},
    { 0.0f,  0.0f,  60.0f}};

    // Calculate the normal for the plane
    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }    


  {
    M3DVector3f vPoints[3] = {{ 0.0f, 0.0f, 60.0f },
    { 0.0f, 15.0f, 30.0f },
    { -15.0f, 0.0f, 30.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  // Body of the Plane 
  {
    M3DVector3f vPoints[3] = {{ -15.0f, 0.0f, 30.0f },
    { 0.0f, 15.0f, 30.0f },
    { 0.0f, 0.0f, -56.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{ 0.0f, 0.0f, -56.0f },
    { 0.0f, 15.0f, 30.0f },
    { 15.0f,0.0f,30.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  glNormal3f(0.0f, -1.0f, 0.0f);
  glVertex3f(15.0f,0.0f,30.0f);
  glVertex3f(-15.0f, 0.0f, 30.0f);
  glVertex3f(0.0f, 0.0f, -56.0f);

  ///
  // Left wing
  // Large triangle for bottom of wing
  {
    M3DVector3f vPoints[3] = {{ 0.0f,2.0f,27.0f },
    { -60.0f, 2.0f, -8.0f },
    { 60.0f, 2.0f, -8.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  {
    M3DVector3f vPoints[3] = {{ 60.0f, 2.0f, -8.0f},
    {0.0f, 7.0f, -8.0f},
    {0.0f,2.0f,27.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{60.0f, 2.0f, -8.0f},
    {-60.0f, 2.0f, -8.0f},
    {0.0f,7.0f,-8.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{0.0f,2.0f,27.0f},
    {0.0f, 7.0f, -8.0f},
    {-60.0f, 2.0f, -8.0f}};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  // Tail section///
  // Bottom of back fin
  glNormal3f(0.0f, -1.0f, 0.0f);
  glVertex3f(-30.0f, -0.50f, -57.0f);
  glVertex3f(30.0f, -0.50f, -57.0f);
  glVertex3f(0.0f,-0.50f,-40.0f);

  {
    M3DVector3f vPoints[3] = {{ 0.0f,-0.5f,-40.0f },
    {30.0f, -0.5f, -57.0f},
    {0.0f, 4.0f, -57.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  {
    M3DVector3f vPoints[3] = {{ 0.0f, 4.0f, -57.0f },
    { -30.0f, -0.5f, -57.0f },
    { 0.0f,-0.5f,-40.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{ 30.0f,-0.5f,-57.0f },
    { -30.0f, -0.5f, -57.0f },
    { 0.0f, 4.0f, -57.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{ 0.0f,0.5f,-40.0f },
    { 3.0f, 0.5f, -57.0f },
    { 0.0f, 25.0f, -65.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  {
    M3DVector3f vPoints[3] = {{ 0.0f, 25.0f, -65.0f },
    { -3.0f, 0.5f, -57.0f},
    { 0.0f,0.5f,-40.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }

  {
    M3DVector3f vPoints[3] = {{ 3.0f,0.5f,-57.0f },
    { -3.0f, 0.5f, -57.0f },
    { 0.0f, 25.0f, -65.0f }};

    m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
    glNormal3fv(vNormal);
    glVertex3fv(vPoints[0]);
    glVertex3fv(vPoints[1]);
    glVertex3fv(vPoints[2]);
  }


  glEnd();

  // Restore the matrix state
  glPopMatrix();
  // Display the results
  glutSwapBuffers();
}

// This function does any needed initialization on the rendering
// context. 
void SetupRC()
{
  // Light values and coordinates
  GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
  GLfloat  diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };

  glEnable(GL_DEPTH_TEST);    // Hidden surface removal
  glFrontFace(GL_CCW);        // Counter clock-wise polygons face out
  glEnable(GL_CULL_FACE);        // Do not calculate inside of jet

  // Enable lighting
  glEnable(GL_LIGHTING);

  // Setup and enable light 0
  glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
  glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
  glEnable(GL_LIGHT0);

  // Enable color tracking
  glEnable(GL_COLOR_MATERIAL);

  // Set Material properties to follow glColor values
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

  // Light blue background
  glClearColor(0.0f, 0.0f, 1.0f, 1.0f );

  glEnable(GL_NORMALIZE);
}

/
// Handle arrow keys
void SpecialKeys(int key, int x, int y)
{
  if(key == GLUT_KEY_UP)
    xRot-= 5.0f;

  if(key == GLUT_KEY_DOWN)
    xRot += 5.0f;

  if(key == GLUT_KEY_LEFT)
    yRot -= 5.0f;

  if(key == GLUT_KEY_RIGHT)
    yRot += 5.0f;

  if(key > 356.0f)
    xRot = 0.0f;

  if(key < -1.0f)
    xRot = 355.0f;

  if(key > 356.0f)
    yRot = 0.0f;

  if(key < -1.0f)
    yRot = 355.0f;

  // Refresh the Window
  glutPostRedisplay();
}


//
// Reset projection and light position
void ChangeSize(int w, int h)
{
  GLfloat fAspect;
  GLfloat lightPos[] = { -50.f, 50.0f, 100.0f, 1.0f };

  // Prevent a divide by zero
  if(h == 0)
    h = 1;

  // Set Viewport to window dimensions
  glViewport(0, 0, w, h);

  // Reset coordinate system
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  fAspect = (GLfloat) w / (GLfloat) h;
  gluPerspective(45.0f, fAspect, 1.0f, 225.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
  glTranslatef(0.0f, 0.0f, -150.0f);
}

int main(int argc, char* argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(800,600);
  glutCreateWindow("Lighted Jet");
  glutReshapeFunc(ChangeSize);
  glutSpecialFunc(SpecialKeys);
  glutDisplayFunc(RenderScene);
  SetupRC();
  glutMainLoop();

  return 0;
}

4. glBufferData() 函数

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBufferData(目标缓冲器类型,传输数据的字节大小,发送的实际数据,显卡如何管理给定的数据);

        第四个参数指定了我们希望显卡如何管理给定的数据。它有三种形式:

 

GL_STATIC_DRAW :数据不会或几乎不会改变。
GL_DYNAMIC_DRAW:数据会被改变很多。
GL_STREAM_DRAW :数据每次绘制时都会改变。
 

        三角形的位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是GL_STATIC_DRAW。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。

        现在已经把顶点数据储存在显卡的内存中,用VBO这个顶点缓冲对象管理。下面会创建一个顶点和片段着色器来真正处理这些数据,接下来需要创建顶点着色器和片段着色器。

 

5. glVertexAttribPointer函数

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

glVertexAttribPointer()函数的参数如下:

 

第一个参数指定我们要配置的顶点属性。还记得我们在顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值(Location)吗?它可以把顶点属性的位置值设置为0。因为我们希望把数据传递到这一个顶点属性中,所以这里我们传入0。
第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
第四个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔,我们在后面会看到更多的例子(译注: 这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节)。
第六个参数的类型是void*,所以需要我们进行强制类型转换。它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。
 

        补充:每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取则是通过在调用glVetexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。由于在调用glVetexAttribPointer之前绑定的是先前定义的VBO对象,顶点属性0现在会链接到它的顶点数据。

 

6. glDrawArrays()函数

 

glDrawArrays(GL_TRIANGLES, 0, 3);

第一个参数是我们打算绘制的OpenGL图元的类型。由于我们在一开始时说过,我们希望绘制的是一个三角形,这里传递GL_TRIANGLES给它。
第二个参数指定了顶点数组的起始索引,我们这里填0。
第三个参数(最后一个)指定我们打算绘制多少个顶点,这里是3(我们只从我们的数据中渲染一个三角形,它只有3个顶点长)。

 

参考链接:

OpenGL 使用光源类型和使用多个光源

mip 贴图模式示例

OpenGL超级宝典笔记——纹理映射(一)

OpenGL纹理映射总结

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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ppipp1109

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值