OpenGL快速入门示例-OpenGL纹理

OpenGL纹理


        在三维图形中,纹理映射(Texture Mapping)的方法运用得很广,尤其描述具有真实感的物体。比如绘制一面砖墙,就可以用一幅真实的砖墙图像或照片作为纹理贴到一个矩形上,这样,一面逼真的砖墙就画好了。如果不用纹理映射的方法,则墙上的每一块砖都必须作为一个独立的多边形来画。另外,纹理映射能够保证在变换多边形时,多边形上的纹理图案也随之变化。例如,以透视投影方式观察墙面时,离视点远的砖块的尺寸就会缩小,而离视点 较近的就会大些。此外,纹理映射也常常运用在其他一些领域,如飞行仿真中常把一大片植被的图像映射到一些大多边形上用以表示地面,或用大理石、木材、布匹等自然物质的图像作为纹理映射到多边形上表示相应的物体。

纹理分类


按照纹理的使用场景和表现形式来分,纹理主要分为以下几类:

  • 一维纹理,例如,程序所绘制的带纹理的镶条的所有变化可能发生在同一个方向,一维纹理就像一个高度为1的二维纹理。
  • 二维纹理,其实是最容易理解的,也是最常用的,具有横向和纵向纹理坐标的,通常一个图片可以用作一个二维纹理。
  • 三维纹理,最常见的应用是医学和地球科学领域的渲染。在医学应用程序中,三维纹理可以用于表示一系列的断层计算成像系统(CT)或者核磁共振(MRI)图像。对于石油和天然气研究人员,三维纹理可以用来对岩石底层进行建模。三维纹理可以看成一层层二维子图像矩形构成的。
  • 球体纹理, 也就是环境纹理,目标是渲染具有完美反射能力的物体,它的表面颜色就是反射到人眼周围环境的颜色。
  • 立方体纹理,是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。立方体纹理非常适用于实现环境、反射和光照效果。
  • 多重纹理,多重纹理允许应用几个纹理,在纹理操作管线中把它们逐个应用到同一个多边形上。

纹理定义


一维纹理

void glTexImage1D(GLenum target,GLint level,GLint components,GLsizei width,
 GLint border,GLenum format,GLenum type,const GLvoid *pixels);

        定义一个一维纹理映射,除了第一个参数target应设置为GL_TEXTURE_1D外,其余所有的参数与函数TexImage2D()的一致,不过纹理图像是一维纹素数组,其宽度值必须是2的幂,若有边界则为2m+2。

二维纹理

void glTexImage2D(GLenum target,GLint level,GLint components,
           GLsizei width, glsizei height,GLint border,
           GLenum format,GLenum type, const GLvoid *pixels);

     定义一个二维纹理映射。其中参数target是常数GL_TEXTURE_2D。参数level表示多级分辨率的纹理图像的级数,若只有一种分辨率,则level设为0。
  参数components是一个从1到4的整数,指出选择了R、G、B、A中的哪些分量用于调整和混合,1表示选择了R分量,2表示选择了R和A两个分量,3表示选择了R、G、B三个分量,4表示选择了R、G、B、A四个分量。
  参数width和height给出了纹理图像的长度和宽度,参数border为纹理边界宽度,它通常为0,width和height必须是2m+2b,这里m是整数,长和宽可以有不同的值,b是border的值。纹理映射的最大尺寸依赖于OpenGL,但它至少必须是使用64x64(若带边界为66x66),若width和height设置为0,则纹理映射有效地关闭。
  参数format和type描述了纹理映射的格式和数据类型,它们在这里的意义与在函数glDrawPixels()中的意义相同,事实上,纹理数据与glDrawPixels()所用的数据有同样的格式。参数format可以是GL_COLOR_INDEX、GL_RGB、GL_RGBA、GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_LUMINANCE或GL_LUMINANCE_ALPHA(注意:不能用GL_STENCIL_INDEX和GL_DEPTH_COMPONENT)。类似地,参数type是GL_BYPE、GL_UNSIGNED_BYTE、GL_SHORT、 GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或GL_BITMAP。
  参数pixels包含了纹理图像数据,这个数据描述了纹理图像本身和它的边界。

纹理控制函数


        OpenGL中的纹理控制函数如下:

void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param);

        第一个参数target可以是GL_TEXTURE_1D或GL_TEXTURE_2D,它指出是为一维或二维纹理说明参数;后两个参数的可能值见下表。

参数 对应的值
GL_TEXTURE_WRAP_S GL_CLAMP ,GL_REPEAT
GL_TEXTURE_WRAP_T GL_CLAMP,GL_REPEAT
GL_TEXTURE_MAG_FILTER GL_NEAREST,GL_LINEAR
GL_TEXTURE_MIN_FILTER GL_NEAREST,GL_LINEAR,GL_NEAREST_MIPMAP_NEAREST ,GL_NEAREST_MIPMAP_LINEAR ,GL_LINEAR_MIPMAP_NEAREST ,GL_LINEAR_MIPMAP_LINEAR

        一般来说,纹理图像为正方形或长方形。但当它映射到一个多边形或曲面上并变换到屏幕坐标时,纹理的单个纹素很少对应于屏幕图像上的象素。根据所用变换和所用纹理映射,屏幕上单个象素可以对应于一个纹素的一小部分(即放大)或一大批纹素(即缩小)。下面用函数glTexParameter*()说明放大和缩小的方法:

glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
  glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

        实际上,第一个参数可以是GL_TEXTURE_1D或GL_TEXTURE_2D,即表明所用的纹理是一维的还是二维的;第二个参数指定滤波方法,其中参数值GL_TEXTURE_MAG_FILTER指定为放大滤波方法,GL_TEXTURE_MIN_FILTER指定为缩小滤波方法;第三个参数说明滤波方式,其值见表12-1所示。
  若选择GL_NEAREST则采用坐标最靠近象素中心的纹素,这有可能使图像走样;若选择GL_LINEAR则采用最靠近象素中心的四个象素的加权平均值。GL_NEAREST所需计算比GL_LINEAR要少,因而执行得更快,但GL_LINEAR提供了比较光滑的效果。

        同时,纹理坐标可以超出(0, 1)范围,并且在纹理映射过程中可以重复映射或约简映射。在重复映射的情况下,纹理可以在s,t方向上重复。例如:

 glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
 glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

纹理坐标


        在绘制纹理映射场景时,不仅要给每个顶点定义几何坐标,而且也要定义纹理坐标。经过多种变换后,几何坐标决定顶点在屏幕上绘制的位置,而纹理坐标决定纹理图像中的哪一个纹素赋予该顶点。并且顶点之间的纹理坐标插值与基础篇中所讲的平滑着色插值方法相同。
   纹理图像是方形数组,纹理坐标通常可定义成一、二、三或四维形式,称为s,t,r和q坐标,以区别于物体坐标(x, y, z, w)和其他坐标。一维纹理常用s坐标表示,二维纹理常用(s, t)坐标表示,目前忽略r坐标,q坐标象w一样,一半值为1,主要用于建立齐次坐标。OpenGL坐标定义的函数是:
  

void gltexCoord{1234}{sifd}[v](TYPE coords);

         设置当前纹理坐标,此后调用glVertex()所产生的顶点都赋予当前的纹理坐标。对于gltexCoord1(),s坐标被设置成给定值,t和r设置为0,q设置为1;用gltexCoord2()可以设置s和t坐标值,r设置为0,q设置为1;对于gltexCoord3(),q设置为1,其它坐标按给定值设置;用gltexCoord4*()可以给定所有的坐标。使用适当的后缀(s,i,f或d)和TYPE的相应值(GLshort、GLint、glfloat或GLdouble)来说明坐标的类型。注意:整型纹理坐标可以直接应用,而不是象普通坐标那样被映射到[-1, 1]之间。

示例

#include <GLUT/GLUT.h>
#include <OpenGL/OpenGL.h>
#include "BMPLoader.h"
GLuint tex2D;
GLfloat angle;
 
// 初始化参数
void init() {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glClearColor(0.1, 0.1, 0.4, 0.0);
    glShadeModel(GL_SMOOTH);
    CBMPLoader bmpLoader;
    bmpLoader.LoadBmp("/123-bmp.bmp");
     
 
    // 创建纹理
    glGenTextures(1, &tex2D);
    glBindTexture(GL_TEXTURE_2D, tex2D);

 
    // 纹理滤波参数设置
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
 
 
    // 设置纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bmpLoader.imageWidth, bmpLoader.imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bmpLoader.image);
    angle = 0;
}
 
 
/** 绘制木箱 */
void DrawBox(){
    glEnable(GL_TEXTURE_2D);
 
    /** 选择纹理 */
    glBindTexture(GL_TEXTURE_2D, tex2D);

    /** 开始绘制四边形 */
    glBegin(GL_QUADS);
 
    /// 前侧面
    glNormal3f(0.0f, 0.0f, 1.0f);                               /**指定法线指向观察者 */
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
 
    /// 后侧面
    glNormal3f(0.0f, 0.0f, -1.0f);                              /** 指定法线背向观察者 */
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);
 
    /// 顶面
    glNormal3f(0.0f, 1.0f, 0.0f);                               /**指定法线向上 */
    glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f, -1.0f);
    glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f, -1.0f);
 
    /// 底面
    glNormal3f(0.0f, -1.0f, 0.0f);                              /** 指定法线朝下 */
    glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, -1.0f, -1.0f);
    glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, -1.0f, -1.0f);
 
    /// 右侧面
    glNormal3f(1.0f, 0.0f, 0.0f);                               /**指定法线朝右 */
    glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);
    
    /// 左侧面
    glNormal3f(-1.0f, 0.0f, 0.0f);                              /**指定法线朝左 */
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
}
 

// 绘图回调函数
void display() {
    // 清除之前帧数据
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glTranslatef(0.0f, 0.0f, -5.0f);
    glRotated(angle, 1, 1, 0);
    DrawBox();
    glPopMatrix();
 
    // 执行绘图命令
    glFlush();
    angle ++;
    glutPostRedisplay();
}

 
// 窗口大小变化回调函数
void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 0.1, 100000.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, const char * argv[]) {
    // 初始化显示模式
    glutInit(&argc, const_cast<char **>(argv));
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB|GLUT_DEPTH);

 
    // 初始化窗口
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
 
    init();
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
 
    // 开始主循环绘制
    glutMainLoop();
    return 0;
}

运行效果如下:
在这里插入图片描述

发布了54 篇原创文章 · 获赞 80 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41685265/article/details/104367046