3D Gallery with Great Artwork Imagine Logo with Three Colors Beach Cabin 3D Android Tablet with Different Screens Studio Apartment Red Figured Greek Vase on Blue Background Silver and Gold Flashlight Lake on Fire

WebGL Points Tutorial

Swipe From the Center Outward

Point Explosion Interactive Example Face Mapped Point Interactive Example Overview JavaScript Vertex Shader Fragment Shader Tips Explosion Texture Face Mapped Texture Summary WebGL Particle Explosion Source

Overview

This tutorial covers four techniques to use with WebGL points. The WebGL Particle Explosion project combines concepts used in three WebGL point examples including WebGL Square Points, WebGL Face Mapped Points and WebGL Simple Round Points. First points react to user interaction. Second points are mapped with a texture. Third shaders draw circular points. Fourth the WebGL blend function causes overlapping points to appear brighter. Brighter overlapping points create the illusion of more heat toward the center of the explosion when sprites draw over each other.

A number of initialization details were left out, to focus on the main topic. Most WebGL projects initialize as described in Seven Thunder Software's Learn WebGL Series. However information in this tutorial should apply to other WebGL projects, such as those that use Unity or Three.js, as well.

JavaScript Constructor

The JavaScript constructor creates a reference of type GLPointsExplode defined within JavaScript file GLPointsExplode.js.

First this tutorial covers JavaScript which loads a square flat plane defined with sixteen vertices. Each vertex represents a point. Create three entities which will access the same same set of vertices, with different matrices for rotation and translation. You may download the JavaScript Model which is used to create a square plane with sixteen vertices.

The following listing initializes three square planes. The matrices are offset along the X, Y, or Z axes.

var shapes = new PlanePoints();

var aIm = new Array();

// Initialize the
// entity with a texture.
var e =  new GLEntity(
 s,
 0
);

e.nOffset = Number(
 shapes.aOffset[0]
);

e.nCount = Number(
 shapes.aCount[0]
);

e.matrix[14] = -2.5;

aIm.push(e);
 
e =  new GLEntity(
 null,1
);

e.nOffset = Number(
 shapes.aOffset[0]
);

e.nCount = Number(
 shapes.aCount[0]
);

e.matrix[14] = -5;
e.matrix[12] = -0.2;
e.matrix[13] = -0.2; 

aIm.push(e);
 
e =  new GLEntity(
 null,
 2
);

e.nOffset = Number(
 shapes.aOffset[0]
);

e.nCount = Number(
 shapes.aCount[0]
);

e.matrix[14] = -5;
e.matrix[12] = 0.2;
e.matrix[13] = 0.2; 
aIm.push(e);

Last the constructor creates a controller, GLControl reference. The controller initializes a vertex buffer object with the vertices and element array index declared in the JavaScript Model. For more details regarding the exploding points example see the JavaScript Constructor.

var controller = new GLControl
(
 shapes.aVertices,
 shapes.aIndices,  
 aIm,
 this
); 

JavaScript Initialization

Before displaying the first frame JavaScript prepares some variables for rendering. For the explosion example set the clear color to black.

gl.clearColor(
 0.0,
 0.0,
 0.0,
 1.0
);

The plane includes vertices only. Neither texels nor vertex colors are needed with face mapped points. The following call to vertexAttribPointer() describes the arrangement of vertices within the vertex buffer object. Three vertices follow each other sequentially with no gaps in between.

gl.vertexAttribPointer
(
 controller.aPosition, 
 3, 
 gl.FLOAT, 
 gl.FALSE, 
 0, 
 0
); 

Save the location of three uniforms named uf_x, uf_y, and uf_pointsize. Uniforms uf_x and uf_y represent the user's current X and Y touch or mouse point on the canvas. Uniform uf_pointsize adjusts the size of WebGL points before performing a draw operation.

glDemo.ufX = gl.getUniformLocation(
 controller.program, 
 "uf_x"
); 
 
// Uniform to modify Y
// coordinates by time.
glDemo.ufY = gl.getUniformLocation(
 controller.program, 
 "uf_y"
); 
 
// Change size of
// points by time.
glDemo.ufPointSize = gl.getUniformLocation(
 controller.program, 
 "uf_pointsize"
); 

Prepare to blend points when they overlap. WebGL methods enable(gl.BLEND) and blendFunc(...) modify color channels when points draw over each other. The destination gl.ONE_MINUS_CONSTANT_COLOR selection, causes the center of the explosion to appear brighter. Color channels are added together when areas overlap. The following listing demonstrates preparing blendFunc() to modify WebGL rendering output for the WebGL Particle Explosion example.

gl.enable(
 gl.BLEND
);

gl.blendFunc(
 gl.ONE, 
 gl.ONE_MINUS_CONSTANT_COLOR
); 

See the entire JavaScript initialization source code.

JavaScript render()

The render() method accesses properties nX, nY, and nMiddle to calculate the amount of rotation for each plane's matrix. Properties nX and nY represent the user's current touch or mouse coordinates over the canvas. Property nMiddle is just the middle of the canvas.

Variable glDemo is a reference to this project's class named GLPointsExplode. The following few lines demonstrate preparing X and Y coordinates for upload to the vertex shader using properties nX, nY, and nMiddle.

Before upload to the GPU coordinates nX and nY need conversion from canvas coordinates to GPU coordinates. Canvas coordinates are in the range {0..canvas.width}. GPU coordinates are in the range {-1.0..0..+1.0}.

var glDemo = controller.glDemo;

var aEntities = controller.aEntities;


// Normalize the current X,Y touch point.
// Range [-1.0...+1.0]

var nX = Number(
 glDemo.nX - glDemo.nMiddle
);

var nXA = Math.abs(nX);

nX /= glDemo.nMiddle;

 
var nY = Number(
 glDemo.nY - glDemo.nMiddle
);

var nYA = Math.abs(nY);

nY /= glDemo.nMiddle;

var nAvg = (nXA+nYA)/glDemo.nMiddle;

var nPointSize = (1 - nAvg) * 128;

Calculate the point size based on the user's touch position on the canvas using converted GPU coordinates.

var nAvg = (nXA+nYA)/glDemo.nMiddle;

var nPointSize = (1 - nAvg) * 128;

Upload the X, Y, and point size values to the vertex shader.

gl.uniform1f
(
 glDemo.ufX,
 nX
);
 
gl.uniform1f
(
 glDemo.ufY,
 nY
);
 
gl.uniform1f
(
 glDemo.ufPointSize,
 nPointSize
);

Last iterate over the list of entities. For each entity rotate the matrix, upload the matrix to the shader, then draw the matrix.

Three sets of points are drawn with different rotations. Mouse or touch interaction offsets the size and coordinates of points. For more exciting realistic explosions apply random numbers to point coordinates.

mat4.set(
 e.matrix,
 matrix
);

if (i % 2 == 1){
 mat4.rotate(
 matrix,
 controller.nRad,
 [0,0,1]
);

}
else {
 mat4.rotate(
  matrix,
  -controller.nRad,
  [0,0,1]
 );
}  

gl.uniformMatrix4fv
(
 controller.uMatrixTransform, 
 false, 
 new Float32Array
 (
  matrix
 )
); 

The following call to the WebGL method drawElements(), draws a set of points. Number e.nCount contains the number of points to draw. Number e.nOffset indicates where, to begin drawing, within the vertex buffer object. Notice the first parameter is gl.POINTS. Each vertex represents one point. WebGL examples often pass gl.TRIANGLES as the first parameter, which draws triangles.

WebGL drawElements()

gl.drawElements
(
 gl.POINTS, 
 e.nCount, 
 gl.UNSIGNED_SHORT, 
 e.nOffset
); 

See the entire rendering source code.

Vertex Shader

The vertex shader copies the current vertex coordinates from attribute a_position to local vector pos. Next the shader multiplies the current vertex X and Y coordinates by uniforms uf_x and uf_y. The previous section titled JavaScript render(), demonstrated uploading values to uniforms uf_x and uf_y during rendering.

vec4 pos = a_position;
pos.x *= uf_x;
pos.y *= uf_y;

Assign the product of the perspective matrix um4_pmatrix, the transformation matrix um4_matrix, and the modified vertex coordinates, to built in vector gl_Position.

gl_Position =  um4_pmatrix * 
 um4_matrix *  
 pos; 

Last assign the value from uniform uf_pointsize to the built in variable gl_PointSize. Modifying gl_PointSize allows different particles to render at different sizes. However not all browsers support it. See the entire vertex shader for more details.

Fragment Shader

The fragment shader first tests to determine if the fragment's coordinate is within the area defined by a circle.

The distance() function returns a floating point value representing the distance between two values. The following function call tests the distance from the center of the point's texture to the current coordinate on the point.

float f_center_distance = distance( 
 gl_PointCoord, 
 vec2(0.5,0.5) 
);
 

If the distance is greater than 0.5 just return. Don't render this fragment. The corners of a square point would return a value of 0.707. The following code avoids drawing the corners of the square, eventually rendering a round area for each point.

if ( f_center_distance >= 0.5 ) {
 discard;  
}

Next sample the texture at the current point coordinate.

vec4 color0 = texture2D(
 u_sampler0, 
 gl_PointCoord
); 

Last multiply the sampled color by constant cf_dark.

See the entire fragment shader for more details.

Tips

Some older Android Chrome and Firefox browsers lock up with texture mapped points. Additionally older Microsoft browsers haven't implemented point size modifications. Points display as one pixel only. However Microsoft Edge does display texture mapped points at various point sizes.

Summary

This tutorial covered four techniques to use with WebGL points. The WebGL Particle Explosion project combines concepts used in three WebGL point examples including WebGL Square Points, WebGL Face Mapped Points and WebGL Simple Round Points. First points react to user interaction. Second points are mapped with a texture. Third shaders draw circular points. Fourth the WebGL blend function causes overlapping points to appear brighter. Brighter overlapping points create the illusion of more heat toward the center of the explosion when sprites draw over each other.


Ads >
Create 3D Games: Learn WebGL Book 2 Simple Shaders: Learn WebGL Book 4
3D Programming for Beginners: Learn WebGL Book 1

for Web graphics!

Copyright © 2022 Amy Butler. All Rights Reserved.