primers

Following the WebGL basics, do a simple example of drawing a line.

Mainly refer to the following two articles:

Draw a line

I won’t explain each function in detail below, but I prefer to get a feel for the overall logic first and then look it up as needed.

Create a WebGL context

The basic concept is to use WebGL with the Canvas element:


<canvas id="demo" width="300" height="200"></canvas>

Copy the code

const canvasObj = document.querySelector("#demo");

const glContext = canvasObj.getContext("webgl");

if(! glContext) { alert("Browsers do not support WebGL");

return;

}

Copy the code

Then prepare the vertex data.

Prepare and buffer vertex data

In WebGL all objects are in 3D space and drawing a line requires two vertices, each with a 3D coordinate:


let vertices = [

-0.5, -0.5.0.0.0.5, -0.5.0.0

];

Copy the code

There are several types of buffers. The vertex buffer object is of type gl.array_buffer.


/** * set buffer *@param {*} Gl WebGL context *@param {*} VertexData specifies the vertexData */

function setBuffers(gl, vertexData) {

// Create a blank buffer object

const buffer = gl.createBuffer();

// Bind the target

gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

// WebGL does not support using JavaScript primitive array types directly

const dataFormat = new Float32Array(vertexData);

// Initialize the data store

gl.bufferData(gl.ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW);

},

Copy the code

The bufferData method copies the data to the current binding buffer object and provides parameters to manage the given data:

  • STATIC_DRAW: The contents of the buffer are likely to be used frequently and will not change often.

  • DYNAMIC_DRAW: The contents of the buffer may be used frequently and change frequently.

  • STREAM_DRAW: The contents of the buffer may not be used very often.

The data for the line doesn’t change, it stays the same every render, so the type used here is STATIC_DRAW. Now that you have the vertex data stored in memory on your video card, start preparing the vertex shaders.

Vertex shader

Vertex shaders need to be written in GLSL ES, and there are two types of front-end writing:

  • Script tag wrapped, used like a DOM object.

  • A pure string.


<script id="shader" type="x-shader/x-vertex">

attribute vec3 vertexPos;

void main(void){

gl_Position = vec4(vertexPos, 1);

}

</script>

<script>

const shader = document.getElementById('shader').innerHTML,

</script>

Copy the code

Each vertex has a 3D coordinate, creating an input variable vertexPos of type VEC3, which represents a vector of triples floating point numbers.

Gl_Position is a variable built into the shader. In GLSL, a variable has up to four components, the last of which is used for perspective division. The value set by gl_Position becomes the output of the vertex shader. Recall here the state machine mentioned in the basic concept.

Here is the pure character form:


/** * Create vertex shader *@param {*} Gl WebGL context */

function createVertexShader(gl) {

// Vertex shader GLSL code

const source = ` attribute vec3 vertexPos; void main(void){ gl_Position = vec4(vertexPos, 1); } `;

// Create a shader

const shader = gl.createShader(gl.VERTEX_SHADER);

// Sets the vertex shader code

gl.shaderSource(shader, source);

/ / compile

gl.compileShader(shader);

// Check whether the compilation succeeded

if(! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Compiling shader error:" + gl.getShaderInfoLog(shader));

gl.deleteShader(shader);

return null;

}

return shader;

}

Copy the code

In order for WebGL to use the shader, its source code must be compiled dynamically at run time.

  1. The createShader function creates a shader object of type gl.vertex_shader.

  2. The function compileShader compiles.

Next, prepare the fragment shader.

Fragment shader

The fragment shader is also written in GLSL ES. What the fragment shader does is calculate the color output at the end of the pixel, which simply specifies the output white. Gl_FragColor is A built-in variable that represents A color and has four components that correspond to R, G, B, and A.


/** * Create fragment shader *@param {*} Gl WebGL context */

function createFragmentShader(gl) {

// Fragment shader GLSL code

const source = 'void main(void){gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } `;

// Create a shader

const shader = gl.createShader(gl.FRAGMENT_SHADER);

// Sets the fragment shader code

gl.shaderSource(shader, source);

/ / compile

gl.compileShader(shader);

// Check whether the compilation succeeded

if(! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Compiling shader error:" + gl.getShaderInfoLog(shader));

gl.deleteShader(shader);

return null;

}

return shader;

},

Copy the code

Once both shaders are ready, they need to be linked together before they can be used.

Shader program

A shader program object is a version of a shader that has been merged and eventually linked. When linking shaders to a program, it links the output of each shader to the input of the next shader. When the output and input do not match, you get a connection error.

  • The createProgram function creates the object;

  • AttachShader add shader;

  • LinkProgram link to add shader.

When the shader needs to be activated, the useProgram function is called with the object as an argument.


/** * Initialize shader program *@param {*} Gl WebGL context *@param {*} VertexShader vertexShader@param {*} FragmentShader fragmentShader */

function initShaderProgram(gl, vertexShader, fragmentShader) {

// Create a shader object

const shaderProgram = gl.createProgram();

// Add shaders

gl.attachShader(shaderProgram, vertexShader);

gl.attachShader(shaderProgram, fragmentShader);

// Multiple shaders merge links

gl.linkProgram(shaderProgram);

// Check whether the creation is successful

if(! gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Cannot initialize shader program:" + gl.getProgramInfoLog(shaderProgram));

return null;

}

return shaderProgram;

}

Copy the code

So far, you’ve sent input vertex data to the GPU and instructed the GPU how to process it in vertex and fragment shaders. All that’s left is to draw.

draw

  • The vertexAttribPointer function tells WebGL how to interpret vertex data;

  • The enableVertexAttribArray function enables the vertex property, which is disabled by default.

  • The useProgram function activates the shader.

  • The drawArrays function draws. The first argument is the type of pixel to draw. It draws a line, so gl.line_strip.


/** * Initialize shader program *@param {*} Gl WebGL context *@param {*} ShaderProgram shaderProgram object */

function draw(gl, shaderProgram) {

// Get the corresponding data index

const vertexPos = gl.getAttribLocation(shaderProgram, "vertexPos");

// Parse vertex data

gl.vertexAttribPointer(vertexPos, 3, gl.FLOAT, false.0.0);

// Enable vertex attributes. Vertex attributes are disabled by default.

gl.enableVertexAttribArray(vertexPos);

// Activate the shader

gl.useProgram(shaderProgram);

/ / to draw

gl.drawArrays(gl.LINE_STRIP, 0.2);

}

Copy the code

The sample

Here’s an example, and the overall logic looks something like this:


const canvasObj = document.querySelector("#demo");

const glContext = canvasObj.getContext("webgl");

let vertices = [-0.5, -0.5.0.0.0.5, -0.5.0.0]; // Vertex data

setBuffers(glContext, vertices); // Buffer data

const vertexShader = createVertexShader(glContext); // Vertex shader

const fragmentShader = createFragmentShader(glContext); // Fragment shader

const shaderProgram = initShaderProgram(

glContext,

vertexShader,

fragmentShader

); // The shader program object

draw(glContext, shaderProgram); / / to draw

Copy the code

There are a lot of methods and variables involved in this, and it’s really confusing at first, but you’ll get used to it after you read it a few times and type the code yourself.

Some of the questions raised during this period are summarized in the JavaScript WebGL Basic Questions section.

The resources

When I first saw the King rank, I thought it was an inspirational movie.

But a recent episode shattered that notion. The ending song is great!