簡單紋理貼圖

 

1 紋理 Texture

紋理定義了物體表面的結構,如花紋,圖案,皺紋等等。有了紋理,模型世界纔會更加豐富多彩。如一個球形模型,我們給其映射足球的紋理,這就是一個足球,給其映射地球紋理,就是一個地球。另外,如果給一個四邊形映射一個牆的紋理,這邊是牆,否則,我們需要一塊磚一塊磚的構建在本節中,我們所指的是狹義的紋理: 圖像紋理(對應的有函數紋理—用數學函數來定義的紋理)。

紋理實際上是一個二維數組,其元素是一些顏色值,每一元素稱之爲紋理像素 (texel)。 紋理對象是一個內部數據類型,存儲着紋理數據。你不能直接訪問紋理對象,但是可以通過一個整數的 ID 來作爲其句柄跟蹤之。通過此句柄,你可以作爲當前使用的紋理(稱之爲紋理綁定),也可以從內存中刪除這個紋理對象,還可以爲一的紋理賦值(將一些紋理數據加載到關聯的紋理中,稱之爲指定紋理)。

通常一個紋理映射的步驟是:

  1. 創建紋理對象。就是獲得一個新的紋理句柄 ID.
  2. 指定紋理。就是將數據賦值給 ID 的紋理對象,在這一步,圖像數據正式加載到了 ID 的紋理對象中。
  3. 設定過濾器。定義了opengl現實圖像的效果,如紋理放大時的馬賽克消除。
  4. 綁定紋理對象。就是將 ID 的紋理作爲下面操作的紋理。
  5. 紋理映射。將已綁定紋理的數據繪製到屏幕上去,在這一步,就能看到貼圖的效果了。

1.1 紋理座標 和 紋理映射

一個紋理對象有其自己的一套座標系,左下角是 (0,0) 右上角是 (1,1):

我們要將一個圖像的一部分繪製到屏幕上,稱之爲紋理映射, 就是將圖像根據上述座標系計算出要繪製的部分的各個點的紋理座標,然後一一對應到屏幕上的座標中去(下圖中的座標系是左上角爲原點的):

1.2 opengl 中啓用紋理映射功能

在默認設置中,紋理映射是關閉的,啓用的參數是 GLTEXTURE2D, 還有其他的參數: GL_TEXTURE_1D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP。我們只用到2D紋理,其他不再贅述。

gl.glEnable(GL_TEXTURE_2D)

1.3 創建紋理

創建紋理,用函數 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

 

1.4 指定紋理

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);

1.5 刪除紋理

刪除紋理, 第三個參數指明瞭第二個參數 textures 數組中紋理ID 的步長,一般是緊湊順序存放,設爲0即可。

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

1.6 綁定紋理

綁定後,此紋理處於活動狀態。在第一次綁定一個紋理對象時, 會將一系列初始值來適應你的應用。綁定比較簡單,用函數 glBindTexture():

void glBindTexture (int target, int texture)

第一個參數是紋理類型,我們使用 2D 紋理,參數設爲 GL_TEXTURE_2D, 第二個參數是紋理對象的 ID。

1.7 設置過濾器

有兩個版本: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); // 線形濾波

1.8 紋理映射

用函數 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的次冪的重新建立了一個圖像。詳見代碼吧。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
public class Texture2D {
    privateint mWidth;
    privateint mHeight;
    privateint mPow2Width;
    privateint mPow2Height;
    privatefloat maxU = 1.0f;
    privatefloat maxV = 1.0f;
      
    privateBitmap mBitmap = null;
      
    privateint textureId = 0;
      
      
    // 刪除紋理數據
    publicvoid 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;
        }
          
    }
      
    publicstatic int pow2(int size)
    {
        intsmall = (int)(Math.log((double)size)/Math.log(2.0f)) ;
        if( (1 << small) >= size)
            return1 << small;
        else 
            return1 << (small + 1);
    }
      
    // 構建,推遲到第一次綁定時
    publicTexture2D(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;
    }
      
    // 第一次會加載紋理數據
    publicvoid 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);
    }
      
    // 繪製到屏幕上
    publicvoid draw(GL10 gl, float x, floaty)
    {
        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(newfloat[]{
            x,y,
            x+mWidth,0,
            x, y+mHeight,
            x+mWidth, y+mHeight,
        });
        FloatBuffer coordBuffer = FloatBuffer.wrap(newfloat[]{
            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);
    }
      
    publicvoid draw(GL10 gl, float x, floaty, 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(newfloat[]{
            x,y,
            x+width,0,
            x, y+height,
            x+width, y+height,
        });
        FloatBuffer coordBuffer = FloatBuffer.wrap(newfloat[]{
            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, 具體下載代碼看看吧:

1                
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AndroidScene extendsGlObject{
    Texture2D texture;
      
    publicAndroidScene()
    {
        super();
          
        // 使用 assets 文件夾下的 androida.jpg 
        Bitmap androidBitmap = GameSystem.getInstance().getBitmapFromAssets("androida.jpg");
        texture =new Texture2D(androidBitmap);
    }
      
    publicvoid draw(GL10 gl)
    {
        texture.draw(gl,0, 0);
    }
}

這一節有點枯燥,學習愉快。

轉載:http://www.cnblogs.com/shengdoushi/archive/2011/01/13/1934181.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章