3. Texture triangle
Although the triangle in the previous chapter looks bright, it is often not practical. We want to display an image on a triangle, or more appropriately, we want to display the part corresponding to the triangle in an image. This can be achieved through practical texture images. This is the purpose of this chapter.
To enable webgl to access the specified image data, we need to save the image data to the texture objects in webgl. First, create a texture object and use the webgl function createtexture (). Then, set the texture object to the current operation object of the corresponding Texture type, which is completed through the webgl function bindtexture (target, textureobject. The first parameter of this function indicates the Texture type to bind. There are two types: texture_2d and texture_cube_map. Here we use a 2D image and a 2D texture. Therefore, the first parameter is texture_2d. Then, copy the specified image data to the corresponding storage area of the texture object so that webgl can access it internally. The "copy" webgl functions are divided into two types: teximage2d series and texsubimage2d series (For details, refer to the "webgl Reference Manual" and "OpenGL
ES 2.0 programming guide). The former updates the data of the entire texture storage area, and the latter can update the data of some storage areas. We use the image specified by the HTML image element as the texture image and copy it to the texture object at a time. Therefore, the webgl function we use is:
Void teximage2d (glenum target, glint level, glenum internalformat,
Glenum format, glenum type, htmlimageelement image)
The first parameter of this function specifies a specific data block to be set, either a 2D texture or a plane of the cube texture, which is one of the following enumerated values: texture_2d, texture_cube_map_positive_x, texture_cube_map_negative_x, texture_cube_map_positive_y, expires, texture_cube_map_positive_z, and expires. The second parameter specifies the texture level to be loaded. Its meaning will be described in the anti-aliasing section. Currently, we only set the original image data with a texture level of 0. Internalformat specifies the format of the texture storage area, which can be rgba, RGB, luminance_alpha, luminance, and Alpha. Format refers to the pixel format of the image represented by the parameter image. In webgl, internalformat and format must be the same. Type indicates the input pixel data type, which can be one of the following: unsigned_byte, unsigned_short_4_4_4_4, unsigned_short_5_5_5_1, unsigned_short_5_6_5. Its value and format
Closely related, you can regard it as a format sub-format. For example, when format is rgba, you can set type to unsigned_short_4_4_4_4 and unsigned_short_5_5_5_1 (unsigned_byte is special, which indicates that each component of a pixel is represented by a byte ). In this chapter, we use a 24-bit bitmap image in RGB format. The unsigned_byte type indicates red, 6-bit indicates green, and 5-bit indicates blue ). The final image parameter is the object represented by the image element specified in HTML.
After the image data is obtained inside webgl, You need to mark it before use. In the fragment shader, texture access is performed through this identifier. This identifier is called a texture unit. Marked in two steps: Use the webgl function activetexture (textureunit) to activate the identity, and then use the webgl function bindtexture (target, textureobject) associate the specified texture object with the currently activated Texture unit (bindtexture is also used above, which is not surprising; The above function + the function here is the complete function of the bindtexture function ). The texture units can be texture0, texture1,... texture31. But this does not mean you can use all of them. The texture units you can actually use must be within the range of the webgl function getparameter (max_texture_image_units. Required. If the return value of the function is 8, you can only use texture0, texture1,... texture7.
At this point, half of the work has been completed. The remaining half is to calculate the texture coordinates of the vertex in the vertex coloring tool (in other words, it is to calculate the vertex on the image to be displayed; in my examples in this chapter, our image is 600px * pixel PX, which is exactly the same size as the visible area. The content to be displayed is also the part occupied by the triangle. Therefore, you just need to simply convert the vertex coordinate to the texture coordinate) and pass it to the fragment shader through the varying variable. Then, let the fragment shader sample the texture based on the input varying texture coordinates and assign the sampling result to the built-in variable gl_fragcolor. Sampling, using the built-in function texture2d (s_texture,
V_texcoord. Note that this function is only valid in the shader. It is not a function in webgl. Do not confuse it. The first parameter of this function is a samplezer. The sample set corresponds to the texture unit mentioned in the previous section. For example, if the sample set value is 0, it indicates sampling from the Texture unit texture0. If the value is 1, it indicates sampling from the Texture unit texture1. In general, the fragment shader cannot determine the samplezer by itself (I .e., the texture unit to be used ). The solution is to define a unirom samplezer In the fragment shader, and then specify it in the application through the uniform * series functions of webgl. The uniform * series functions need to know the associated index of the uniform variable. As mentioned above, the management index of the uniform variable is automatically generated when the program object is linked. We can only use the webgl function getuniformlocation (programobject,
Name.
Note that the texture coordinates are different from the rendering coordinates. The lower-left corner of the texture coordinate is (), and the upper-right corner is ). The behavior of texture coordinates beyond this range is determined by the texture packaging mode. In the vertex coloring tool, only three texture coordinates are calculated, which correspond to the three vertices of the triangle respectively. But the area corresponding to the entire Triangle must be displayed. Don't worry, webgl will automatically perform Interpolation Calculation for us and output the results. In many cases, the texture coordinates calculated by interpolation cannot uniquely identify a point on the image. For example, linear interpolation is performed between 1 and 2. The result is 1.5. However, the point in the image does not have the 1.5 coordinate. At this time, we need to tell webgl how to handle this situation. For our current example, there are two available methods: one is to take the point closest to 1.5 (if there are multiple, take the first point ); the other is linear interpolation, where the first and second points and the last two points are closest to each other. The median value is calculated as the final value based on the linear formula. In some cases, these two methods will produce a sawtooth, but this is enough for our current example. When you set the parameters, you must also distinguish between zoom-in and zoom-out. The webgl function used is texparameterf (target,
Pname, Param ). The first parameter is texture_2d and texture_cube_map. The second parameter is texture_mag_filter and texture_min_filter, which respectively indicate amplification and reduction. The third parameter corresponds to the nearest and linear methods I just mentioned. This function can also be used to specify the texture packaging mode. In this case, the second parameter is texture_wrap_s and exture_wrap_t, corresponding to the X and Y directions respectively. The third parameter is repeat, clamp_to_edge, and mirrored_repeat. For details about their effects, see OpenGL.
Chapter 9 texture/texture coordinate packaging in ES 2.0 programming guide. In addition, when the width and height of the texture image are not the integer power of 2, the packaging mode can only be gl_clamp_to_edge, and the narrow filtering mode can only be gl_nearest or gl_linear (To put it another way, is not a MIP texture ).
The following is an example after integration:
<HTML>
<Head>
<Meta http-equiv = "Content-Type" content = "text/html; charset = gb2312">
<SCRIPT type = "text/JavaScript" src = "glMatrix-0.9.5.js"> </SCRIPT>
<SCRIPT id = "shader-Vs" type = "X-shader/X-vertex">
Attribute vec3 v3position;
Varying vec2 v_texcoord;
Void main (void)
{
V_texcoord = vec2 (v3position. x + 1.0)/2.0, 1.0-(v3position. Y + 1.0)/2.0 );
Gl_position = vec4 (v3position, 1.0 );
}
</SCRIPT>
<SCRIPT id = "shader-Fs" type = "X-shader/X-fragment">
# Ifdef gl_fragment_precision_high
Precision highp float;
# Else
Precision mediump float;
# Endif
Uniform sampler2d s_texture;
Varying vec2 v_texcoord;
Void main (void)
{
Gl_fragcolor = texture2d (s_texture, v_texcoord );
}
</SCRIPT>
<SCRIPT>
Function shadersourcefromscript (scriptid)
{
VaR shaderscript = Document. getelementbyid (scriptid );
If (shaderscript = NULL) Return "";
VaR sourcecode = "";
VaR child = shaderscript. firstchild;
While (child)
{
If (child. nodetype = Child. text_node) sourcecode + = Child. textcontent;
Child = Child. nextsibling;
}
Return sourcecode;
}
VaR webgl = NULL;
VaR vertexshaderobject = NULL;
VaR fragmentshaderobject = NULL;
VaR programobject = NULL;
VaR trianglebuffer = NULL;
VaR v3positionindex = 0;
VaR textureobject = NULL;
VaR samplerindex =-1;
Function Init ()
{
VaR mycanvasobject = Document. getelementbyid ('mycanvas ');
Webgl = mycanvasobject. getcontext ("experimental-webgl ");
Webgl. viewport (0, 0, mycanvasobject. clientwidth, mycanvasobject. clientheight );
Vertexshaderobject = webgl. createshader (webgl. vertex_shader );
Fragmentshaderobject = webgl. createshader (webgl. fragment_shader );
Webgl. shadersource (vertexshaderobject, shadersourcefromscript ("shader-"));
Webgl. shadersource (fragmentshaderobject, shadersourcefromscript ("shader-Fs "));
Webgl. compileshader (vertexshaderobject );
Webgl. compileshader (fragmentshaderobject );
If (! Webgl. getshaderparameter (vertexshaderobject, webgl. compile_status) {alert (webgl. getshaderinfolog (vertexshaderobject); return ;}
If (! Webgl. getshaderparameter (fragmentshaderobject, webgl. compile_status) {alert (webgl. getshaderinfolog (fragmentshaderobject); return ;}
Programobject = webgl. createprogram ();
Webgl. attachshader (programobject, vertexshaderobject );
Webgl. attachshader (programobject, fragmentshaderobject );
Webgl. bindattriblocation (programobject, v3positionindex, "v3position ");
Webgl. linkprogram (programobject );
If (! Webgl. getprogramparameter (programobject, webgl. link_status) {alert (webgl. getprograminfolog (programobject); return ;}
Samplerindex = webgl. getuniformlocation (programobject, "s_texture ");
Webgl. useprogram (programobject );
VaR jsarraydata = [
0.0, 1.0, 0.0, // top Vertex
-1.0,-1.0, 0.0, // left Vertex
1.0, 0.0, 0.0]; // right Vertex
Trianglebuffer = webgl. createbuffer ();
Webgl. bindbuffer (webgl. array_buffer, trianglebuffer );
Webgl. bufferdata (webgl. array_buffer, new float32array (jsarraydata), webgl. static_draw );
Textureobject = webgl. createtexture ();
Webgl. bindtexture (webgl. texture_2d, textureobject );
VaR IMG = Document. getelementbyid ('mytexture ');
Webgl. teximage2d (webgl. texture_2d, 0, webgl. RGB, webgl. RGB, webgl. unsigned_byte, IMG );
Webgl. clearcolor (0.0, 0.0, 0.0, 1.0 );
Webgl. Clear (webgl. color_buffer_bit );
Webgl. bindbuffer (webgl. array_buffer, trianglebuffer );
Webgl. enablevertexattribarray (v3positionindex );
Webgl. vertexattribpointer (v3positionindex, 3, webgl. Float, false, 0, 0 );
Webgl. texparameteri (webgl. texture_2d, webgl. texture_min_filter, webgl. Nearest );
Webgl. texparameteri (webgl. texture_2d, webgl. texture_mag_filter, webgl. Nearest );
Webgl. texparameteri (webgl. texture_2d, webgl. texture_wrap_s, webgl. clamp_to_edge );
Webgl. texparameteri (webgl. texture_2d, webgl. texture_wrap_t, webgl. clamp_to_edge );
Webgl. activetexture (webgl. texture0 );
Webgl. bindtexture (webgl. texture_2d, textureobject );
Webgl. uniform1i (samplerindex, 0 );
Webgl. drawarrays (webgl. triangles, 0, 3 );
}
</SCRIPT>
</Head>
<Body onload = 'init () '>
<Canvas id = "mycanvas" style = "border: 1px solid red;" width = '600px 'Height = '0000p'> </canvas>
</Body>
</Html>
The running result is as follows: