zoukankan      html  css  js  c++  java
  • 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); 
        } 
    } 

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

  • 相关阅读:
    通过system调用Am命令执行动作
    windows中如何在命令行启动启动程序
    UICC 实现框架和数据读写
    软件设计方法(转载)
    好诗欣赏——邀请 The Invitation
    leaflet使用turfjs插件,基于格点数据绘制等值线效果
    深信服防火墙做端口映射
    关于本博客的一些声明
    sqlserver – SQL Server – 包含多个字段的IN子句
    JavaScript Array join() 方法
  • 原文地址:https://www.cnblogs.com/Anita9002/p/4581024.html
Copyright © 2011-2022 走看看