Key Content of This Lesson#
Why WebGL / Why GPU?#
- What is WebGL?
- GPU ≠ WebGL ≠ 3D
- Why is WebGL not as simple as other frontend technologies?
Modern Image Systems#
- Raster: Almost all modern graphics systems are based on raster to draw graphics, where raster refers to the array of pixels that make up an image.
- Pixel: A pixel corresponds to a point on the image, typically storing color and other information for a specific location on the image.
- Frame Buffer: During the drawing process, pixel information is stored in the frame buffer, which is a block of memory.
- CPU (Central Processing Unit): Responsible for logical computation.
- GPU (Graphics Processing Unit): Responsible for graphics computation.
- As shown in the image, the rendering of modern images follows the process in the figure.
- Contour extraction/meshing
- Rasterization
- Frame buffer
- Rendering
The Pipeline#
GPU#
- The GPU consists of a large number of small computing units.
- Each computing unit is responsible for processing very simple calculations.
- Each computing unit is independent of one another.
- Therefore, all calculations can be processed in parallel.
Relationship between WebGL & OpenGL#
OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables (umich.edu)
WebGL Drawing Steps#
Steps
- Create a WebGL context
- Create a WebGL Program
- Store data in the buffer
- Read buffer data to the GPU
- The GPU executes the WebGL program and outputs the result
As shown in the image, explanations for a few terms:
- Raw Vertices & Primitives
- Vertex Processor Vertex Shader
- Sent to Fragment Shader for processing: Fragment Processor
Creating a WebGL Context#
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
// Create context, note compatibility
function create3DContext(canvas, options) {
const names = ['webgl', 'experimental-webgL','webkit-3d','moz-webgl']; // Feature detection
if(options.webgl2) names.unshift(webgl2);
let context = null;
for(let ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], options);
} catch(e) {
// no-empty
}
if(context) {
break;
}
}
return context;
}
Creating a WebGL Program (The Shaders)#
-
Vertex Shader
Process the position of each vertex in parallel using the typed array position.
attribute vec2 position; // vec2 two-dimensional vector void main() { gl_PointSize = 1.0; gl_Position = vec4(position, 1.0, 1.0); }
-
Fragment Shader
Color all pixels within the area surrounded by the vertex contour.
precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Corresponds to rgba(255, 0, 0, 1.0), red }
The specific steps are as follows:
-
Create vertex shader and fragment shader code:
// Vertex shader program code const vertexShaderCode = ` attribute vec2 position; void main() { gl_PointSize = 1.0; gl_Position = vec4(position, 1.0, 1.0); } `; // Fragment shader program code const fragmentShaderCode = ` precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `;
-
Use
createShader()
to create a shader object. -
Use
shaderSource()
to set the shader program code. -
Use
compileShader()
to compile a shader.// Vertex shader const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertex); gl.compileShader(vertexShader); // Fragment shader const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragment); gl.compileShader(fragmentShader);
-
Use
createProgram()
to create aWebGLProgram
object. -
Use
attachShader()
to add a fragment or vertex shader to theWebGLProgram
. -
Use
linkProgram()
to link the givenWebGLProgram
, completing the process of preparing GPU code for the program's fragment and vertex shaders. -
Use
useProgram()
to add the definedWebGLProgram
object to the current rendering state.// Create shader program and link const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program);
Storing Data in the Buffer (Data to Frame Buffer)#
- Coordinate System: The WebGL coordinate system is normalized, while the browser and canvas2D coordinate system has the top-left corner as the origin, with the y-axis down and the x-axis right, with coordinate values relative to the origin. The WebGL coordinate system has the center of the drawing canvas as the origin, following a normal Cartesian coordinate system.
Represent the vertices using a vertex array, and use createBuffer()
to create and initialize a WebGLBuffer
object for storing vertex data or shader data, returning bufferId
. Then use bindBuffer()
to bind the given bufferId
to the target and return it, and finally use bufferData()
to bind the data to the buffer.
// Vertex data
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
// Create buffer
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
Reading Buffer Data to GPU (Frame Buffer to GPU)#
getAttribLocation() returns the index pointing to a given attribute in the
WebGLProgram
.vertexAttribPointer() tells the GPU to read vertex data from the currently bound buffer (specified by bindBuffer()).
enableVertexAttribArray() can enable the generic vertex attribute array at the specified index in the attribute array list.
const vPosition = gl.getAttribLocation(program, 'position'); // Get the address of the position variable in the vertex shader
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0); // Set the length and type for the variable
gl.enableVertexAttribArray(vPosition); // Activate this variable
Output Result (Output)#
drawArrays() draws primitives from the vector array.
// output
gl.clear(gl.COLOR_BUFFER_BIT); // Clear the buffer data
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
Is WebGL Too Complex? Other Methods#
Canvas 2D#
Look at how simple the canvas2D is for drawing the same triangle:
// Canvas is simple and straightforward, everything is encapsulated
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(250, 0);
ctx.lineTo(500, 500);
ctx.lineTo(0, 500);
ctx.fillStyle = 'red';
ctx.fill();
Mesh.js#
mesh-js/mesh.js: A graphics system born for visualization 😘. (github.com)
const {Renderer, Figure2D, Mesh2D} = meshjs;
const canvas = document.querySelector('canvas');
const renderer = new Renderer(canvas);
const figure = new Figure2D();
figure.beginPath();
figure.moveTo(250, 0);
figure.lineTo(500, 500);
figure.lineTo(0, 500);
const mesh = new Mesh2D(figure, canvas);
mesh.setFill({
color: [1, 0, 0, 1],
});
renderer.drawMeshes([mesh]);
Earcut#
Use Earcut for triangulation.
const vertices = [
[-0.7, 0.5],
[-0.4, 0.3],
[-0.25, 0.71],
[-0.1, 0.56],
[-0.1, 0.13],
[0.4, 0.21],
[0, -0.6],
[-0.3, -0.3],
[-0.6, -0.3],
[-0.45, 0.0],
];
const points = vertices.flat();
const triangles = earcut(points);
3D Meshing#
Exported by designers for us, then extracted.
SpriteJS/next - The next generation of spritejs.
Graphic Transformations (Transforms)#
This is related to knowledge of digital image processing (everything learned is coming back.jpg).
Translation#
Rotation#
Scaling#
Linear Transformation (Rotation + Scaling)#
From linear transformations to homogeneous matrices.
Another example from the teacher: Apply Transforms
3D Matrix#
The four homogeneous matrices (mat4) of the standard 3D model:
- Projection Matrix (Orthographic and Perspective Projection)
- Model Matrix (Transforming vertices)
- View Matrix (3D perspective, imagine it as a camera under the camera's viewport)
- Normal Matrix (Normal vectors perpendicular to the surface of the object, usually used for calculating lighting on the object)
Read more#
- The Book of Shaders (Introduces fragment shaders, very fun)
- Mesh.js (Low-level library, heh)
- Glsl Doodle (A lightweight library for fragment shaders, with many small demos)
- SpriteJS (An open-source library written by Teacher Yueying, orz)
- Three.js (Many interesting
gamesprojects) - Shadertoy BETA (Many interesting projects)
Summary and Thoughts#
In this lesson, the teacher explained WebGL drawing and its related libraries in great detail, showcasing many interesting WebGL mini-projects~
Most of the content cited in this article comes from Teacher Yueying's class and MDN! Teacher Yueying, you are awesome!