OpenGL ES Tutorial for Android – Part VI – Textures

这篇文章花费的时间最长,从这里了解的UV mapping,

 

Last tutorial we worked a bit more on meshes and we have also talked about adding colors to our mesh. The most common way of adding colors to your mesh is to add a texture. There is a couple of different steps involved with adding a texture to the mesh I will try to go through them all and explain the basics about them.

Loading bitmaps

First step would be to get a bitmap to generate a texture from. You can get hold of a bitmap in many different ways from downloading, generating or simply just load one from the resources. I'm going with the simplest one for this example witch is loading from the resources.

Bitmap bitmap = BitmapFactory.decodeResource(contect.getResources
(
)
,

                                             R.drawable
.icon
)
;

One other thing about textures is that some hardware requires that the height and width are in the power of 2 (1, 2, 4, 8, 16, 32, 64...). If you run a texture with a size of 30x30pixels on a hardware that don’t support it you will just get a white square (unless you change the default color).

Generating a texture

After we have loaded the bitmap we need to tell OpenGL to actually create the texture.

First thing we need to do is to let OpenGL generate some texture id's that we will use as handles to the textures later on. In this example we will only have one texture.

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

With the same parameters you can delete the textures:

// Delete a texture.


gl.glDeleteTextures
(
1
, textures, 0
)

Now when the texture id's are generated we need to just like everything else tell OpenGL what to work with. With textures we use the command glBindTexture:

gl.glBindTexture
(
GL10.GL_TEXTURE_2D
, textures[
0
]
)
;

From this point all commands we call on regarding textures will be applied on to your texture with the generated id.

glTexParameter

There is a couple of parameters we need to set on the texture, the first one is to tell OpenGL what to do if the texture need to be shrunk or magnified to match the rendered image.
If the texture is smaller it needs to be magnified that is done with the magnification function:

// Scale up if the texture if smaller.


gl.glTexParameterf
(
GL10.GL_TEXTURE_2D
,

                   GL10.GL_TEXTURE_MAG_FILTER
,

                   GL10.GL_LINEAR
)
;

And how to scale if the texture needs to be scaled down using the minification function.

// scale linearly when image smalled than texture


gl.glTexParameterf
(
GL10.GL_TEXTURE_2D
,

                   GL10.GL_TEXTURE_MIN_FILTER
,

                   GL10.GL_LINEAR
)
;

You need to pass an argument to these functions. I'm only going to show you two of them the rest you can investigate your self ;)

If you want a crisp and clean rendering like this image you need to use the GL10.GL_NEAREST parameter.

If you rather want a blurred image you should use the GL10.GL_LINEAR parameter.

UV Mapping

We will also need to tell OpenGL how to map this image onto the mesh this is done in two steps, fist we need to assign UV coordinates

UV mapping is the way we map the pixels on the bitmap to the vertices in our mesh. The UV coordinates are 0,0 in the upper left and 1,1 is the bottom right, like the left image below. The right image below illustrates how our plane is built. To get the texture mapped correctly we need to map the lower left part of the texture (0,1) to the lower left vertex (0) in our plane and we need to map the the bottom right (1,1) in the texture to the bottom right (1) to the bottom right in our plane and... you get the idea.

We put this mapping into a float array like this:

float
 textureCoordinates[
]
 = {
0
.0f, 1
.0f,

                              1
.0f, 1
.0f,

                              0
.0f, 0
.0f,

                              1
.0f, 0
.0f }
;

If we instead used 0.5 instead of 1.0 like this:

float
 textureCoordinates[
]
 = {
0
.0f, 0
.5f,

                              0
.5f, 0
.5f,

                              0
.0f, 0
.0f,

                              0
.5f, 0
.0f }
;


The texture will be mapped so the plane will have the upper left part of it.

Back to the glTexParameterf, if we go the other way and uses values higher then 1.0 like this:

float
 textureCoordinates[
]
 = {
0
.0f, 2
.0f,

                              2
.0f, 2
.0f,

                              0
.0f, 0
.0f,

                              2
.0f, 0
.0f }
;

We actually tell OpenGL to use part of the texture that does not exist so we need to tell OpenGL what to do with the part that does not exist.

We use the glTexParameterf function to tell OpenGL what to do with the texture. By default OpenGL uses something called GL_REPEAT.

GL_REPEAT means that OpenGL should repeat the texture beyond 1.0.
GL_CLAMP_TO_EDGE means that OpenGL only will draw the image once and after that just repeat the last pixel line the rest of the image.

Since we are working with a 2D texture so we need to tell OpenGL what to do in two directions: GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T.

Below you see a chart with the 4 combinations of GL_REPEAT and GL_CLAMP_TO_EDGE.

WRAP_S: GL_REPEAT
WRAP_T: GL_REPEAT
WRAP_S: GL_REPEAT
WRAP_T: GL_CLAMP_TO_EDGE
WRAP_S: GL_REPEAT
WRAP_T: GL_CLAMP_TO_EDGE
WRAP_S: GL_CLAMP_TO_EDGE
WRAP_T: GL_CLAMP_TO_EDGE

This is how we use the glTexParameterf function:

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

The last thing we need to do is to bind the bitmap we loaded to the texture id we created.

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

Using the texture

To be able to use the texture we need just like with everything else create a byte buffer with the UV coordinates:

FloatBuffer byteBuf = ByteBuffer.allocateDirect
(
texture.length
 * 4
)
;

byteBuf.order
(
ByteOrder.nativeOrder
(
)
)
;

textureBuffer = byteBuf.asFloatBuffer
(
)
;

textureBuffer.put
(
textureCoordinates)
;

textureBuffer.position
(
0
)
;

Rendering

// 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
]
)
;如果有超过一个texture的话,在这里可以选择textures[n],如果没有这个

语句的话,就默认是最后一个bind的texture


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

Putting it all together

I'm using a modified version of the code from the previous tutorial. The different is mostly that I renamed some variables and functions and added more comments and all code is now under Apache License. To make the code easier to understand I removed the previous plane and added a new easier one called SimplePlane.

Updating the Mesh class

The first thing we need to do is to update the Mesh class (se.jayway.opengl.tutorial.mesh.Mesh). We need to add the functionality to load and render a texture.

We need to be able to set and store the UV coordinates.

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

}


 

We also need to add functions to set the bitmap and create the texture.

// 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
]
; 可以有超过一个texture


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

}


 

And finally we need to add the call to the texture loading and to actually tell OpenGL to render with this texture. I removed some code so the page would not be so long but you will find the code complete in the attached zip file.

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

	}


 

	...

 

}

Creating the SimplePlane class

We also need to create the SimplePlane.java. The code is pretty simple and self-explaining if you have read my previous tutorials. The new element is the textureCoordinates variable.

 

package
 se.jayway
.opengl
.tutorial
.mesh
;

 

/**

 * SimplePlane is a setup class for Mesh that creates a plane mesh.

 *

 * @author Per-Erik Bergman (per-erik.bergman@jayway.com)

 *

 */


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

	}


}


 

References

The info used in this tutorial is collected from:
Android Developers
OpenGL ES 1.1 Reference Pages

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值