Dynamic Control of WebGL Shaders: Attributes, Uniforms, and Buffer Objects
This tutorial explains how to dynamically control WebGL vertex and fragment shaders by using attribute and uniform variables, assign values from JavaScript, manage buffer objects for large vertex datasets, and render multiple points with proper WebGL API calls.
After creating a WebGL program, dynamic control of shaders is required to achieve the desired visual effects. This article introduces the two key variable types— attribute for vertex shaders and uniform for both vertex and fragment shaders—that bridge JavaScript and GLSL ES.
Making Vertex Positions Dynamic
Replace fixed vec4 values in the vertex shader with variables and use attribute to pass data from JavaScript.
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';Obtain the attribute location and assign values:
var aPosition = gl.getAttribLocation(gl.program, 'a_Position');
if (aPosition < 0) { console.log('Failed to get the storage location of a_Position'); }
gl.vertexAttrib3f(aPosition, 1.0, 1.0, 0.0);
// Or using a Float32Array
var p = new Float32Array([1.0, 1.0, 1.0]);
gl.vertexAttrib3fv(aPosition, p);The vertexAttrib family follows the naming convention where the suffix indicates the number of components (1‑4), the data type ( f for float, i for integer, etc.), and whether a vector ( v ) is passed.
Fragment Shader Programming
Fragment shaders use uniform variables to receive data from JavaScript.
var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec4 vColor;\n' +
'void main() {\n' +
' gl_FragColor = vColor; // Set the point color\n' +
'}\n';Get the uniform location and set its value:
var vColor = gl.getUniformLocation(gl.program, 'vColor');
gl.uniform4f(vColor, 1.0, 0.0, 0.0, 1.0);
// Or using a Float32Array
var color = new Float32Array([1.0, 0.0, 0.0, 1.0]);
gl.uniform4fv(vColor, color);Buffer Objects
To render many vertices efficiently, WebGL provides buffer objects that store vertex data in GPU memory.
Create a buffer: gl.createBuffer()
Bind it to a target: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
Upload data: gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
Link it to an attribute: gl.vertexAttribPointer(...)
Enable the attribute array: gl.enableVertexAttribArray(...)
Example of creating a buffer and drawing 200 points:
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) { console.log('Failed to create the buffer object'); return -1; }
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, createPoints(), gl.STATIC_DRAW);
var a_position = gl.getAttribLocation(gl.program, 'a_Position');
gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_position);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 200);
function createPoints() {
var arr = [];
var n = 20, m = 10;
for (var i = 0; i < n; i++) {
for (var j = 0; j < m; j++) {
var x = webglX(-(width/2) + i*20);
var y = webglY((height/2) - j*20);
var z = -1;
arr = arr.concat([x, y, z]);
}
}
return new Float32Array(arr);
}After executing the above steps, a dense set of points appears on the canvas, confirming that the shader programming and buffer handling are correctly implemented.
Further reading: MDN WebGL Uniform Documentation
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.