OpenGL ES 详解纹理生成和纹理映射步骤以及函数

通常一个纹理映射的步骤是:

  1. 创建纹理对象。就是获得一个新的纹理句柄 ID.
  2. 指定纹理。就是将数据赋值给 ID 的纹理对象,在这一步,图像数据正式加载到了 ID 的纹理对象中。
  3. 设定过滤器。定义了opengl现实图像的效果,如纹理放大时的马赛克消除。
  4. 绑定纹理对象。就是将 ID 的纹理作为下面操作的纹理。
  5. 纹理映射。将已绑定纹理的数据绘制到屏幕上去,在这一步,就能看到贴图的效果了。

一、opengl 中启用纹理映射功能

在默认设置中,纹理映射是关闭的,启用的参数是 GLTEXTURE2D, 还有其他的参数: GL_TEXTURE_1D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP。我们只用到2D纹理,其他不再赘述。

gl.glEnable(GL_TEXTURE_2D)

二、创建纹理

创建纹理,用函数 glGenTextures() 完成,函数返回新创建的纹理的 ID。此函数可以创建 n 个纹理,并将纹理ID 放在 textures 中:

 
void glGenTextures (int n, IntBuffer textures)

范例:

?


1

2

3

IntBuffer intBuffer = IntBuffer.allocate(1);

gl.glGenTextures(1, intBuffer);

int textureId = intBuffer.get(); // 纹理 ID

指定纹理

OpenGL 提供了三个函数来指定纹理: glTexImage1D(), glTexImage2D(), glTexImage3D(). 这三个版本用于相应维数的纹理,我们用到的是 2D 版本: glTexImage2D().

 
void glTexImage2D (int target, int level, int internalformat, int width, int height, int border, int format, int type, Buffer pixels)

参数过多,可以使用 GLUtils 中的 texImage2D() 函数,好处是直接将 Bitmap 数据作为参数:

 
void texImage2D (int target, int level, Bitmap bitmap, int border)

参数:

target
操作的目标类型,设为 GL_TEXTURE_2D 即可
level
纹理的级别,本节不涉及,设为 0 即可
bitmap
图像
border
边框,一般设为0

?


1

GLUtils.texImage2D (GL10.GL_TEXTURE_2D, 0, mBitmap, 0);

删除纹理

删除纹理, 第三个参数指明了第二个参数 textures 数组中纹理ID 的步长,一般是紧凑顺序存放,设为0即可。

 
void glDeleteTextures (int n, int[] textures, int offset)

绑定纹理

绑定后,此纹理处于活动状态。在第一次绑定一个纹理对象时, 会将一系列初始值来适应你的应用。绑定比较简单,用函数 glBindTexture():

 
void glBindTexture (int target, int texture)

第一个参数是纹理类型,我们使用 2D 纹理,参数设为 GL_TEXTURE_2D, 第二个参数是纹理对象的 ID。

设置过滤器

有两个版本:float版和int版本。

 
void glTexParameterf (int target, int pname, float param)  
void glTexParameterx (int target, int pname, int param)

一般我们设置两个, 一个放大器的: GL_TEXTURE_MAG_FILTER, 一个缩小器的: GL_TEXTURE_MIN_FILTER.

下面的两行告诉OpenGL在显示图像时,当它比放大得原始的纹理大 ( GL_TEXTURE_MAG_FILTER )或缩小得比原始得纹理小( GL_TEXTURE_MIN_FILTER )时OpenGL采用的滤波方式。

通常这两种情况下我都采用 GL_LINEAR 。这使得纹理从很远处到离屏幕很近时都平滑显示。使用 GL_LINEAR 需要CPU和显卡做更多的运算。

如果您的机器很慢,您也许应该采用 GL_NEAREST 。过滤的纹理在放大的时候,看起来斑驳的很(马赛克)。您也可以结合这两种滤波方式。在近处时使用 GL_LINEAR ,远处时 GL_NEAREST 。


1

2

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波

 三、纹理映射

用函数 glTexCoordPointer 指定纹理坐标数组,

 
void glTexCoordPointer (int size, int type, int stride, Buffer pointer)

默认这个功能是关闭的,所以需要打开:

?


1

2

3

4

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

// ... 

// 关闭

gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

2 常见的几个问题

2.1 贴图呈现白色

可能的原因:

  • 未启用 GL_TEXTURE_2D 选项。请使用 glEnable()glDisable() 函数进行开启和关闭。
  • 纹理对象无数据。 使用 GLUtils.texImage2D() 来指定,指定前需 glBindTexture() 激活当前纹理。

2.2 图像扭曲

可能的原因:

  • 纹理坐标和顶点坐标对应关系是否正确,调整之
  • 图像的大小不是 2 的次幂, 解决: 内部重新生成一张 2 的次幂的image,调整uv坐标

3 代码实现

先定义一个纹理对象,其基本接口有:

  • 创建+指定。 构造函数完成
  • 绑定。
  • 绘制。

@note: 为了处理 2 的次幂,内部对原始图像不是2的次幂的重新建立了一个图像。详见代码吧。

public class Texture2D {
    private int mWidth;
    private int mHeight;
    private int mPow2Width;
    private int mPow2Height;
    private float maxU = 1.0f;
    private float maxV = 1.0f; 

    private Bitmap mBitmap = null; 

    private int textureId = 0; 

    // 删除纹理数据
    public void delete(GL10 gl)
    {
        if (textureId != 0){
            gl.glDeleteTextures(1, new int[]{textureId}, 0);
            textureId = 0;
        } 

        // bitmap
        if (mBitmap != null)
        {
            if (mBitmap.isRecycled())
                mBitmap.recycle();
            mBitmap = null;
        } 

    } 

    public static int pow2(int size)
    {
        int small = (int)(Math.log((double)size)/Math.log(2.0f)) ;
        if ( (1 << small) >= size)
            return 1 << small;
        else
            return 1 << (small + 1);
    } 

    // 构建,推迟到第一次绑定时
    public Texture2D(Bitmap bmp)
    {
        // mBitmap = bmp;
        mWidth = bmp.getWidth();
        mHeight = bmp.getHeight(); 

        mPow2Height = pow2(mHeight);
        mPow2Width =pow2(mWidth); 

        maxU = mWidth/(float)mPow2Width;
        maxV = mHeight/(float)mPow2Height; 

        Bitmap bitmap = Bitmap.createBitmap(mPow2Width, mPow2Height,
                bmp.hasAlpha() ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(bmp, 0, 0, null);
        mBitmap = bitmap;
    } 

    // 第一次会加载纹理数据
    public void bind(GL10 gl)
    {
        if (textureId ==0)
        {
            int[] textures = new int[1];
            gl.glGenTextures(1, textures, 0);
            textureId = textures[0]; 

            gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); 

            gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
            gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 

            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0); 

            mBitmap.recycle();
            mBitmap = null;
        } 

        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
    } 

    // 绘制到屏幕上
    public void draw(GL10 gl, float x, float y)
    {
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 

        //  绑定
        this.bind(gl); 

        // 映射
        FloatBuffer verticleBuffer = FloatBuffer.wrap(new float[]{
            x,y,
            x+mWidth, 0,
            x, y+mHeight,
            x+mWidth, y+mHeight,
        });
        FloatBuffer coordBuffer = FloatBuffer.wrap(new float[]{
            0,0,
            maxU,0,
            0,maxV,
            maxU,maxV,
        }); 

        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, coordBuffer);
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, verticleBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4); 

        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D);
    } 

    public void draw(GL10 gl, float x, float y, float width, float height)
    {
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 

        //  绑定
        bind(gl); 

        // 映射
        // 映射
        FloatBuffer verticleBuffer = FloatBuffer.wrap(new float[]{
            x,y,
            x+width, 0,
            x, y+height,
            x+width, y+height,
        });
        FloatBuffer coordBuffer = FloatBuffer.wrap(new float[]{
            0,0,
            maxU,0,
            0,maxV,
            maxU,maxV,
        }); 

        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, coordBuffer);
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, verticleBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4); 

        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D); 

        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D); 

    } 

} 

4 贴图一个机器人

代码很简单了,在场景 scene 的 draw() 中绘制一个 texture2D, 具体下载代码看看吧:

public class AndroidScene extends GlObject{
    Texture2D texture; 

    public AndroidScene()
    {
        super(); 

        // 使用 assets 文件夹下的 androida.jpg
        Bitmap androidBitmap = GameSystem.getInstance().getBitmapFromAssets("androida.jpg");
        texture = new Texture2D(androidBitmap);
    } 

    public void draw(GL10 gl)
    {
        texture.draw(gl, 0, 0);
    }
} 

这一节有点枯燥,学习愉快。

时间: 2024-11-06 09:44:02

OpenGL ES 详解纹理生成和纹理映射步骤以及函数的相关文章

详解User Defined Java Class步骤(三)

 详解User Defined Java Class步骤(三) kettle中的"user defined java class"步骤,也称UDJC步骤,从4.0版本就有,功能非常强大,无所不能:可以在其中写任意代码,却不影响效率.本文将详细介绍在不同场景中用示例展示如果使用该步骤,由于内容非常多,便于阅读方便,把内容分成三部分,请完整看完全部内容,示例代码在这里下载. 如果没有看第二部分,请先访问第二部分. 错误处理 udjc步骤支持kettle的错误处理特性,从udjc步骤拖动

OpenGL ES为缓存提供数据的7个步骤

OpenGL ES为缓存提供数据的7个步骤: 1.生成glGenBuffers()——请求OpenGL ES为图形处理器控制的缓存生成一个独一无二的标识符. 2.绑定glBindBuffer()——告诉OpenGL ES为接下来的运算使用一个缓存. 3.缓冲数据glBufferData()或glBufferSubData()——让OpenGL ES为当前绑定的缓存分配病初始化足够的连续内存(通常是从CPU控制的内存复制数据到分配的内存). 4.启用或者禁止glEnableVertexAttrib

详解User Defined Java Class步骤(一)

 详解User Defined Java Class步骤(一) kettle中的"user defined java class"步骤,也称UDJC步骤,从4.0版本就有,功能非常强大,无所不能:可以在其中写任意代码,却不影响效率.本文将详细介绍在不同场景中用示例展示如果使用该步骤,由于内容非常多,便于阅读方便,把内容分成三部分,请完整看完全部内容,示例代码在这里下载. UDJC步骤工作机制 用户定义java类是从org.pentaho.di.trans.steps.userdef

详解User Defined Java Class步骤(二)

 详解User Defined Java Class步骤(二) kettle中的"user defined java class"步骤,也称UDJC步骤,从4.0版本就有,功能非常强大,无所不能:可以在其中写任意代码,却不影响效率.本文将详细介绍在不同场景中用示例展示如果使用该步骤,由于内容非常多,便于阅读方便,把内容分成三部分,请完整看完全部内容,示例代码在这里下载. 如果没有从第一部分开始,请访问第一部分. 使用步骤参数(Step Parameter) 如果你写了一段代码,如果

详解C#委托,事件与回调函数

.Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等.“protected void Page_Load(object sender, EventArgs e)”这段代码相信没有人不熟悉的.细心一点一定会发现,非常多的事件方法都是带了“object sender, EventArgs e”这两个参数.这是不是和委托非常相似呢? 一.委托(有些书中也称为委派) 委托是什么呢?这个名字的意思已

【OpenGL】详解第一个OpenGL程序

写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下.搞到最后都不知道自己在干嘛,更有可能因为某一步的顺序错误导致最后渲染出错,又或者觉得记下这些操作的顺序是非常烦人的一件事.那么,OpenGL为什么会长成这个样子呢?这篇文章旨在通过一个最简单的OpenGL程序开始,让我们能够"看懂"它,"记住"这些操作顺序. 我们先来解

初学Android OpenGL ES之使用纹理 八十三

在网上发现这些讲纹理的文章,非常不错 android 游戏导引(4. 简单纹理贴图) http://www.cnblogs.com/shengdoushi/archive/2011/01/13/1934181.html Android OpenGL es 纹理坐标设定与贴图规则 http://blog.csdn.net/cjkwin/article/details/6016224 Android OpenGL | ES给立方体进行纹理映射 http://www.ourunix.org/andro

Android OpenGL ES(七)----理解纹理与纹理过滤

1.理解纹理 OpenGL中的纹理可以用来表示图像,照片,甚至由一个数学算法生成的分形数据.每个二维的纹理都由许多小的纹理元素组成,它们是小块的数据,类似于我们前面讨论过的片段和像素.要使用纹理,最常用的方式是直接从一个图像文件加载数据. 每个二维纹理都有其自己的坐标空间,其范围是从一个拐角的(0,0)到另一个拐角的(1,1).按照惯例,一个维度叫做S,而另一个称为T.当我们想要把一个纹理应用于一个三角形或一组三角形的时候,我们要为每个顶点指定一组ST纹理坐标,以便OpenGL知道需要用那个纹理

对Android opengl ES世界坐标系和纹理坐标系的理解

初学opengl ES,每一个教你在屏幕上贴图的opengl版hello world都有这么两数组: static final float COORD[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static final float TEXTURE_COORD[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; 但是几乎都不解释,所以我学的时候都不明白这些点