WebGL
Posted on April 9, 2022
Tags: codeetc
1 Abstract
3 helper functions + 1 subhelper:
initBuffers(gl)
initShaderProgram(gl, vsSource, fsSource)
loadShader (gl, type, source)
drawScene(gl, programInfo, buffers)
Main pipeline:
- ShaderProgram: Vertex Shader + Fragment Shader into initShaderProgram
- Buffer: Build buffers with initBuffers
- ShaderProgram + Buffer = Scene
2 Code
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js" integrity="sha512-eV9ExyTa3b+YHr99IBTYpwk4wbgDMDlfW8uTxhywO8dWb810fGUSKDgHhEv1fAqmJT4jyYnt1iWWMW4FRxeQOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
//----------------------------------------------------------------------
// initBuffers
//
// Initialize the buffers we'll need. For this demo, we just
// have one object -- a simple two-dimensional square.
//
const = initBuffers(gl) => {
// Create a buffer for the square's positions.
const positionBuffer = gl.createBuffer();
// Select the positionBuffer as the one to apply buffer
// operations to from here out.
.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl
// Now create an array of positions for the square.
const positions = [
1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
-1.0, -1.0,
;
]
// Now pass the list of positions into WebGL to build the
// shape. We do this by creating a Float32Array from the
// JavaScript array, then use it to fill the current buffer.
.bufferData(gl.ARRAY_BUFFER,
glnew Float32Array(positions),
.STATIC_DRAW);
gl
return {
position: positionBuffer,
;
}
}
//-----------------------------------------------------------------------
// Draw the scene.
//
const drawScene= (gl, programInfo, buffers) => {
.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl
// Clear the canvas before we start drawing on it.
.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl
// Create a perspective matrix, a special matrix that is
// used to simulate the distortion of perspective in a camera.
// Our field of view is 45 degrees, with a width/height
// ratio that matches the display size of the canvas
// and we only want to see objects between 0.1 units
// and 100 units away from the camera.
const fieldOfView = 45 * Math.PI / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = glMatrix.mat4.create();
// note: glmatrix.js always has the first argument
// as the destination to receive the result.
.mat4.perspective(projectionMatrix,
glMatrix,
fieldOfView,
aspect,
zNear;
zFar)
// Set the drawing position to the "identity" point, which is
// the center of the scene.
const modelViewMatrix = glMatrix.mat4.create();
// Now move the drawing position a bit to where we want to
// start drawing the square.
.mat4.translate(modelViewMatrix, // destination matrix
glMatrix, // matrix to translate
modelViewMatrix-0.0, 0.0, -6.0]); // amount to translate
[
// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
{const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
gl.attribLocations.vertexPosition,
programInfo,
numComponents,
type,
normalize,
stride;
offset).enableVertexAttribArray(
gl.attribLocations.vertexPosition);
programInfo
}
// Tell WebGL to use our program when drawing
.useProgram(programInfo.program);
gl
// Set the shader uniforms
.uniformMatrix4fv(
gl.uniformLocations.projectionMatrix,
programInfofalse,
;
projectionMatrix).uniformMatrix4fv(
gl.uniformLocations.modelViewMatrix,
programInfofalse,
;
modelViewMatrix)
{const offset = 0;
const vertexCount = 4;
.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
gl
}
}
//-----------------------------------------------------------------------
// Initialize a shader program, so WebGL knows how to draw our data
//
const initShaderProgram = (gl, vsSource, fsSource) => {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
// Create the shader program
const shaderProgram = gl.createProgram();
.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl// If creating the shader program failed, alert
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}return shaderProgram;
}
//---------------------------------------------------------------------------
// creates a shader of the given type, uploads the source and
// compiles it.
// SUBHELPER function to be used in initShaderProgram
const loadShader = (gl, type, source) => {
const shader = gl.createShader(type);
// Send the source to the shader object
.shaderSource(shader, source);
gl// Compile the shader program
.compileShader(shader);
gl// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
.deleteShader(shader);
glreturn null;
}return shader;
}
//Everything above are helper functions for main
//MAIN START-----------------------------------------------------
const main = async () => {
let ctxcanv = document.querySelector("#tutorial");
var gl = ctxcanv.getContext('webgl');
var rect = ctxcanv.getBoundingClientRect();
//--------------- Vertex shader program-------------START
const vsSource = `
attribute vec4 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
`;
//--------------- Vertex shader program---------------END
//--------------- Fragment shader program-----------START
const fsSource = `
void main() {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
//--------------- Fragment shader program-------------END
// Initialize a shader program; this is where all the lighting
// for the vertices and so forth is established.
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
// Look up which attribute our shader program is using
// for aVertexPosition and look up uniform locations.
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
,
}uniformLocations: {
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
,
};
}
// Here's where we call the routine that builds all the
// objects we'll be drawing.
const buffers = initBuffers(gl);
// Draw the scene
drawScene(gl, programInfo, buffers);
}
//INIT main()
window.addEventListener("load", () => {main();});
</script>
3 Shader
- Vertex Shader builds the shape
- Fragment Shader fills with color
4 Snipped from other parts of the web
<script>
/*================Creating a canvas=================*/
var canvas = document.getElementById('my_Canvas');
= canvas.getContext('experimental-webgl');
gl
/*==========Defining and storing the geometry=======*/
var vertices = [
-0.5,0.5,0.0,
0.0,0.5,0.0,
-0.25,0.25,0.0,
;
]
// Create an empty buffer object to store the vertex buffer
var vertex_buffer = gl.createBuffer();
//Bind appropriate array buffer to it
.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl
// Pass the vertex data to the buffer
.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl
// Unbind the buffer
.bindBuffer(gl.ARRAY_BUFFER, null);
gl
/*=========================Shaders========================*/
// vertex shader source code
var vertCode =
'attribute vec3 coordinates;' +
'void main(void) {' +
' gl_Position = vec4(coordinates, 1.0);' +
'gl_PointSize = 10.0;'+
'}';
// Create a vertex shader object
var vertShader = gl.createShader(gl.VERTEX_SHADER);
// Attach vertex shader source code
.shaderSource(vertShader, vertCode);
gl
// Compile the vertex shader
.compileShader(vertShader);
gl
// fragment shader source code
var fragCode =
'void main(void) {' +
' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
'}';
// Create fragment shader object
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
// Attach fragment shader source code
.shaderSource(fragShader, fragCode);
gl
// Compile the fragmentt shader
.compileShader(fragShader);
gl
// Create a shader program object to store
// the combined shader program
var shaderProgram = gl.createProgram();
// Attach a vertex shader
.attachShader(shaderProgram, vertShader);
gl
// Attach a fragment shader
.attachShader(shaderProgram, fragShader);
gl
// Link both programs
.linkProgram(shaderProgram);
gl
// Use the combined shader program object
.useProgram(shaderProgram);
gl
/*======== Associating shaders to buffer objects ========*/
// Bind vertex buffer object
.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl
// Get the attribute location
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
// Point an attribute to the currently bound VBO
.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
gl
// Enable the attribute
.enableVertexAttribArray(coord);
gl
/*============= Drawing the primitive ===============*/
// Clear the canvas
.clearColor(0.5, 0.5, 0.5, 0.9);
gl
// Enable the depth test
.enable(gl.DEPTH_TEST);
gl
// Clear the color buffer bit
.clear(gl.COLOR_BUFFER_BIT);
gl
// Set the view port
.viewport(0,0,canvas.width,canvas.height);
gl
// Draw the triangle
.drawArrays(gl.POINTS, 0, 3);
gl</script>
5 Nextjs plot graph
plotting a graph in webgl
- It uses just one array but the x,y axis is encoded by pairing consecutive numbers
- Even indices are the x-axis
- Odd indices are the y-axis
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Shit from '../components/Shit.jsx'
import React, { useState, useEffect,useReducer,useRef } from 'react';
import { mat4 } from 'gl-matrix';
const ptnum = -1
const createPoints = (x,y) => {
const xnormed = 1/Math.max(...x);
const ynormed = 1/Math.max(...y);
const ret = [];
for(let i = 0; i < x.length; i++){
*2] = x[i]*xnormed;
ret[i*2)+1] = y[i]*ynormed;
ret[(i
}= x.length;
ptnum return ret;
}
const initBuffers = (gl) => {
const positionBuffer = gl.createBuffer();
.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl
//---------------------------------------------------------graph plotting
const xdata = [...Array(5).keys()];
const ydata = xdata.map((x)=>x*x);
const positions = createPoints(xdata,ydata);
// const positions = createPoints([1,2,3,4,5],[1,9,1,9,1]);
console.log(positions);
.bufferData(gl.ARRAY_BUFFER,
glnew Float32Array(positions),
.STATIC_DRAW);
glreturn {
position: positionBuffer,
;
}
}
const loadShader = (gl, type, source) => {
const shader = gl.createShader(type);
// Send the source to the shader object
.shaderSource(shader, source);
gl.compileShader(shader);
glif (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
.deleteShader(shader);
glreturn null;
} return shader;
}
const initShaderProgram = (gl, vsSource, fsSource) => {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
// Create the shader program
const shaderProgram = gl.createProgram();
.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl// If creating the shader program failed, alert
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}return shaderProgram;
}const drawScene= (gl, programInfo, buffers,delta) => {
.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl
// Clear the canvas before we start drawing on it.
.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl
// Create a perspective matrix, a special matrix that is
// used to simulate the distortion of perspective in a camera.
// Our field of view is 45 degrees, with a width/height
// ratio that matches the display size of the canvas
// and we only want to see objects between 0.1 units
// and 100 units away from the camera.
const fieldOfView = 45 * Math.PI / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
console.log(mat4)
const projectionMatrix = mat4.create();
// note: glmatrix.js always has the first argument
// as the destination to receive the result.
.perspective(projectionMatrix,
mat4,
fieldOfView,
aspect,
zNear;
zFar)
// Set the drawing position to the "identity" point, which is
// the center of the scene.
const modelViewMatrix = mat4.create();
// Now move the drawing position a bit to where we want to
// start drawing the square.
.translate(modelViewMatrix, // destination matrix
mat4, // matrix to translate
modelViewMatrix-0.0, 0.0, -6.0]); // amount to translate
[.rotate(modelViewMatrix, // destination matrix
mat4, // matrix to rotate
modelViewMatrix, // amount to rotate in radians
delta0, 0, 1]); // axis to rotate around
[
// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
{const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
gl.attribLocations.vertexPosition,
programInfo,
numComponents,
type,
normalize,
stride;
offset).enableVertexAttribArray(
gl.attribLocations.vertexPosition);
programInfo
}
// Tell WebGL to use our program when drawing
.useProgram(programInfo.program);
gl
// Set the shader uniforms
.uniformMatrix4fv(
gl.uniformLocations.projectionMatrix,
programInfofalse,
;
projectionMatrix).uniformMatrix4fv(
gl.uniformLocations.modelViewMatrix,
programInfofalse,
;
modelViewMatrix)
{const offset = 0;
const vertexCount = 4;
//gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
.drawArrays(gl.POINTS,0,ptnum)
gl
}
}
const MyWebGL = () => {
const width = 400;
const height = 400;
const canvasRef = useRef();
const vsSource = `
attribute vec4 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_PointSize = 10.0;
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
`;
const fsSource = `
void main() {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
const initWebGL = () => {
let mycanvas = canvasRef.current;
let gl = mycanvas.getContext('webgl2');
console.log(gl);
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
,
}uniformLocations: {
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
,
};
}const buffers = initBuffers(gl);
const delta = 0.0;
drawScene(gl, programInfo, buffers,delta); //first render
document.addEventListener('keydown',animateFrame(gl,programInfo,buffers,delta));
}
const animateFrame = (gl,programInfo,buffers,delta) => {
const animateFramehelper = (e)=>{
if(e.code == "KeyG"){
console.log(e.code)
= delta + 0.2;
delta // console.log(toggle)
drawScene(gl, programInfo, buffers, delta);
}
}return animateFramehelper;
}useEffect(()=>{
initWebGL();
return(() => {
document.removeEventListener('keydown',animateFrame);
});
})return(
<div>
<canvas ref={canvasRef} id="tutorial" width={width} height={height} style={{border: "1px solid black"}} />
</div>
)
}
export default function Home() {
return (
<div>
<MyWebGL/>
</div>
)}