WebGL API Type Uint8Array - WebGL Textures & Vertices: Beginner's Guide (2015)

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

WebGL API Type Uint8Array

Every procedural texture described with the book, creates a Uint8Array. A Uint8Array represents a typed array with unsigned integer entries of 8 bits. The values for an entry in the array range from 0 to 255. Eight bit unsigned values represent 20 - 1 = 0 to 28 - 1 = 255. The values range from {0...255}.

Entries in order represent red, green, and blue color channels. For example given a Uint8Array named u8a, the value at u8a[0] represents the amount of red in the first pixel. The value at the value at u8a[1] represents the amount of green in the first pixel. The value at u8a[2] represents the amount of blue in the first pixel.

Additionally the value at u8a[3] represents the amount of red in the second pixel. The value at the value at u8a[4] represents the amount of green in the second pixel. The value at u8a[5] represents the amount of blue in the second pixel. Every three entries within u8a declare the color for one pixel.

The examples prepare an array filled with color data to display a square texture 32 x 32 pixels in dimension. To fill the array we need 32 * 32 pixels. Each pixel requires 3 entries, where each entry represents a red, green, or blue channel. We need a Uint8Array with 3072 entries.

32 * 32 * 3 = 3072

The constructor saves a member variable named nBuffer to maintain the size of each Uint8Array with the following line of JavaScript.

this.nBuffer = Number(3072);.

Procedural Texture: Red Square

Red Square

This section describes how to create a red texture with method generateSquareR(). The GLTexProcedure class defines method generateSquareR() to create data for a solid red texture. First generateSquareR() instantiates a Uint8Array.

Every texture generation method in this book follows the same pattern. The first line initializes a Uint8Array named u8a. The following line demonstrates creating a Uint8Array of the desired length.

var u8a = new Uint8Array(this.nBuffer);.

The body of each texture generation method fills u8a with color data within a for loop. Every three entries within u8a represent one pixel with three color channels. One channel for red. One channel for green, and one for blue, in that order. The following line demonstrates iterating over every three entries in the array.

for (var i = 0; i < this.nBuffer; i = i+3).

Assume c represents a value between 0 and 255. Fill the red color channel with u8a[i] = c. Fill the green color channel with u8a[i + 1] = c. Fill the blue color channel with u8a[i + 2] = c. The last line of every texture generation method, returns the Uint8Array with the following line return u8a;.

To generate a solid red texture, assign the maximum value to the first channel, which represents red. The red channel receives the value 255. The second and third channels represent green and blue. Green and blue channels receive the minimum value 0. The following listing demonstrates creating a solid red texture.

for (i = 0; i < this.nBuffer; i+=3){

u8a[i] = 255; // Red

u8a[i + 1] = 0; // Green

u8a[i + 2] = 0; // Blue

}

Listing 20: Generate Solid Red Procedural Data

See the entire generateSquareR() method.

Procedural Texture: Blue Green Gradient

Gradation

This section describes how to create gradient texture data with method generateGradientBG(). The GLTexProcedure class defines method generateGradientBG(), to create data for a linear gradation from left to right. The left side displays primarily blue. The right side displays primarily green.

The first line in every texture generation method instantiates a Uint8Array named u8a. The body of every texture generation method fills the array with color data inside a for loop. The last line in every texture generation method returns u8a.

Each row in a 32 x 32 pixel texture includes 96 color channels, because each pixel includes 3 color channels. 32 * 3 = 96. We use the variable j to represent the particular pixel we're coloring per row. In other words j represents a column. Every column is the same color in a left to right linear gradient. The following line assigns a column number from 0 to 95 to j.

j = i % 96;.

The blue channel starts at 255 and ends at 160. Assign the blue channel a value with the following line.

u8a[i + 2] = 255-j; .

We want the green channel to follow an opposite pattern from the blue channel. In other words we want less green for the columns on the left and more green for the columns on the right. Use the variable k for the green channel. The following line reverses the values for k which start at 160 and end at 255.

k = 95 - j;

Iterate over every three entries within u8a to assign color channels. The following listing demonstrates creating a graded blue green texture. See the entire generateGradientBG() method.

for (var i = 0; i < this.nBuffer; i = i+3){

// Each row r,g,b{0..95}

// Each row one pixel {0..31}

j = i % 96;

k = 95 - j;

// Assign Red, Green,

// Blue values:

u8a[i] = 0;

u8a[i + 1] = 255-k;

u8a[i + 2] = 255-j;

}

Listing 21: Generate Blue Green Gradation Procedural Data

Procedural Texture: Red Blue Stripes

Stripes

This section describes how to create a texture with alternating red and blue stripes. The GLTexProcedure class defines method generateStripesRB(), to create data with red and blue vertical stripes.

The first line in every texture generation method instantiates a Uint8Array named u8a. The body of every texture generation method fills the array with color data inside a for loop. The last line in every texture generation method returns u8a.

All we need to generate stripes is to know if the for loop is processing and odd or even numbered pixel. The following line assigns either 0 or 1 to the variable j, with the modulo operator.

var j = i % 2;

If j equals zero, then the pixel represents an even entry in the array. If j equals one, then the pixel represents an odd entry in the array.

Iterate over every three entries within u8a to assign color channels. The following listing demonstrates creating a texture with alternating red and blue vertical stripes. See the entire generateStripesRB() method.

for (var i = 0; i < this.nBuffer; i = i+3){

var j = i % 2;

// Red stripe:

if (j == 0){

u8a[i] = 255; // Red

u8a[i+1] = 0;

u8a[i+2] = 0;

}

// Blue stripe:

else {

u8a[i] = 0;

u8a[i+1] = 0;

u8a[i+2] = 255; // Blue.

}

}

Listing 22: Generate Red and Blue Stripes Procedural Data

Procedural Texture: Four Tiles

Tiles

This section describes how to create a texture with four different colored tiles. Each quadrant or quarter of the texture displays a different color. The GLTexProcedure class defines method generateTilesRGB(), to create data with red, blue, violet, and green tiles.

The first line in every texture generation method instantiates a Uint8Array named u8a. The body of every texture generation method fills the array with color data inside a for loop. The last line in every texture generation method returns u8a.

Determine which quadrant the pixel represents. JavaScript checks to determine if the pixel displays in the top or bottom half. Then JavaScript checks to determine if the pixel displays on the left or right side.

Find the Top Half

The total number of entries in the array equals 3072, which we assigned to the instance variable nBuffer. The line nHalf = this.nBuffer/2;, assigns the local variable nHalf half the number of entries in the array. Colors for the top half of the texture reside in the first half of the array. Within the for loop the statement bTop = i < nHalf, assigns either true or false to the local variable bTop. If bTop equals true then i displays within the top half of the texture. Otherwise i displays in the bottom half of the texture.

Find the Left Half

We know each row equals 96 entries in the Uint8Array. Because 32 * 3 = 96. Every 96 entries represent another row. The following line assigns the column number to variable j.

var j = i % 96;

48 equals one half of a row because because 96/2 = 48. Therefore bLeft = j < 48 equals true when j represents the left half of the texture. If j represents the right half of the texture, then bLeft equals false.

The following listing demonstrates assigning color channels based on quadrant. In other words display four square tiles on a square texture map. See the entire generateTilesRGB() method.

for (var i = 0; i < this.nBuffer; i = i+3){

// Default black:

nRed = Number(0);

nGreen = Number(0);

nBlue = Number(0);

// pixels display

// in the top half if

// less than

// half the size

// of the entire buffer.

bTop = i < nHalf;

// j equals

// column number:

var j = i % 96;

bLeft = j < 48;

// Top tiles.

if (bTop){

// Top left tile:

if (bLeft){

nRed = Number(255);

}

// Top right tile.

else {

nBlue = Number(255);

}

}

// Bottom tiles.

else {

// Bottom left.

// Blue+Red=Violet.

if (bLeft){

nRed = Number(255);

nBlue = Number(255);

}

// Bottom right tile.

else {

nGreen = Number(255);

}

}

// Assign three

// color channels

// for one pixel.

u8a[i] = nRed;

u8a[i+1] = nGreen;

u8a[i+2] = nBlue;

}

Listing 23: Generate Tiles Procedural Data

Procedural Texture: Red Green Random Colors

Random Colors

This section describes how to create a texture with random values for red and green color channels. The GLTexProcedure class defines method generateRandomRG(), to create data with random values for red and green.

The first line in every texture generation method instantiates a Uint8Array named u8a. The body of every texture generation method fills the array with color data inside a for loop. The last line in every texture generation method returns u8a.

The for loop within generateRandomRG() calls the JavaScript method Math.random() which returns a value between 0.0 and 1.0. Multiply the random number by 255 to obtain a value between 0.0 and 255.0. Values might exist to the right of the decimal point. A Uint8Array only holds whole number integers. In other words entries in Uint8Array can't include floating point numbers. Assignment to an entry in the Uint8Array truncates values after the decimal point. See the entire generateRandomRG() method.

for (var i = 0; i < this.nBuffer; i = i+3){

// Red:

u8a[i] = Math.random()*255;

// Green:

u8a[i + 1] = Math.random()*255;

// Blue:

u8a[i + 2] = 0;

}

Listing 24: Generate Random Colors Procedural Data

WebGL Properties for Procedural Textures

The GLEntity Details section, explains how to prepare textures from image files which download. Procedural textures require two modifications to the texture initialization process. First the WebGL method texImage2D() has two overloads. The simpler overload applies to image files. Image objects maintain width and height parameters. The texImage2D() overload which applies to procedural textures requires width and height parameters in the method call. Second the book's GLEntity class initializes textures after an image downloads, with the method setImage(ev). We can call setImage(ev) directly to prepare a texture from a Uint8Array.

Change to WebGL Initialization

The following diagram provides an overview of the sequence of events to initialize a WebGLTexture from a Uint8Array of procedurally generated data.

First select a texture from the Web page's drop down menu. The selection calls a method to fill a Uint8Array with color data. Second call the GLEntity method setImage(ev). Pass a reference of the controller as the only parameter.Third GLEntity calls the overloaded version of the WebGL method texImage2D(). The rest of the method calls are covered in the sections titled Entity Details and Finalize then Display the Mesh.

Procedural Texture Diagram

Diagram 9: Procedural Texture Diagram

If nImagesToLoad == 0 then GLEntity calls the overloaded version of texImage2D(). With procedural textures, we don't need to load an image. Overloaded means the same method can receive a different number of parameters. In the section titled Load the Image File, we demonstrate calling texImage2D() with six parameters. However call texImage2D() with nine parameters to properly initialize procedural textures.