Sy = UzVx - UxVz
Sz = UxVy - UyVx
上述公式中,U、V为两不平行向量,S为U、V的外积(即U×V)。计算完外积之后,我们还需要将得到的向量转换为单位向量。只要将一个向量的x、y、z因子全部除以该向量的模就可以得到单位向量。因此,可以使用下面的代码计算一个三角形的法线向量。
请注意应该将三角形的顶点按逆时针顺序传给函数以获得正确结果。
通过计算得到法线向量之后,我们需要在绘制顶点之前调用glNormal函数为顶点或图元指定法线。例如: glBegin(GL_TRIANGLES);
glNormal3f(1,1,1); //为即将绘制的三角形指定法线
glVertex3f(1,0,1);glVertex3f(1,1,1);glVertex3f(1,0,0); glEnd();
除此之外,也可以为同一图元的不同顶点指定不同法线向量。例如: glBegin(GL_TRIANGLES); glNormal3f(1,1,1); glVertex3f(1,0,1);
glNormal3f(1,0,1); glVertex3f(1,1,1);
glNormal3f(0,1,1); glVertex3f(1,0,0); glEnd(); 5 材质
指定了图元的法线之后,我们还需要为其指定相应的材质以决定物体对各种颜色的光的反射程度,这将影响物体表现为何种颜色。和光源的成分相似,材质也分为漫射光反光率、平行光反光率。材质的漫射光成分将决定该材质对环境中的漫射光的反射程度,相似的,平行光成分将决定材质对平行光的反射程度。与设置光源相似,我们只需在绘制图元之前调用glMaterialf或glMaterialfv函数就能对即将绘制的图元的材质的各项参数进行设定。 5.1 设置材质对各种光的反光率 调用:
glMaterialfv(GL_FRONT,GL_DIFFUSE,@Diffuse);
可以指定材质对漫射光的反射率。其中,第一个参数决定该材质运用于图元的正面还是反面。可以取GL_FRONT(正面),GL_BACK(反面),GL_FRONT_AND_BACK(正反两面)。第2个值表示对何种光进行设置,GL_DIFFUSE表示对漫射光反射率进行设置,可以取GL_AMBIENT(环境光)、GL_DIFFUSE(漫射光)、
GL_AMBIENT_AND_DIFFUSE(环境光和漫射光)、GL_SPECULAR(平行/镜面光)。而第三个参数是一个四维数组,这个数组描述了反光率的RGBA值,每一项取值都为0-1之间。例如
Diffuse[4]={1,0,0,1};
glMaterialfv(GL_FRONT,GL_DIFFUSE,Diffuse); 将会使物体在有红色成分的光照下表现为红色。 5.2 使用颜色跟踪
在启用光照系统之后,为图元指定颜色变得不太方便。首先我们需要创建一个数组,然后调用glMaterial函数将数组传给材质,以此决定物体的颜色。为了简便,我们可以开启颜色跟踪来简化代码。调用 glEnable(GL_CORLOR_MATERIAL); 启动颜色跟踪,再调用
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
来决定对物体的正面还是反面,对环境光、镜面光还是漫射光进行颜色跟踪。第一个参数可以取GL_FRONT、GL_BACK、GL_FRONT_AND_BACK中的任意一种,第二个参数可以取GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE、GL_SPECULAR中的任意一种。
启动颜色跟踪之后,我们就可以像以前一样,使用glColor函数来指定图元的颜色了。这时,OpenGL将自动根据从glColor函数传递的颜色来决定物体材质,省去了我们手工指定材质的麻烦。 例如,要在启用光照系统的前提下绘制一个红色的三角形,可以这样做: glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE); glColor3ub(255,0,0); glBegin(GL_TRIANGLES);
glVertex3f(1,0,1);glVertex3f(1,1,1);glVertex3f(1,0,0); glEnd();
5.3 材质的镜面指数
当光源中含有镜面光成分,且镜面光较强时,一些光滑的物体便会出现一些高亮的焦点
我们可以通过设置材质的镜面指数来确定光斑的大小和聚焦程度。调用 glMateriali(GL_FRONT,GL_SHININESS,N );
可以对镜面指数进行设定。如果N值越大,则光斑尺寸越小,物体越有光泽,反之越大。N值可取1-128之间的任意整数。 5.4 辐射性材质
有些物体,本身会发光。我们可以设置材质的Emission成分来使物体看起来有发光效果。只需添加如下代码:
int LightEmission[4]={1,1,0,1}
glMaterialfv(GL_FRONT,GL_EMISSION,LightEmission);
其中,LightEmission表示物体所发光的颜色的RGBA值。 转:OpenGL基本概念入门5——纹理贴图2 2009-05-04 19:21 3.2 纹理缠绕
前面提到,纹理坐标应位于0-1之间。那么当纹理坐标大于这个值会出现什么情况呢?
我们可以对OpenGL进行设置,以决定当纹理坐标不位于这一区间时应采取的操作。我们可以指定两种操作:GL_CLAMP和GL_REPEAT。对于GL_CLAMP,超出纹理坐标的区域会使用纹理图像的边界颜色来代替,如图6.4-2所示。
图6.4-2 GL_CLAMP 的工作方式
而GL_REPEAT方式则是对纹理坐标进行重置而得到重复的图像。观察图6.4-3,你就能很容易地发现这一点。
图6.4-3 GL_REPEAT 的工作方式
可以调用glTexParameter设置缠绕方式:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,WrapMode);//在s方向上的缠绕方式
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,WrapMode);//在t方向上的缠绕方式
其中,WrapMode可取GL_CLAMP或者GL_REPEAT。 4.纹理对象
创建和使用文理对象
在OpenGL中,我们使用glGenTextures创建纹理对象: glGenTextures(Count:Integer;TexObjs:Pointer);
其中,Count是我们要创建的纹理数目,当我们只想创建一个纹理时,只需调用
var Texture:GLUint; ...
glGenTextures(1,@Texture);
这样,Texture变量中就存储了我们创建的纹理的ID号。
创建之后,我们使用glBindTexture将创建的纹理绑定到当前纹理。这样所有的纹理函数都将针对当前纹理。
glBindTexture(Texture:GLUint);
这样,我们就可以调用glTexParameter、glTexImage2D等函数来设置这个纹理对象了。 删除纹理对象
在纹理资源使用完毕后(一般是程序退出或场景转换时),一定要删除纹理对象,释放资源。 调用
glDeleteTextures(Count:Integer;TexObj:Pointer); 来删除纹理对象。例如
glDeleteTextures(1,@Texture); 5. 多贴图纹理
多贴图纹理(Mip Mapping)为一个纹理对象生成不同尺寸的图像。在需要时,根据绘制图形的大小来决定采用的纹理等级或者在不同的纹理等级之间进行线性内插。使用多贴图纹理的好处在于消除纹理躁动。这种情况在所绘制的景物离观察者较远时常常发生(如图6.6-1和6.6-2)。由于多贴图纹理现在的渲染速度已经很快,以至于和普通纹理没有什么区别,我们现在一般都使用多贴图纹理。
图6.6-1 GL_LINEAR滤镜下的远图6.6-1 使用多纹理贴图的远
景 景
使用多贴图纹理并不麻烦。首先,我们需要创建不同等级(尺寸)的纹理
图片。我们需要调用n次glTexImage2D函数,生成不同等级的纹理贴图。例如: glTexImage2D(GL_TEXTURE_2D,0,3,8,8,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
glTexImage2D(GL_TEXTURE_2D,1,3,4,4,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
glTexImage2D(GL_TEXTURE_2D,2,3,2,2,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
glTexImage2D(GL_TEXTURE_2D,3,3,1,1,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
这些函数调用的第二个参数表示当前纹理的等级。0级的分辨率最大。之后,每一级的分辨率是上一级分辨率的一半。这样的函数调用应一直进行下去,直至图像的高度和宽度都为1。
但有时候,这样做总并不是很方便。我们可以借助一个glu函数帮我们自动生成这些多贴图纹理。只需要把生成纹理图像的函数调用由glTexImage2D改为gluBuild2DMipMaps即可:
gluBuild2DMipMaps(GL_TEXTURE_2D,3,Bit.Width,Bit.Height,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
此外,还必须把纹理的滤镜改为MIP_MAP滤镜。例如:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 转:OpenGL基本概念入门5——纹理贴图1 2009-05-04 19:21 1. 创建纹理图像
OpenGL要求纹理的高度和宽度都必须是2的n次方大小,只有满足这个条件,这个纹理图片才是有效的。 一旦获取了像素值,我们就可以将这些数据传给OpenGL,让OpenGL生成一个纹理贴图: glGenTextures(1,@Texture);
glBindTexture(GL_TEXTURE_2D,Texture);
glTexImage2D(GL_TEXTURE_2D,0,3,Bit.Width,Bit.Height,0,GL_RGB,GL_UNSIGNED_BYTE,Pixels);
glGenTextures和glBindTexture函数用于创建和绑定纹理对象,
glTexImage2D函数将Pixels数组中的像素值传给当前绑定的纹理对象,于是便创建了纹理。glTexImage函数的参数分别是纹理的类型,纹理的等级,每个像素的字节数,纹理图像的宽度和高度,边框大小,像素数据的格式,像素值的数据类型,像素数据。 2. OpenGL中的贴图方式
OpenGL为我们提供了三种纹理——GL_TEXTURE_1D、GL_TEXTURE_2D和GL_TEXTURE_3D。它们分别表示1维纹理、2维纹理和3维纹理。无论是哪一中纹理,使用方法都是相同的:即先创建一个纹理对象和一个存储纹理数据的n维数组,在调用glTexImageN D函数来传入相应的纹理数据。除此之外,我们可以一些函数来设置纹理的其他特性。 2.1 设置贴图模式
OpenGL提供了3种不同的贴图模式:GL_MODULATE,GL_DECAL和GL_BLEND。默认情况下,贴图模式是GL_MODULATE,在这种模式下,OpenGL会根据当前的光照系统调整物体的色彩和明暗。第二种模式是GL_DECAL,在这种模式下所有的光照效果都是无效的,OpenGL将仅依据纹理贴图来绘制物体的表面。最后是GL_BLEND,这种模式允许我们使用混合纹理。在这种模式下,我们可以把当前纹理同一个颜色混合而得到一个新的纹理。我们可以调用glTexEnvi函数来设置当前贴图模式:
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,TextureMode);
其中TextureMode就是想要设置的纹理模式,可以为GL_MODULATE,GL_DECAL和GL_BLEND中的任何一种。
另外,对于GL_BLEND模式,我们可以调用
glTexEnvfv(GL_TEXUTRE_ENV,GL_TEXTURE_ENV_COLOR,@ColorRGBA); 其中,ColorRGBA为一个表示RGBA颜色的4维数组。 2.2 纹理滤镜
在纹理映射的过程中,如果图元的大小不等于纹理的大小,OpenGL便会对纹理进行缩放以适应图元的尺寸。我们可以通过设置纹理滤镜来决定OpenGL对某个纹理采用的放大、缩小的算法。
调用glTexParameter来设置纹理滤镜。如:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILETER, MagFilter);//设置放大滤镜