[转]OpenGL 2D纹理

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

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

3.启用纹理贴图

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

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

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

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

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的整数,1:选择R;2:选择R A;3:选择R G B;

源文档 <http://www.pinxue.net/OpenGL/credbook/chapter9_textuer.htm>

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

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

概述

     概括的说, 纹理映射机制允许你将一个图像关联到一个多边形上,从而呈现出真实视觉效果。例如, 你可以将书的封面图像应用到一个方形上, 这样这个方形看起来就像是一本书了。 你可以将地球的地图通过纹理映射应用到一个球体上, 那么这个球体就是一个3D的具真实感的地球了。纹理映射在当今的3D图形应用上处处皆是。游戏都是通过纹理映射来作为虚拟真实的第一个步骤。

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

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

预备知识: 纹理坐标

     OpenGl 中是通过指定纹理坐标来将纹理映射到多边形上去的在纹理坐标系中左下角是 (0,0), 右上角是 (1,1). 2D 纹理的坐标中通过指定 (s,t) (sx轴上,ty轴上取值0~1). 1D, 3D, 4D纹理坐标系中对应的需要指定 (s), (s,t,r), (s,t, r,q).

    纹理坐标需要通过函数 glTexCoord() 来设置此函数:

void glTexCoord{1234}{sifd}(TYPE coords);

void glTexCoord{1234}{sifd}v(TYPE coords);

   

    如将 2D 纹理坐标设为 (0.2, 0.4):

1

glTexCoord2f(0.2f,0.4f);

    每次通过 glVertex() 指定一个顶点时当前的纹理坐标会被应用到这个点上所以每指定一个新的顶点需要同时修改纹理坐标:

1

2

3

4

5

6

glBegin(GL_POLYGON);

        glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, 0.5f, 0.5f);//左下角

        glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, 0.5f, 0.5f); // 右下角

        glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f);// 右上角

        glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f);// 左上角

glEnd();

    至此我们知道了纹理坐标如何赋值.且看如何创建纹理:

使用纹理映射

    纹理就是应用到多边形上的图像这些图像可以从文件中加载或是在内存中生成一旦你将图像数据加载到了内存中你需要指定其为纹理映射来使用它指定其为纹理映射首先需要生成一个纹理对象其中存储着纹理的诸如图像数据如何应用等信息.

    纹理是一个OpenGL状态因而通过 glEnable()  glDisable() 来开闭, 参数是 GL_TEXTURE_1D,  GL_TEXTURE_2DGL_TEXTURE_3D,  GL_TEXTURE_CUBE_MAP.

3.1 纹理对象

    纹理对象是内部数据类型存储着纹理数据和选项等你不能直接访问它但是可以通过一个整数的 ID 来作为其句柄(handler) 来跟踪之为了分配到一个唯一的 ID, OpenGL 提供了glGenTextures() 函数来获取一个有效的 ID 标识值:

void glGenTexture(Glsizei n, GLuint *texture);

   

    texture 是一个数组用于存储分配到的nID在调用一次 glGenTextures() 会将分配到的 ID 标识为'已用', 虽然直到绑定后才真正为'已用'.

    分配3个纹理对象 ID:

?

1

2

unsigned inttextureObjects[3];

glGenTexture(3, textureObjects);

3.2 纹理绑定

    在第一次绑定一个纹理对象时会将一系列初始值来适应你的应用函数 glBindTexture() 用于绑定操作:

void glBindTexture(GLenum target, GLuint texture);

   

    target 指定了纹理类型GL_TEXTURE_1D,  GL_TEXTURE_2D,  GL_TEXTURE_3D,  GL_TEXTURE_CUBE_MAP.  texure 是你希望绑定的纹理对象的 ID.

    一个被绑定的纹理对象直到被删除,或被另外的纹理对象绑定到 target 上才被解除绑定当一个纹理对象绑定到 target 上后, OpenGL 的后续的纹理操作都是基于这个纹理对象的。

?

1

2

3

4

5

6

7

8

9

10

glBindTexture (GL_TEXTURE_2D, textureObject[0]);

// 后面的对 GL_TEXTURE_2D 的纹理操作影响textureObject[0]

   

glBindTexture (GL_TEXTURE_3D, textureObject[1]);

// 后面的对 GL_TEXTURE_3D 的纹理操作影响textureObject[1]

//  GL_TEXTURE_2D 的纹理操作依然影响textureObject[0]

   

glBindTexture (GL_TEXTURE_2D, textureObject[2]);

// 后面的对 GL_TEXTURE_2D 的纹理操作影响textureObject[2]

//  GL_TEXTURE_3D 的纹理操作依然影响textureObject[1]

3.3 删除纹理对象

    创建一个纹理对象后, OpenGL为其分配内存所以当不再使用一个纹理对象时为防止内存泄露必须删除删除纹理对象的函数glDeleteTexture() :

void glDeleteTexure(Glsizei n, Gluint *texture);

   

    texture 指定了要删除的纹理对象的 ID (n). 在删除后, texture 中的各个对象 ID 会置为0.

3.4 驻留纹理

    显卡有一块固定大小的内存区域专门用于存储纹理数据。当数据超量时,会将一部分纹理数据移除到系统内存中(通常是最近最少使用的纹理数据). 当这些移除的纹理被再次使用时,会影响击中率, 因为它们会被再次移入显卡的内存中。你可以查看一个纹理对象是否驻留在显卡内存中未被移出, 通过函数 glAreTexturesResident() :

GLboolean glAreTexturesResident (GLsizei n, GLuint *textures, GLboolean *residents);

   

    texture 中每一项纹理对象的驻留情况会存储在 resident 参数中返回。  textures 中有一项纹理对象不在内存驻留内存,函数会返回 GL_FALSE.

3.5 纹理优先级

    纹理的优先级是针对驻留显卡内存而言。优先级设置函数 glPrioritizeTextures() :

void glPrioritizeTextures (GLsizei n, GLuint *textures, GLclampf *priorities)

   

    前两个参数 textures  n 指定了要设置优先级的纹理对象数组。 priorities  textures 中每一项纹理对象对应的优先级,priorities 中每一项的优先级取值区间是 [0,1], 0为优先级最低, 1 为最高。 glPrioritizeTextures() 函数会忽略掉那些未使用的和优先级要设为 0 的纹理对象。

指定纹理

    OpenGL 提供了三个函数来指定纹理glTexImage1D()glTexImage2D()glTexImage3D()这三个版本用于相应维数的纹理, 例如如果纹理是3D纹理,则需要有 glTexImage3D() 来指定。

4.1 2D 纹理

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

   

    参数 target  GL_TEXTURE_2D (二维纹理 GL_PROXY_TEXTURE_2D (二维代理纹理), 代理纹理暂且不提。

    参数 level 指定了纹理映射细节的级别,用在mipmap中。 基本的纹理图像级别为0 在后面的mipmap部分讲解。

    参数 internalFormat 指定了纹理存储在显存中的内部格式, 取值在下表, 为兼容 OpenGL1.0 internalFormat 可以取值123分别对应常量 LUMINANCELUMINANCE_ALPHA,  RGB,  RGBA.

格式

注解

GL_ALPHA

Alpha 

GL_DEPTH_COMPONENT

深度值

GL_LUMINCE

灰度值

GL_LUMINANCE_ALPHA

灰度值和 Alpha 

GL_INTENSITY

亮度值

GL_RGB

Red, Green, Blue三原色值

GL_RGBA

Red, Green, Blue Alpha 

纹理内部格式

    参数 width  height 定义了纹理映射的大小,前面已经说过纹理映射就是一个二维数组。  glDrawPixels() 一样, 纹理映射的宽度和高度必须是 2 的整数次幂。

    参数 border 注明了纹理是否有边框。无边框取值为 0 有边框取值为 1 边框的颜色由 GL_TEXTURE_BORDER_COLOR 选项设置。

    接下来的三个参数主要定义了图像数据的格式。

    参数 format 定义了图像数据数组 texels 中的格式。可以取值如下:

格式

注解

GL_COLOR_INDEX

颜色索引值

GL_DEPTH_COMPONENT

深度值

GL_RED

红色像素值

GL_GREEN

绿色像素值

GL_BLUE

蓝色像素值

GL_ALPHA

Alpha 

GL_RGB

Red, Green, Blue 三原色值

GL_RGBA

Red, Green, Blue Alpha 

GL_BGR

Blue, Green, Red 

GL_BGRA

Blue, Green, Red Alpha 

GL_LUMINANCE

灰度值

GL_LUMINANCE_ALPHA

灰度值和 Alpha 

图像数据数组 texels 格式

    参数 type 定义了图像数据数组 texels 中的数据类型。可取值如下

数据类型

注解

GL_BITMAP

一位(01)

GL_BYTE

带符号8位整形值(一个字节)

GL_UNSIGNED_BYTE

不带符号8位整形值(一个字节)

GL_SHORT

带符号16位整形值(2个字节)

GL_UNSIGNED_SHORT

不带符号16未整形值(2个字节)

GL_INT

带符号32位整形值(4个字节)

GL_UNSIGNED_INT

不带符号32位整形值(4个字节)

GL_FLOAT

单精度浮点型(4个字节)

GL_UNSIGNED_BYTE_3_3_2

压缩到不带符号8位整形:R3,G3,B2

GL_UNSIGNED_BYTE_2__3_REV

压缩到不带符号8位整形:B2,G3,R3

GL_UNSIGNED_SHORT_5_6_5

压缩到不带符号16位整形:R5,G6,B5

GL_UNSIGNED_SHORT_5_6_5_REV

压缩到不带符号16位整形:B5,G6,R5

GL_UNSIGNED_SHORT_4_4_4_4

压缩到不带符号16位整形:R4,G4,B4,A4

GL_UNSIGNED_SHORT_4_4_4_4_REV

压缩到不带符号16位整形:A4,B4,G4,R4

GL_UNSIGNED_SHORT_5_5_5_1

压缩到不带符号16位整形:R5,G5,B5,A1

GL_UNSIGNED_SHORT_1_5_5_5_REV

压缩到不带符号16位整形:A1,B5,G5,R5

GL_UNSIGNED_INT_8_8_8_8

压缩到不带符号32位整形:R8,G8,B8,A8

GL_UNSIGNED_INT_8_8_8_8_REV

压缩到不带符号32位整形:A8,B8,G8,R8

GL_UNSIGNED_INT_10_10_10_2

压缩到32位整形:R10,G10,B10,A2

GL_UNSIGNED_INT_2_10_10_10_REV

压缩到32位整形:A2,B10,G10,R10

图像数据数组 texels 中数据类型

    你可能会注意到有压缩类型, 先看看 GL_UNSIGNED_BYTE_3_3_2所有的 red, green  blue 被组合成一个不带符号的8位整形中,在 GL_UNSIGNED_SHORT_4_4_4_4 中是把 red, green , blue  alpha 值打包成一个不带符号的 short 类型。

    最后一个参数是 texels, 这个指针指向实际的图像数据(你自己生成的或是从文件中加载的)。OpenGL 会按照 type 参数指定的格式来读取这些数据,

    例如, 假设你加载了一个 RGBA 图像到 textureData 中( 宽高为 textureWidth, textureHeight).你想要用它来指定一个纹理, 可以这样做:

?

1

2

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0,

        GL_RGBA, GL_UNSIGNED_BYTE, textureData);

    执行完这个函数后, 纹理会加载,等待被使用。

4.2 1D 纹理

    1D 纹理其实就是 2D 纹理的特殊形式(高度等于1)。这类纹理常常用来描绘颜色边界从而创造出阴影效果。创建 1D 纹理的函数如下:

void glTExImage1D (GLenum target, GLint level, GLint internalFormat, GLsizei width,

GLint border, GLenum format, GLenum type, const GLvoid *texels);

   

    函数中的参数同 glTexImage2D()不同的是 height 参数没有被给出(因为定值1), 参数 target 也需要指定为*GL_TEXTURE_1D*

    下面是简单的代码, 用于创建32个纹理点宽度的 RGBA 纹理:

?

1

2

3

unsigned char imageData[128];

...

glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

4.3 3D 纹理

    创建 3D 纹理的函数:

glTexImage3D(GLenum target, GLint level, GLint internalFormat, GLsizei width GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *texels);

   

    函数参数同 glTexImage1D()  glTexImage2D() 大部分相同,不同的是多了一个深度参数 depth, 指定了纹理的第三维。

    下面的代码片段, 用于创建一个 16*16*16 个纹理点的 RGB 纹理:

?

1

2

3

...

glTexImage3D (GL_TEXTURE_3D, 0, GL_RGB, 16, 16, 16, 0, GL_RGB,

                GL_UNSIGNED_BYTE, imageData);

4.4 Cube Map 纹理

    一个 Cube Map 纹理是由62D纹理组成。对应的, 需要通过 glTexImage2D() 来指定6 target 参数: GL_TEXTURE_CUBE_MAP_POSITIVE_XGL_TEXTURE_CUBE_MAP_NEGATIVE_X,  GL_TEXTURE_CUBE_MAP_POSITIVE_YGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z,  GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.

纹理过滤

    将纹理映射到多边形上, 实际上是将纹理的图像数据空间映射到帧缓冲图像空间上。所以, 你必须保证纹理图像加载完成。 纹理图像被映射到多边形上可能会造成失真。纹理图像映射到多边形上去,屏幕上的一个点可能是纹理点的一个部分(如果视口设置的离纹理很近), 也有可能屏幕上的一个像素点是多个纹理的集合(如果视口设置的足够远). 纹理过滤就是告诉OpenGL 在纹理到屏幕像素点的映射中如何计算最终显示的图像数据。

    在纹理过滤中, 放大器处理一个屏幕像素点代表一个纹理点的一部分的情况;缩小器处理一个像素点代表多个纹理点的情况你可以通过下面函数来告诉 OpenGL 怎样处理这两种情况:

void glTexParameter{if}(GLenum target, GLenum pname, T param);

void glTexParameter{if}v(GLenum target, GLenum pname, T params);

   

glTexParameter 不仅仅设置放大器和缩小器, 在本章中,由于只涉及纹理,所以只讨论纹理相关的参数取值.

   参数 target 指的是纹理目标, 可以是 GL_TEXTURE_1D, GL_TEXTURE_2D*, GL_TEXTURE_3D  GL_TEXTURE_CUBE_MAP  指定纹理放大过滤器需要指定参数 pname GL_TEXTURE_MAG_FILTER指定纹理缩小过滤器需要指定参数 pname  GL_TEXTURE_MIN_FILTER.

当指定为 GL_TEXTURE_MAG_FILTER参数 param 取值 GL_NEAREST  GL_LINEAR对放大过滤器而言, 使用 GL_NEAREST 将告诉 OpenGL 使用离像素点中心最近的纹理来渲染这被称作 点样point sampling); 使用 GL_LINEAR 告诉 OpenGL 会使用离像素点中心最近的四个纹理的平均值来渲染这被称作 双线性过滤bilinear filtering)

缩小过滤器比放大过滤器的取值更广, 下表是指定缩小过滤器时, 参数 param 的取值, 下面表中的值是为了增强渲染质量。

过滤参数

注解

GL_NEAREST

使用像素点中心最近的点渲染

GL_LINEAR

使用双线性过滤

GL_NEAREST_MIPMAP_NEAREST

  

GL_NEAREST_MIPMAP_LINEAR

  

GL_LINEAR_MIPMAP_NEAREST

  

GL_LINEAR_MIPMAP_LINEAR

  

缩小过滤器的参数

    在缩小过滤器中, 4个参数处理mipmap 这将会在后面的mipmap部分讲解。

默认情况下, 放大过滤器的参数为 GL_LINEAR缩小过滤器为 GL_NEAREST_MIPMAP_LINEAR.

在渲染纹理时, OpenGL 会先检查当前的纹理是否加载完成,同时也会处理其他事情,如在选用缩小过滤器的mipmap处理时会验证mipmap的所有级别是否被定义。 如果纹理未完成, 纹理会被禁用。因为缩小过滤器的缺省值使用mipmap,所以你必须指定所有的mipmap级别或是将缩小过滤器的参数设为 *GL\_LINEAR* *GL\_NEAREST*.

   

简单例程

    在初始化函数 init() 中创建了纹理对象, 设定了过滤模式:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

bool CGfxOpenGL::init ()

{

    glClearColor (0.0f, 0.0f, 0.0f, 0.0f);

   

    // 启用 2D 纹理

    glEnable (GL_TEXTURE_2D);

   

    m_textureOne = new CTargaImage;

   

    // 加载纹理图像

    if (!m_textureOne->Load ("rock.tga"))

        return false;

   

    // 创建纹理对象,

    glGenTextures (1, &m_textureObjectOne);

   

    // 绑定纹理对象

    glBindTexture (GL_TEXTURE_2D, m_textureObjectOne);

   

    // 设定缩放器的过滤参数

    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

   

    // 为纹理对象指定纹理图像数据

    glTExImage2D (GL_TEXTURE_2D, 0, GL_RGB, m_textureOne->GetWidth(),

                  m_textureOne->GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,

                  m_textureOne->GetImage());

   

    // 创建第二个纹理对象

    glGenTexture (1, &m_textureObjectTown);

    glBindTexture (GL_TEXTURE_2D, m_textureObjectTwo);

   

    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

   

    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, m_textureOne->GetWidth(),

                  m_textureOne->GetHeight(), 0, GL_TGB, GL_UNSIGNED_BYTE,

                  m_textureOne->GetImage());

   

    // 初始化运动变量

    m_zPos = -0.5f;

    m_zMoveNegative = true;

   

    return true;

}

     init() 函数中, 我们先启用 2D 纹理glEnable() ), 然后加载图像到 CTargaImage 类中(详见第6章), 然后通过 glGenTextures() 获得一个未被使用的纹理对象, 继而绑定, 指定缩放器的过滤模式, 最后为纹理指定图像数据(通过 glTexImage2D() ). 然后同样的流程创建了第二个纹理对象, 使用了同样的纹理图像数据。只是缩放器的过滤参数做了下更改。

    主要的渲染函数有两个 DrawPlane()Render() :

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

void CGfxOpenGL::DrawPlane ()

{

    glBegin (GL_TRIANGLE_STRIP);

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

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

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

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

    glEnd();

}

   

void CGfxOpenGL::Render ()

{

    // 清除屏幕和深度缓存

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   

    // 重置当前矩阵

    glLoadIdentity ();

   

    // 绘制左边的多边形

    glPushMatrix ();

            //

            glTranslatef (-3.0, 0.0, m_zPos);

            glRotatef (90.0, 1.0, 0.0, 0.0);

           

            // 绑定纹理

            glBindTexture (GL_TEXTURE_2D, m_textureObjectOne);

           

            // 绘制 Plane

            DrawPlane ();

    glPopMatrix();

   

    // 同样地, 绘制右边多边形

    glPushMatrix ();

            glTranslatef (3.0, 0.0, m_zPos);

            glRotatef (90.0, 1.0, 0.0, 0.0);

            glBindTexture (GL_TEXTURE_2D, m_textureObjectTwo);

            DrawPlane ();

    glPopMatrix();

   

}

     DrawPlane() 中, 我们指定了纹理坐标然后绘制多边形的顶点。在 Render() 中,我们先绑定好纹理对象, 然后绘制多边形。


你可能感兴趣的:(OpenGL)