WebGL class
634
We've learned many things so far, and our code examples have quite grown by this point. Before we continue, let's organize our code in the object-oriented style so we could keep things neat and clean. We will update this class from time to time after we learn new things in WebGL. But as for now, it has the following functions:
createProgram
— create GLSL program from the given source code;createTexture
— create WebGL texture from the given image URL. Set up the texture and initialize it inonload
handler;createBuffer
— create vertex buffer, fill it with the given data, and bind topology and vertex attributes information to it;drawArrays
— draw geometry from the given buffer and GLSL program;draw
— rendering callback wrapper which is bound to WebGL class instance and dorequestAnimationFrame
stuff;context
— read-only access to the underlying WebGL context.
Full code of our brand new WebGL class:
var WebGL = function(canvas)
{
const gl = canvas.getContext("webgl2");
// Access WebGL context
this.context = function()
{
return gl;
};
// Create GLSL program from given sources, compile and link
this.createProgram = function(vertexProgram, fragmentProgram)
{
const vs = gl.createShader(gl.VERTEX_SHADER);
const fs = gl.createShader(gl.FRAGMENT_SHADER);
const program = gl.createProgram();
compile(vs, vertexProgram);
compile(fs, fragmentProgram);
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if(!gl.getProgramParameter(program, gl.LINK_STATUS))
{
alert(gl.getProgramInfoLog(program));
}
return program;
};
// Create texture from the given image source URL
this.createTexture = function(src)
{
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
const image = new Image();
image.src = src;
image.onload = function()
{
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this);
gl.generateMipmap(gl.TEXTURE_2D);
};
return texture;
};
// Create vertex buffer with the given topology and vertex attributes
this.createBuffer = function(data, topology, attributes)
{
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
buffer.topology = topology;
buffer.attributes = attributes;
buffer.count = data.length / valuesPerVertex(attributes);
return buffer;
}
// Draw geometry from the given buffer with the given GLSL program
this.drawArrays = function(program, buffer)
{
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
const stride = valuesPerVertex(buffer.attributes) * Float32Array.BYTES_PER_ELEMENT;
let offset = 0;
for(const name in buffer.attributes)
{
const size = buffer.attributes[name];
const location = gl.getAttribLocation(program, name);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, size, gl.FLOAT, false, stride, offset);
offset += size * Float32Array.BYTES_PER_ELEMENT;
}
gl.drawArrays(buffer.topology, 0, buffer.count);
};
// Rendering callback wrapper with binding to WebGL class instance
this.draw = function(callback)
{
callback = callback.bind(this);
const draw = function(timestamp)
{
callback(timestamp);
window.requestAnimationFrame(draw);
};
window.requestAnimationFrame(draw);
};
// Count quantity of values of a single vertex
function valuesPerVertex(attributes)
{
let values = 0;
for(const name in attributes)
{
values += attributes[name];
}
return values;
}
// Compile the given shader and check for errors
function compile(shader, source)
{
gl.shaderSource(shader, source);
gl.compileShader(shader);
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
alert(gl.getShaderInfoLog(shader));
}
}
So our previous “Textures” lesson can be rewritten in a cleaner way:
<script src="webgl.js"></script>
<script type="text/javascript">
function text(id)
{
return document.getElementById(id).value;
}
const wgl = new WebGL(document.getElementById("canvas"));
const gl = wgl.context();
const program = wgl.createProgram(text("vs"), text("fs"));
const texture = wgl.createTexture("texture.jpg");
const vertices =
[
-1, -1, 0, 0,
-1, 1, 0, 1,
1, -1, 1, 0,
1, 1, 1, 1
];
const buffer = wgl.createBuffer(new Float32Array(vertices), gl.TRIANGLE_STRIP,
{
position : 2,
texCoord : 2
});
const aspect = gl.getUniformLocation(program, "aspect");
const time = gl.getUniformLocation(program, "time");
const diffuse = gl.getUniformLocation(program, "diffuse");
wgl.draw(function(timestamp)
{
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1f(aspect, canvas.height / canvas.width);
gl.uniform1f(time, timestamp / 1000);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(diffuse, 0);
wgl.drawArrays(program, buffer);
});
</script>
Now we can easily distinguish how things are initialized and how they're used. Full source code for this lesson: source.zip
Rate this post:
Lesson 15
Share this page:
Learning plan
How to evaluate a vertex-specified data and interpolate it between vertices
14. Textures
An introduction on how to load texture, initialize it properly with WebGL and pass it into GLSL program
15. glMatrix
How to install and use glMatrix library which provides vector and matrix arithmetics and helper functions
16. WebGL class
We've learned many thing by this point. Let's start organizing them neat and clean in the object-oriented way