使用前面學(xué)過(guò)的技術(shù)已經(jīng)可以利用OpenGL ES構(gòu)建立體圖形,并通過(guò)頂點(diǎn)著色器和片元著色器對(duì)其進(jìn)行各種變化呢和光照等效果使得三維效果更加真實(shí),實(shí)際上我看看到很多的3D游戲漂亮多了,那是因?yàn)橛懈鞣N各樣的漂亮的圖像帶給人很多視覺盛宴,這篇文章在前面的基礎(chǔ)上,增加物體的表面貼圖,使得物體更加好看。
紋理概念
紋理用來(lái)表示圖像照片或者說(shuō)一系列的數(shù)據(jù),使用紋理可以使物體用用更多的細(xì)節(jié)。OpenGL ES 2.0 中有兩種貼圖:二維紋理和立方體紋理。
每個(gè)二維紋理都由許多小的紋理元素組成,類似與片元和像素,使用紋理最簡(jiǎn)單的方式就是直接從一個(gè)圖像加載數(shù)據(jù)。在OpenGL中規(guī)定紋理圖像的左下角由stst坐標(biāo)(0.0,0.0)指定,右上角由stst坐標(biāo)(1.0,1.0)指定,不過(guò)超過(guò)1.0的坐標(biāo)也是允許的,在該區(qū)間之外的紋理在讀取時(shí)的時(shí)候由紋理拉伸模式?jīng)Q定。
OpenGL ES 2.0不必是正方形,但是每個(gè)維度都應(yīng)該是2的冪
在Android中使用的OpenGL ES的紋理坐標(biāo)系跟官方的紋理坐標(biāo)系統(tǒng)不一樣,在Android中使用官方的紋理坐標(biāo)系統(tǒng),得到的結(jié)果是相反的,而是左上角是stst坐標(biāo)(0.0,0.0)點(diǎn),右下角是stst坐標(biāo)(1.0,1.0)點(diǎn)。
二維紋理映射的原理
使用紋理就是在紋理圖中進(jìn)行采樣,因此需要將選定的紋理坐標(biāo)穿進(jìn)頂點(diǎn)著色器,經(jīng)過(guò)插值在片元著色器中從紋理圖中的指定位置采樣即可,紋理圖的數(shù)據(jù)通過(guò)往片元插值器傳遞紋理單元指定的。
紋理對(duì)象和紋理加載
創(chuàng)建一個(gè)紋理對(duì)象,保存渲染所需的紋理數(shù)據(jù),例如圖像數(shù)據(jù)、過(guò)濾模式、包裝模式。創(chuàng)建生成紋理對(duì)象的函數(shù)
1
2
3
4
5
|
public static native void glGenTextures( int n, // 指定要生成的紋理對(duì)象的數(shù)量 int [] textures, // 保存紋理對(duì)象ID的數(shù)組 int offset ); |
紋理對(duì)象在應(yīng)用程序中不再使用時(shí),需要?jiǎng)h除。
1
2
3
4
5
|
public static native void glDeleteTextures( int n, // 指定要?jiǎng)h除的紋理數(shù)量 int [] textures, // 保存待刪除的紋理ID的數(shù)組 int offset ); |
紋理對(duì)象的 ID 必須是 glGenTextures 產(chǎn)生的,一旦生成紋理ID,就必須綁定紋理對(duì)象才能繼續(xù)進(jìn)行后續(xù)的操作。后續(xù)的操作將影響綁定的紋理對(duì)象。一旦紋理被綁定到一個(gè)特定的紋理目標(biāo),再刪除之前就一直保持著綁定狀態(tài)。
1
2
3
4
|
public static native void glBindTexture( int target, // 綁定紋理對(duì)象到目標(biāo) GL_TEXTURE_2D 或 GL_TEXTURE_CUBE_MAP int texture // 要綁定的紋理對(duì)象ID ); |
激活某個(gè)紋理單元
1
2
3
|
public static native void glActiveTexture( int texture // 要激活的紋理單元 ); |
對(duì)這兩個(gè)函數(shù)的理解:顯卡中有N個(gè)紋理單元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2…),每個(gè)紋理單元中保存著很多紋理目標(biāo)(targetTexture1D,targetTexture2D,targetTexture3D,targetTextureCube…),OpenGL ES 2.0貌似只支持了targetTexture2D和targetTextureCube。
紋理單元TextureUnit的定義如下
1
2
3
4
5
6
7
8
|
struct TextureUnit { GLuint targetTexture1D; GLuint targetTexture2D; GLuint targetTexture3D; GLuint targetTextureCube; ... }; |
glActiveTexture函數(shù)就是設(shè)置當(dāng)前活動(dòng)的紋理單元
1
2
3
4
5
6
7
|
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS] GLuint currentTextureUnit = 0 ; // ... void glActiveTexture(GLenum textureUnit) { currentTextureUnit = textureUnit - GL_TEXTURE0 ; } |
glBindTexture函數(shù)就是將紋理對(duì)象ID賦值給當(dāng)前活動(dòng)的紋理單元的對(duì)應(yīng)的目標(biāo)紋理。
1
2
3
4
5
6
7
8
9
10
11
|
void glBindTexture(GLenum textureTarget, GLuint textureObject) { TextureUnit *texUnit = &textureUnits[currentTextureUnit]; switch (textureTarget) { case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break ; case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break ; case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break ; case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break ; } } |
獲取一副圖片的紋理數(shù)據(jù)
1
2
3
4
5
|
public static void texImage2D( int target, // 常數(shù)GL_TEXTURE_2D int level, // 表示多級(jí)分辨率的紋理圖像的級(jí)數(shù),若只有一種分辨率,則level設(shè)為0。 Bitmap bitmap, int border // 邊框,一般設(shè)為0 ) |
其他紋理選項(xiàng)的設(shè)置使用glTexParameterf系列函數(shù)
1
2
3
4
5
|
public static native void glTexParameterf( int target, int pname, // 設(shè)定的參數(shù),可以是GL_TEXTURE_MAG_FILTER,GL_TEXTURE_MIN_FILTER,GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T float param // 參數(shù)對(duì)應(yīng)的值 ); |
應(yīng)用紋理的例子
對(duì)前面的立方體的每個(gè)面應(yīng)用一張圖片作為紋理貼圖,效果圖(這個(gè)紋理圖是哪個(gè)老師來(lái)著?)
Rectangle.java
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
public class Rectangle { private FloatBuffer mVertexBuffer; private int mProgram; private int mPositionHandle; private int muMVPMatrixHandle; private int mColorHandle; private int muMMatrixHandle; private int muLightLocationHandle; private int mTextureCoordHandle; private int textureId; private int muTextureHandle; private Context mContext; public Rectangle(Context context) { this .mContext = context; initVetexData(); } public void initVetexData() { float vertices[] = new float [] { // 頂點(diǎn) 顏色 紋理坐標(biāo) //前面 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //后面 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //左面 - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //右面 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //上面 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //下面 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f }; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4 ); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position( 0 ); int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition" ); mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor" ); mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord" ); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix" ); muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix" ); muLightLocationHandle = GLES20.glGetUniformLocation(mProgram, "uLightLocation" ); muTextureHandle = GLES20.glGetUniformLocation(mProgram, "uTexture" ); initTexture(); } // 初始化紋理 public void initTexture() { int [] textures = new int [ 1 ]; GLES20.glGenTextures( 1 , textures, 0 ); textureId = textures[ 0 ]; // 激活紋理單元,默認(rèn)激活的就是0號(hào)紋理單元 //GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // 將紋理對(duì)象ID綁定到當(dāng)前活動(dòng)的紋理單元0上的GL_TEXTURE_2D目標(biāo) GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 后面對(duì)紋理的設(shè)置都是對(duì)綁定了的紋理所生效的 //縮小采樣使用最近點(diǎn)采樣 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); //縮小采樣使用最近點(diǎn)采樣 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR); //紋理包裹拉伸方式在st軸采用截取拉伸方式,這些設(shè)置指的是對(duì)坐標(biāo)范圍超過(guò)1的部分的限制 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE); Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0 , bitmap, 0 ); // 圖片已經(jīng)加載到了顯存,可以回收 bitmap.recycle(); } public void draw() { GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0 , 12 * 6 ); } public void setValue( float [] mvpMatrix, float [] mMatrix) { GLES20.glUseProgram(mProgram); mVertexBuffer.position( 0 ); GLES20.glVertexAttribPointer(mPositionHandle, 3 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); mVertexBuffer.position( 3 ); GLES20.glVertexAttribPointer(mColorHandle, 4 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); mVertexBuffer.position( 7 ); GLES20.glVertexAttribPointer(mTextureCoordHandle, 2 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glEnableVertexAttribArray(mTextureCoordHandle); GLES20.glUniform3f(muLightLocationHandle, 0 , 0 , 20 ); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1 , false , mvpMatrix, 0 ); GLES20.glUniformMatrix4fv(muMMatrixHandle, 1 , false , mMatrix, 0 ); // 將使用的紋理單元0傳遞給片元著色器 GLES20.glUniform1i(muTextureHandle, 0 ); } private int loaderShader( int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec2 aTextureCoord;" + "varying vec2 vTextureCoord;" + "uniform mat4 uMMatrix;" + "uniform vec3 uLightLocation;" + "attribute vec4 aColor;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "attribute vec3 aPosition;" + "void main(){" + "vec3 normalVectorOrigin = aPosition;" + "vec3 normalVector = normalize((uMMatrix*vec4(normalVectorOrigin,1)).xyz);" + "vec3 vectorLight = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);" + "float factor = max(0.0, dot(normalVector, vectorLight));" + "vDiffuse = factor*vec4(1,1,1,1.0);" + "gl_Position = uMVPMatrix * vec4(aPosition,1);" + "vColor = aColor;" + "vTextureCoord = aTextureCoord;" // 將紋理坐標(biāo)傳到片元著色器,得到更多的插值紋理坐標(biāo) + "}" ; private String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D uTexture;" // 這個(gè)uniform變量表示了紋理數(shù)據(jù),從java中傳過(guò)來(lái)的是所在的紋理單元編號(hào) + "varying vec2 vTextureCoord;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "void main(){" + "gl_FragColor = (vColor*vDiffuse + vColor*vec4(0.6,0.6,0.6,1))*texture2D(uTexture, vTextureCoord);" // 在紋理的基礎(chǔ)上還考慮到光照,texture2D函數(shù)用于紋理采樣 + "}" ; } |
需要注意的還是傳入的頂點(diǎn)的時(shí)候數(shù)組里面包含了頂點(diǎn)、顏色和紋理坐標(biāo),因此要用ByteBuffer的position方法定位。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/cauchyweierstrass/article/details/52947279