Next, explore textures.
Texture is simply an image. The process of mapping an image to a graph is called texture mapping.
Let’s say you have the following graph and triangle and you want to map part of the graph to the triangle.
The result is this:
This is a small example of texture mapping.
The basic principle of
Note that OpenGL draws objects in 3D and textures in 2D, so texture mapping maps 2D textures to 3D objects, just like wrapping an object in a piece of paper, but following certain rules.
Objects drawn in OpenGL have coordinates, each point corresponds to x, Y, z coordinates, and texture also has its coordinates, as long as every point in the 3D object corresponds to a point in the 2D texture, then the texture can be mapped to the 3D object.
The coordinates of the texture are calledTextured coordinate system
. It has a range of only 到 。
Its coordinate origin is located in the lower left corner, the horizontal right is the S axis, the vertical up is the Y axis. Regardless of the actual texture image size, the maximum horizontal and vertical coordinates are 1.
For example, if the actual map has a resolution of 512 x 256 pixels, the 512th horizontal pixel corresponds to texture coordinate 1, and the 256th vertical pixel corresponds to texture coordinate 1. However, it is best to use a texture map with 2 to the power of n pixels.
The basic idea of texture mapping is to first specify appropriate texture coordinates for each vertex in the pixel, then determine the selected texture region in the texture map by texture coordinates, and finally map the content in the selected texture region to the specified pixel according to texture coordinates.
Texture mapping is reflected in OpenGL’s rendering pipeline: in the rendering pipeline, vertex shaders are first carried out to draw the rough shape of the object, and then rasterization is carried out to raster the object into many fragments, and then fragment shaders are carried out to color each fragment of the graph.
In the rasterization stage, texture coordinates will be generated based on the vertex shader’s processing of the texture and the interpolation between the segment and each vertex, and then the interpolated results will be fed into the segment shader.
Shader operation
Shaders also change when using textures instead of drawing directly.
Vertex shaders:
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;
uniform mat4 u_Matrix;
void main() {
v_TextureCoordinates = a_TextureCoordinates ;
gl_Position = u_ProjectionMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
}
Copy the code
The v_TextureCoordinates variable is added to the vertex shader. It is of type VARYING, which means variable type. This variable is processed during rasterization and then passed into the fragment shader.
Fragment shader
precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;
void main(){
// No texture color assignment: gl_FragColor = u_Color;
gl_FragColor = texture2D(u_TextureUnit,v_TextureCoordinates);
}
Copy the code
The v_TextureCoordinates1 variable is the one that accepts the value passed in from the vertex shader, and the u_TextureUnit variable is the sampler used and is of type sampler2D.
The fragment shader after using the texture assigns values to the colors using the texture2D function.
The texture2D function is used to sample, taking pixels from the texture and assigning them to the gl_FragColor variable, which is the final color.
The upper code
With an overview of shader code, it’s time to move on to the upper level Java code.
Similar to creating an OpenGL ProgramId, using a texture requires creating a texture ID.
/** * Returns the ID of the OpenGl texture after the image is loaded@param context
* @param resourceId
* @return* /
public static int loadTexture(Context context, int resourceId) {
final int[] textureObjectIds = new int[1];
glGenTextures(1, textureObjectIds, 0);
if (textureObjectIds[0] = =0) {
Timber.d("Could not generate a new OpenGL texture object.");
return 0;
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
if (bitmap == null) {
Timber.d("resource Id could not be decoded");
glDeleteTextures(1, textureObjectIds, 0);
return 0;
}
glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);
// Set the filter mode for zooming out
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// Set the filter mode for zooming in
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load the texture into OpenGL, read the Bitmap data defined by the Bitmap, and copy it to the currently bound texture object
// The currently bound texture object will be attached to the texture image.
texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
// Automatically generates all required multi-level fade textures for the currently bound texture
// Generate MIP map
glGenerateMipmap(GL_TEXTURE_2D);
// Unbind the texture to avoid accidentally changing the texture with other texture methods
glBindTexture(GL_TEXTURE_2D, 0);
return textureObjectIds[0];
}
Copy the code
- The first to use
glGenTextures
Create the texture ID. - If the creation fails, use
glDeleteTextures
Delete and exit. - After successful creation, use the
glBindTexture
The texture ID () function binds the texture ID to the texture target. - It then sets how the texture filters when zoomed in and out.
- To use
texImage2D
Bind texture targets to Bitmap images. - use
glGenerateMipmap
Function to generate multilevel fade texture and MIP texture maps. - To use
glBindTexture
Function unbind.
GlBindTexture function
The highlight here is the glBindTexture function.
It binds the texture name to the specified active texture unit. When a texture is bound to a target, the previously bound texture object of the target texture unit is automatically disconnected. The texture target is bound to 0 by default, so if you want to disconnect, you should also bind the texture target to 0.
So the glBindTexture(GL_TEXTURE_2D, 0) is called at the end of the code to unbind.
When a texture is bound, OpenGL operations on the bound target are applied to the bound texture, and a query on the bound target returns the state of the bound texture on it.
That is, the texture target becomes an alias for the texture bound to it, and a texture name of 0 refers to its default texture. Therefore, when you call glTexParameteri to set the filter method for the texture target, you are also setting the filter method for the texture.
Bind values in the texture
Once the texture shader ID is created and set, it is time to bind and set variables in the shader language.
// Bind variables in the shader script
uTextureUnitAttr = glGetUniformLocation(mProgram, U_TEXTURE_UNIT)
mTextureId = TextureHelper.loadTexture(mContext,R.drawable.texture)
// Activate the texture unit
glActiveTexture(GL_TEXTURE0)
// Bind the texture target
glBindTexture(GL_TEXTURE_2D, mTextureId)
// Assign to the sample2D variable in the fragment shader
glUniform1i(uTextureUnitAttr, 0)
Copy the code
The sampler2D variable of Uniform type is defined in the shader script, which is bound and assigned by the upper application code. Variables of type VARYING are passed by the vertex shader and no additional assignment is required.
The next step is to activate the texture unit using the glActiveTexture function. In a system, the data of texture units is limited. In the source code, there are thirty-two texture units defined from GL_TEXTURE0 to GL_TEXTURE31, but the exact number depends on the model.
This can be found using the GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS constant.
var intBuffer:IntBuffer = IntBuffer.allocate(1)
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,intBuffer)
LogUtil.d("max combined texture image units " + intBuffer[0])
Copy the code
Once the texture unit is activated, you need to bind the texture target.
A texture unit contains multiple types of texture targets, such as GL_TEXTURE_1D, GL_TEXTURE_2D, CUBE_MAP, and so on.
Because a texture unit is an alias for a texture, anything you do to a texture unit is the same as anything you do to a texture. Extract some of the operations done to the texture into the function, and finally load the texture and bind it to the texture target.
Assign a value of 0 to the sampler using the glUniform1i function, which corresponds to activating the texture unit. Since the active texture unit is 0, the assignment is also 0. If it’s not consistent here, you can’t see anything directly.
The actual effect
After binding and setting the values in the fragment shader, the rest of the process is the same as drawing the basic graphics.
The specific rendering operations are defined in the fragment shader, while the upper layer code does not need to pay much attention to, in the case of the vertex shader unchanged, you can even just change the fragment shader value to draw different texture effects.
Summary & confusion of nouns
It is easy to confuse the texture unit and texture target above.
GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2 are texture units. The number of texture units on a machine is limited, depending on the specific model. GlActiveTexture activates specific texture units.
A texture unit contains multiple types of texture targets: GL_TEXTURE_1D, GL_TEXTURE_2D, CUBE_MAP, and so on.
GlGenTextures generates a value of int that is a texture. GlBindTexture binds the texture to the target, and all operations performed on the target are reflected in the texture.
The texture target requires a Bitmap to be attached via the texImage2D function.
reference
- http://blog.csdn.net/opengl_es/article/details/19852277
- http://blog.csdn.net/artisans/article/details/76695614
Finally, if you think the article is good, welcome to pay attention to wechat public number: