前面讨论了如何给3D图形染色,更一般的情况是使用位图来给Mesh上色(渲染材质)。主要步骤如下:
创建Bitmap对象
使用材质渲染,首先需要构造用来渲染的Bitmap对象,Bitmap对象可以从资源文件中读取或是从网络下载或是使用代码构造。为简单起见,本例从资源中读取:
1
2
|
Bitmap bitmap = BitmapFactory.decodeResource(contect.getResources(),
R.drawable.icon);
|
要注意的是,有些设备对使用的Bitmap的大小有要求,要求Bitmap的宽度和长度为2的几次幂(1,2,4,8,16,32,64.。。。),如果使用不和要求的Bitmap来渲染,可能只会显示白色。
创建材质(Generating a texture)
下一步使用OpenGL库创建一个材质(Texture),首先是获取一个Texture Id。
1
2
3
4
5
|
// Create an int array with the number of textures we want,
// in this case 1.
int
[] textures =
new
int
[
1
];
// Tell OpenGL to generate textures.
gl.glGenTextures(
1
, textures,
0
);
|
textures中存放了创建的Texture ID,使用同样的Texture Id ,也可以来删除一个Texture:
1
2
|
// Delete a texture.
gl.glDeleteTextures(
1
, textures,
0
)
|
有了Texture Id之后,就可以通知OpenGL库使用这个Texture:
1
|
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[
0
]);
|
设置Texture参数glTexParameter
下一步需要给Texture填充设置参数,用来渲染的Texture可能比要渲染的区域大或者小,这是需要设置Texture需要放大或是缩小时OpenGL的模式:
1
2
3
4
5
6
7
8
9
|
// Scale up if the texture if smaller.
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
// scale linearly when image smalled than texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_LINEAR);
|
常用的两种模式为GL10.GL_LINEAR和GL10.GL_NEAREST。
需要比较清晰的图像使用GL10.GL_NEAREST:
而使用GL10.GL_LINEAR则会得到一个较模糊的图像:
下一步要告知OpenGL库如何将Bitmap的像素映射到Mesh上。这可以分为两步来完成:
定义UV坐标
UV Mapping指将Bitmap的像素映射到Mesh上的顶点。UV坐标定义为左上角(0,0),右下角(1,1)(因为使用的2D Texture),下图坐标显示了UV坐标,右边为我们需要染色的平面的顶点顺序:
为了能正确的匹配,需要把UV坐标中的(0,1)映射到顶点0,(1,1)映射到顶点2等等。
1
2
3
4
|
float
textureCoordinates[] = {
0
.0f,
1
.0f,
1
.0f,
1
.0f,
0
.0f,
0
.0f,
1
.0f,
0
.0f };
|
1
2
3
4
|
float
textureCoordinates[] = {
0
.0f,
0
.5f,
0
.5f,
0
.5f,
0
.0f,
0
.0f,
0
.5f,
0
.0f };
|
而
1
2
3
4
|
float
textureCoordinates[] = {
0
.0f,
2
.0f,
2
.0f,
2
.0f,
0
.0f,
0
.0f,
2
.0f,
0
.0f };
|
将使用一些不存在的Texture去渲染平面(UV坐标为0,0-1,1 而 (0,0)-(2,2)定义超过UV定义的大小),这时需要告诉OpenGL库如何去渲染这些不存在的Texture部分。
有两种设置
- GL_REPEAT 重复Texture。
- GL_CLAMP_TO_EDGE 只靠边线绘制一次。
下面有四种不同组合:
1
2
3
4
5
6
|
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_WRAP_S,
GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_WRAP_T,
GL10.GL_REPEAT);
|
然后是将Bitmap资源和Texture绑定起来:
1
|
GLUtils.texImage2D(GL10.GL_TEXTURE_2D,
0
, bitmap,
0
);
|
使用Texture
为了能够使用上面定义的Texture,需要创建一Buffer来存储UV坐标:
1
2
3
4
5
|
FloatBuffer byteBuf = ByteBuffer.allocateDirect(texture.length *
4
);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
textureBuffer.put(textureCoordinates);
textureBuffer.position(
0
);
|
渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// Telling OpenGL to enable textures.
gl.glEnable(GL10.GL_TEXTURE_2D);
// Tell OpenGL where our texture is located.
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[
0
]);
// Tell OpenGL to enable the use of UV coordinates.
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Telling OpenGL where our UV coordinates are.
gl.glTexCoordPointer(
2
, GL10.GL_FLOAT,
0
, textureBuffer);
// ... here goes the rendering of the mesh ...
// Disable the use of UV coordinates.
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Disable the use of textures.
gl.glDisable(GL10.GL_TEXTURE_2D);
|
本例代码是在一个平面上(SimplePlane)下使用Texture来渲染,首先是修改Mesh基类,使它能够支持定义UV 坐标:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Our UV texture buffer.
private
FloatBuffer mTextureBuffer;
/**
* Set the texture coordinates.
*
* @param textureCoords
*/
protected
void
setTextureCoordinates(
float
[] textureCoords) {
// float is 4 bytes, therefore we multiply the number if
// vertices with 4.
ByteBuffer byteBuf = ByteBuffer.allocateDirect(
textureCoords.length *
4
);
byteBuf.order(ByteOrder.nativeOrder());
mTextureBuffer = byteBuf.asFloatBuffer();
mTextureBuffer.put(textureCoords);
mTextureBuffer.position(
0
);
}
|
并添加设置Bitmap和创建Texture的方法:
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
|
// Our texture id.
private
int
mTextureId = -
1
;
// The bitmap we want to load as a texture.
private
Bitmap mBitmap;
/**
* Set the bitmap to load into a texture.
*
* @param bitmap
*/
public
void
loadBitmap(Bitmap bitmap) {
this
.mBitmap = bitmap;
mShouldLoadTexture =
true
;
}
/**
* Loads the texture.
*
* @param gl
*/
private
void
loadGLTexture(GL10 gl) {
// Generate one texture pointer...
int
[] textures =
new
int
[
1
];
gl.glGenTextures(
1
, textures,
0
);
mTextureId = textures[
0
];
// ...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId);
// Create Nearest Filtered Texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
// Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_REPEAT);
// Use the Android GLUtils to specify a two-dimensional texture image
// from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D,
0
, mBitmap,
0
);
}
|
最后修改draw方法来渲染材质:
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
|
// Indicates if we need to load the texture.
private
boolean
mShouldLoadTexture =
false
;
/**
* Render the mesh.
*
* @param gl
* the OpenGL context to render to.
*/
public
void
draw(GL10 gl) {
...
// Smooth color
if
(mColorBuffer !=
null
) {
// Enable the color array buffer to be used during rendering.
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glColorPointer(
4
, GL10.GL_FLOAT,
0
, mColorBuffer);
}
if
(mShouldLoadTexture) {
loadGLTexture(gl);
mShouldLoadTexture =
false
;
}
if
(mTextureId != -
1
&& mTextureBuffer !=
null
) {
gl.glEnable(GL10.GL_TEXTURE_2D);
// Enable the texture state
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Point to our buffers
gl.glTexCoordPointer(
2
, GL10.GL_FLOAT,
0
, mTextureBuffer);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId);
}
gl.glTranslatef(x, y, z);
...
// Point out the where the color buffer is.
gl.glDrawElements(GL10.GL_TRIANGLES, mNumOfIndices,
GL10.GL_UNSIGNED_SHORT, mIndicesBuffer);
...
if
(mTextureId != -
1
&& mTextureBuffer !=
null
) {
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
...
}
|
本例使用的SimplePlane定义如下:
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
|
package
se.jayway.opengl.tutorial.mesh;
/**
* SimplePlane is a setup class for Mesh that creates a plane mesh.
*
* @author Per-Erik Bergman ([email protected])
*
*/
public
class
SimplePlane
extends
Mesh {
/**
* Create a plane with a default with and height of 1 unit.
*/
public
SimplePlane() {
this
(
1
,
1
);
}
/**
* Create a plane.
*
* @param width
* the width of the plane.
* @param height
* the height of the plane.
*/
public
SimplePlane(
float
width,
float
height) {
// Mapping coordinates for the vertices
float
textureCoordinates[] = {
0
.0f,
2
.0f,
//
2
.0f,
2
.0f,
//
0
.0f,
0
.0f,
//
2
.0f,
0
.0f,
//
};
short
[] indices =
new
short
[] {
0
,
1
,
2
,
1
,
3
,
2
};
float
[] vertices =
new
float
[] { -
0
.5f, -
0
.5f,
0
.0f,
0
.5f, -
0
.5f,
0
.0f,
-
0
.5f,
0
.5f,
0
.0f,
0
.5f,
0
.5f,
0
.0f };
setIndices(indices);
setVertices(vertices);
setTextureCoordinates(textureCoordinates);
}
}
|
本例示例代码下载 ,到本篇为止介绍了OpenGL ES开发的基本方法,更详细的教程将在以后发布,后面先回到Android ApiDemos中OpenGL ES的示例。