Entity Details - WebGL Textures & Vertices: Beginner's Guide (2015)

WebGL Textures & Vertices: Beginner's Guide (2015)

Entity Details

The GLEntity class prepares a texture and matrix for use with WebGL. The section titled Default Matrix for Each Entity discusses the GLEntity matrix property. This section details texture initialization. A set of GLEntitymay share one texture, or each entity may retain a unique texture. A set of GLEntity may share shader variables, or each entity can use a unique set of shader variables. The GLEntity property uSampler maintains the location of a uniform sampler2D. The aTexCoord property saves the location of an attribute for processing texture coordinates.

This section discusses GLEntity methods getImageVariables(), setImage(), setActiveTexture(), setMinMagFilters(), and last setWrapToEdges(). Method getImageVariables() saves the location of shader variables associated with the entity's texture. Method setImage() activates and creates a WebGLTexture for the entity. Method setMinMagFilters() assigns minification and magnification filters for the active texture. Method setWrapToEdges() assigns wrapping types for the active texture.

WebGL API methods discussed include getUniformLocation(), getAttribLocation(), vertexAttribPointer(), enableVertexAttribArray(), uniformi(), createTexture(), texParameteri(), activeTexture(), bindTexture(),pixelStorei(), and texImage2D().

Prepare Textures

The following activity diagram demonstrates steps to initialize textures. Rectangles with medium blue backgrounds represent GLEntity methods. For readers with black and white display screens, GLEntity methods display in rectangles with medium gray backgrounds. Initialization begins with the solid black circle which represents a class prepared for one of the book's projects. The diagram terminates initialization with a black outlined circle. The dotted line indicates an asynchronous event handler to download images. Asynchronous processes operate during the same time sequence as other processes.

The rectangle with a green background represents method init(controller) which provides initialization unique to individual projects. If the project defines init(controller), then the controller calls init(controller) before rendering the scene. For readers with black and white display screens, the init(controller) method displays with the darkest gray background. The Display Repeating Graphic project includes an init(controller) method. Other projects in the series also employ different initialization techniques. However Display Repeating Graphic is the only project in this first book, which defines method init(controller).

In this example, setImage(ev) activates after an image downloads. No one knows how how much time it takes to download an image. The examples run on a range of devices with various Web connection data speeds. While waiting for downloads, the GLControl constructor prepares animation features. See the section titled Animation and Rotation for details. This section focuses on preparing textures from image data.

The texture preparation diagram follows the same path as the comprehensive Controller Activity Diagram. However the texture preparation diagram includes a few more details. GLEntity methods setWrapToEdges(gl) andsetMinMagFilters(gl) assign WebGL values to the currently active texture. The method getImages() called by the GLControl constructor begins the process of preparing images as textures for display with WebGL.

Diagram Prepare Textures

Diagram 19: Prepare Textures

Prepare Textures from Images

The method getImages() called by the GLControl constructor begins the process of preparing images as textures for display with WebGL. GLControl contains three properties which track images as they download. Property aEntitiesmaintains an array of GLEntity instances. Property nImagesToLoad keeps count of the number of images required to download. Property nImagesLoaded tracks how many images have downloaded.

First getImages() initializes both nImagesToLoad and nImagesLoaded to 0. Second getImages() iterates over every GLEntity in the array named aEntities. For each GLEntity in the array, execute the GLEntity methodgetImageVariables(GLControl).

The only parameter to getImageVariables(GLControl), is a reference to the controller. getImageVariables(GLControl) returns either 1 or 0. If the current entity needs to download an image file, then getImageVariables(GLControl)returns 1. Otherwise getImageVariables(GLControl) returns 0. The controller calls getImageVariables(GLControl) in a loop incrementing the instance variable nImagesToLoad, for each iteration. We use nImagesToLoad to verify every image has downloaded before attempting to render a frame. The following listing demonstrates calling getImageVariables(GLControl) from the controller.

for (var i = 0; i < aEntities.length; i++){

// GLEntity at 'i'.

var entity = aEntities[i];

// Increment property nImagesToLoad:

this.nImagesToLoad += entity.getImageVariables

(

this

);

}

Listing 61: Call getImageVariables(GLControl)

GLEntity Method getImageVariables(GLControl controller)

Method getImageVariables(GLControl) prepares shader variables to process one texture for a GLEntity.

If an associated uniform sampler2D exists in the shader, then the entity maintains the uniform's location. If an associated attribute exists in the shader, then the entity maintains the attribute's location. Entities use the idx property to access shader variables. For entities with an image source path getImageVariables(GLControl) prepares to download the image.

For example the book's default fragment shader includes a uniform sampler2D named u_sampler0. The entity with idx value of 0 saves u_sampler0 to it's uSampler property. However the default fragment shader doesn't include a u_sampler1. Therefore an entity with idx value of 1 simply doesn't have an associated sampler2D. This flexible structure allows other books in the series to utilize additional samplers and attributes. The following listing demonstrates saving the location of a sampler2D to the instance variable uSampler.

this.uSampler = gl.getUniformLocation

(

program,

"u_sampler"+this.idx

);

Listing 62: Save Uniform sampler2D Location

The book's default vertex shader includes an attribute named a_tex_coord0. The attribute processes texels. The entity with idx value of 0 saves a_tex_coord0 to it's aTexCoord property. However the default fragment shader doesn't include an a_tex_coord1. Therefore an entity with idx value of 1 simply doesn't have an associated attribute. This flexible structure allows other books in the series to utilize additional attributes. The following listing demonstrates saving the location of an attribute to the instance variable aTexCoord.

this.aTexCoord = gl.getAttribLocation

(

program,

"a_tex_coord"+this.idx

);

Listing 63: Save Attribute Location

WebGL API vertexAttribPointer() for Texels

If the entity has a valid attribute for processing texels, then prepare the attribute to receive texel coordinate data from a buffer.

The WebGL API method vertexAttribPointer() assigns texels from our vertex texel buffer, to the GLEntity aTexCoord property. Follow the same process described in the section titled WebGL API vertexAttribPointer() for Vertices. However, the first, second and last parameter receive different values.

Assign values from the buffer which apply to texels. We need to instruct the GPU where texels exist within the buffer. Once instructed, only texels will run through the shader's attribute.

The first parameter to WebGL method vertexAttribPointer() Number index is the location of an attribute within the vertex shader. Use the GLEntity property aTexCoord.

The second parameter Number size tells the processor how many array entries to assign to the attribute a_tex_coord0. We prepared two coordinates for each texel. One coordinate for the S horizontal axis and one for the T vertical axis. Therefore pass 2 to the second parameter.

The sixth parameter Number offset tells the processor where to start accessing entries in the buffer. The processor counts Bytes to determine offsets. Each entry is a float. WebGL floats require four Bytes each. The first texel begins just past the first set of three vertex coordinates representing X, Y, and Z components. Find the product of four Bytes times three entries. 3 * 4 = 12. Assign 12 to the last parameter.

gl.vertexAttribPointer

(

this.aTexCoord,

2,

gl.FLOAT,

gl.FALSE,

20,

12

);

Listing 64: WebGL API vertexAttribPointer() for Texels

Calculate Stride and Offset

The following table demonstrates how to calculate stride and offsets with data from the Float32Array, prepared for the Lighthouse Texture Map project. WebGL method vertexAttribPointer() uses stride and offset parameters, based on Byte location within a buffer. The offset for the first vertex in the buffer equals 0. When assigning vertices to an attribute with vertexAttribPointer(), the offset value equals 0. The offset for the first texel in the buffer equals 12. When assigning texels to an attribute with vertexAttribPointer(), the offset value equals 12. The stride for interleaved vertices and texels always equals 20 when five floating point values separate entries. However if we created an array of vertices only, then stride would equal 0. If we created a non interleaved array with four vertices at the start, followed by four texels, then the texel offset would equal forty eight. Four vertices times three coordinates per vertex times four Bytes per coordinate equals forty eight. Stride between texels and vertices would equal zero.

4 * 3 * 4 = 48

WebGL allows the developer to assign data from different offsets with more or less stride between entries, for a range of uses. This book scratches the surface. Our book WebGL Textures: Introduction to Mipmaps, Sub Images & Atlases covers use of stride and offset with a little more detail.

vertexAttribPointer Table GLSquare

Diagram 20: Calculate Offsets

Method setImageVariables(GLControl) calls the WebGL method enableVertexAttribArray(Number) to activate the texture attribute. The controller section previously demonstrated how to activate the attribute which processes vertices. The following listing activates the attribute which processes texels.

gl.enableVertexAttribArray

(

this.aTexCoord

);

Listing 65: WebGL API enableVertexAttribArray(Number) for Texels

Load the Image File

If this particular GLEntity's sSrc property contains a valid String, then getImageVariables() prepares to load an image file. The sSrc property either equals null, or includes the path and file name for an image to use for texture data. The method getImageVariables(GLControl data) assigns a new JavaScript Image element to this GLEntity's img property, with the following line.

this.img = new Image();

Passing Values Through Event Listeners

Create properties on the Image to save references to this entity and the controller. Then assign an onload event listener for the Image. The Image properties become part of the onload event object's currentTarget properties.

We need the onload event listener to have access to the controller and this entity. When an Image event triggers, the this property represents the Image object, not the entity or controller. Yet after the image file loads, we need references to the GLEntity and GLControl classes in order to continue initialization.

The following listing demonstrates creating properties and assigning references to the controller and this entity. controller is a reference to GLControl. this is a reference to the current GLEntity.

this.img.controller = controller;

this.img.entity = this;

Listing 66: Assign GLControl and GLEntity to Image

The following listing assigns the GLEntity method named setImage(EventObject ev), as the onload event listener for the current Image element. Assign the image file's source path, stored in the String named sSrc, to the Imageelement's src property. Now the Image has a source file and the download process may begin.

this.img.onload = this.setImage;

this.img.src = this.sSrc;

Listing 67: Assign Image onload Event Listener

getImageVariables(GLControl controller) Summary

Method getImageVariables(GLControl) prepares shader variables to process one texture for a GLEntity.

If an associated uniform sampler2D exists in the shader, then the entity maintains the uniform's location. If an associated attribute exists in the shader, then the entity maintains the attribute's location. Entities use the idx property to access shader variables.

getImageVariables(GLControl) saves shader variables, and calls vertexAttribPointer() to process texels through a vertex shader attribute. For entities with an image source file path, getImageVariables(GLControl) prepares to download the image. The image's onload event named setImage() completes initializing a texture. See the source code for method getImageVariables().

Assign WebGL Texture Properties

The following diagram demonstrates the flow of events after an image file downloads. The solid black circle represents the start of the diagram, with setImage(). The black circle with black outline represents the end of the diagram, with animOne(). Rectangles with medium blue backgrounds represent GLEntity methods. For readers with black and white display screens, GLEntity methods display in rectangles with medium gray backgrounds. Rectangles with white backgrounds represent GLControl methods.

The diagram begins with the GLEntity method setImage(ev). The method setImage() activates when a file downloads. setImage() and subsequent method calls assign a number of WebGL texture properties. Method setImage(ev)calls GLEntity methods setWrapToEdges(gl) and setMinMagFilters(gl). Finally setImage(ev) calls the GLControl method setProgramVariables(controller). The diagram terminates initialization with a black outlined circle. The last method call named animOne(ev) displays just one frame, the last frame of the animation.

This section includes the following WebGL methods uniform1i(Number, Number), createTexture(), activeTexture(Number), bindTexture(Number, WebGLTexture), pixelStorei(Number,boolean), validateProgram(WebGLProgram),getProgramParameter(WebGLProgram,Number), getProgramInfoLog(WebGLProgram), deleteProgram(WebGLProgram), drawElements(), texImage2D(), and texParameteri(Number,Number,Number) with four unique calls.

After Images Download

Diagram 21: After Images Download

Method setImage(ev) activates after an Image's onload event listener triggers. However procedural textures may call setImage(ev) immediately.

The only parameter to setImage(ev) may represent either an event object or the controller. When an onload event triggers setImage(ev), then the parameter represents an event object. When a procedural texture method calls setImage(ev), then the parameter represents a reference to GLControl.

Procedural textures use an overloaded version of texImage2D(). See the section titled Procedural Textures for details.

This section focuses on preparing WebGL texture settings after an onload event. The event's currentTarget property is the Image element. We previously demonstrated creating properties controller and entity on the Image itself. The following listing demonstrates retrieving references to to the controller and entity. ev.currentTarget references the Image. Therefore ev.currentTarget.controller references an instance of GLControl and ev.currentTarget.entity references GLEntity.

if (ev.currentTarget != null){

controller = ev.currentTarget.controller;

entity = ev.currentTarget.entity;

}

Listing 68: Retrieve GLControl and GLEntity from Onload Event

Texture by Unit Number

The shader recognizes textures by unit number. Activate textures by unit number with the WebGL method activeTexture(gl.TEXTURE0 + number). Once a texture unit is active, image data, filter, and mapping settings apply to the active unit. Finally assign a texture unit to a sampler2D with the WebGL method uniformi(sampler2D, number). After the shader has a valid sampler2D, the renderer may process the texture.

GLEntity.uSampler is the location of a sampler2D in the shader. GLEntity.idx references a texture unit number. For instance activate the texture with the WebGL method activeTexture(gl.TEXTURE0 + entity.idx). Create a texture. Assign WebGL settings for the texture. Assign the texture to a shader's sampler with the WebGL method uniformi(entity.uSampler,entity.idx).

Texture Preparation Sequence

GLEntity follows the sequence listed below, to prepare a texture for processing in the shader.

1. Assign a texture unit to a sampler with WebGL method uniform1i().

2. Create a texture with WebGL method createTexture().

3. Activate a texture unit with WebGL method activeTexture().

4. Bind the texture to a target with WebGL method bindTexture().

5. Tell the GPU how to store pixel data with WebGL method pixelStorei().

6. Upload image data with WebGL method texImage2D().

7. Assign wrapping modes with WebGL method texParameteri().

8. Assign minification and magnification filters with WebGL method texParameteri().

The next few sections cover each WebGL method and feature in the sequence.