Android OpenGL 纹理绘制图像---基础知识

来源:互联网 发布:c语言产生随机数rand 编辑:程序博客网 时间:2024/05/11 10:02

一、基础概念

(1)纹理贴图

纹理贴图允许把一幅砖墙图像映射到一个多边形的表面上,并把整面砖墙画成单个多边形。纹理贴图能够保证当这个多边形进行变换和渲染时,映射到多边形表面的图像也能够表现出正确的行为。
它是一个相当大的主题,并且具有相当程度的复杂性。在使用纹理贴图时,必须做出一些编程选择。初学者很可能会本能地把纹理理解成二维图像,但是纹理也可以是一维的,甚至是三维的。可以把纹理映射到由一个多边形构成的表面上,也可以把它贴到曲面上,还可以在一个、二个或三个方向上(取决于纹理的维度)重复应用同一个纹理来覆盖整个表面。另外,可以把纹理图像自动映射到物体上,用它表示被观察物体的轮廓线或者其他属性。有光泽的物体也可以进行纹理贴图,当它们位于房间或其他环境的中央时,它们的表面就可以反射周围的物体。最后,纹理也可以按不同的方式应用到物体的表面。它可以直接画到物体的表面(就像表面上的贴花一样),调整表面的颜色。或者,把纹理颜色与表面颜色进行混合。
简单地说,纹理就是矩形的数据数组。例如,颜色数据、亮度数据、颜色和alpha数据。纹理数组中的单个值常常成为纹理单元(texel)。纹理贴图之所以复杂,是因为矩形纹理可以映射到非矩形区域,并且以合理方式实现。

(2)坐标系

这里写图片描述
如图所示,OpenGL世界坐标系和纹理坐标系的对应关系,所以纹理的顶点和世界坐标的顶点之间的对应关系如下:

private static final float VERTICES[] = {            -1.0f, -1.0f,            1.0f, -1.0f,            -1.0f, 1.0f,            1.0f, 1.0f,    };    private static final float TEXTURE[] = {            0.0f, 1.0f,            1.0f, 1.0f,            0.0f, 0.0f,            1.0f, 0.0f,    };

以上只是纹理的顶点和世界坐标的顶点的对应,和数组中的顶点顺序没有关系,但是如果顶点之间的对应关系不一致时,会出现贴图和原始图像颠倒的现象发生,具体看参考博客中介绍和代码。

二、Android OpenGL 纹理贴图流程(二维)

(1)生成纹理对象

纹理对象用于存储纹理数据,以便随时使用它们。生成纹理对象需要以下步骤:
1.命名纹理对象
任何非零的无符号整数都可以用来表示纹理对象的名称。为了避免意外的重名,应该坚持使用glGenTextures()函数来提供未使用的纹理对象名称。
2.创建和使用纹理对象
glBindTexture()函数可以创建和使用纹理对象。当一个纹理对象名称初次绑定时(使用glBindTexture()函数),OpenGL就会创建一个新的纹理对象,并把纹理图像和纹理属性设置为 默认值。

(2)设置过滤

纹理图像是正方形或长方形的,但是当它们映射到多边形的表面并转换为屏幕坐标之后,纹理图像中的单个纹理单元很少与最终屏幕图像中的单个像素形成一一对应的关系。根据物体使用的变换以及它所应用的纹理图像,屏幕上的一个像素可以对应很广的范围,可能是一个纹理单元的一小部分(放大),也可能是多个纹理单元(缩小)。无论是哪种情况,我们很难知道具体使用的是哪些纹理纹理值以及它们是如何匀和或插值的。为此,OpenGL允许指定几种过滤选项,确定这些计算的细节。
这里写图片描述
如上图,使用glTexParameteri()函数指定放大和缩小过滤方法:
1)glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,*)
当待纹理映射的像素大于纹理元素时生效,默认值是GL_NEAREST_MIPMAP_LINER
2)glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,*)
当待纹理映射的像素小于等于纹理元素时生效,默认值是GL_LINER
假设GL_TEXTURE_MIN_FILTER采用默认值时,那就需要为每一个MIPMAP层指定图像,否则不论过滤是否在某些片段生效,都将不能正常的纹理贴图,因为纹理是不完整的。因此为纹理贴图指定确定的过滤模式,而不依赖于默认值,是一个不错的主意。
当像素的尺寸和纹理的尺寸相匹配时,哪一种过滤模式被使用那是很难说的,但是也没有简单的方式去确认哪一种过滤模式被使用在某一特定的纹理片段。如果你想要不论以何种方式,像素都能相对完美的纹理映射(比如在2D屏幕上绘制位图),为GL_TEXTURE_MIN_FILTER和GL_TEXTURE_MAG_FILTER过滤都设置为GL_NEAREST模式,是一种比较好的想法,从而你将不必纠结于使用哪一种过滤模式。

(3)定义着色器—确定像素颜色和位置

顶点着色器:纹理贴图的顶点着色器与基本的顶点着色器相比,主要是增加了分配纹理坐标。
当我们进行纹理贴图时,必须为每个顶点提供物体坐标和纹理坐标。经过变换之后,物体坐标决定了应该在屏幕上的哪个地点渲染每个特定的顶点。纹理坐标决定了纹理图像中的哪个纹理单元将分配给这个顶点。
片段着色器:纹理贴图的片段着色器与基本的片段着色器相比,主要是通过着色器函数texture2D()来设置片段的颜色,而不再是单一的固定颜色值。

(4)指定纹理

glTexImage2D()函数用于定义二维纹理,将数据存储在生成的纹理对象中,同时OpenGL 也提供了glSubTexImage2D()来替换纹理图像的全部或者一部分。和修改原有纹理相比,创建新纹理开销更大一些。用新的信息修改一个现有纹理的全部或一部分常常比从头创建一个新纹理更合适。在有些应用场合,例如使用实时捕捉的视频图像作为纹理图像的应用程序,这个技巧非常实用。在这种应用程序中,可以定义一个纹理,然后反复调用glSubTexImage2D()函数,用新的视频纹理作为纹理数据。

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

target参数常设置为常量GL_TEXTURE_2D;如果提供了多种分辨率的纹理图像,可以使用 level参数。如果只提供了一种分辨率的纹理图像,level参数应该设置为0;参数internalFormat确定了哪些成分(RGBA、深度、亮度或强度)被选定为图像的纹理单元,根据支持透明度与否,可设置GL_RGBA或GL_RGB;width和height参数表示纹理图像的宽度和高度;border参数表示边框的宽度,它可以是0(没有边框)或者是1(对于OpenGL 3.1实现,必须是0);format参数设置可参考internalFormat;type参数表示像素数据的数据类型,可以是GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT、GL_BITMAP或其中一种像素包装数据类型;最后texts参数包含了纹理图像像素数据,这个数据描述了纹理图像本身以及它的边框。

void glTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,   GLsizei width,GLsizei height,GLenum format,   GLenum type,const void *texles);

target、level、format、type参数类似于glTexImage2D()使用的对应参数;texts包含了表示子图像的纹理数据;width和height参数是当前纹理图像中全部或部分被替换的子区域的宽度和高度;xoffset和yoffset指定了x和y方向上的纹理单元偏移量((0,0)表示纹理的左下角),并且指定了子图像应该从现有纹理数组中的什么地方开始替换。

(5)绘制

当采用顶点数组方式绘制图形时,可以使用该函数glDrawArrays()。该函数根据顶点数组中的坐标数据和指定的模式,进行绘制。

void glDrawArrays (GLenum mode, GLint first, GLsizei count);

相似功能的函数是 glDrawElements(),前面已经涉及到。
mode,绘制方式,常见参数数值有:GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN。它们之间的区别和含义可以参考博客。
first,从数组缓存中的哪一位开始绘制,一般为0。
count,数组中顶点的数量。

0 0
原创粉丝点击