Introduction
next WebGL basic concepts , do a simple example of drawing a straight line.
Mainly refer to the following two articles:
Draw a line
I won't explain each function in detail below. I prefer to have a sense of the overall logic first, and then check the data as needed during actual use.
Create WebGL context
stay Basic concepts It is mentioned in that WebGL is used through the Canvas element:
<canvas id="demo" width="300" height="200"></canvas>
const canvasObj = document.querySelector("#demo"); const glContext = canvasObj.getContext("webgl"); if (!glContext) { alert("Browser does not support WebGL"); return; }
Then prepare the vertex data.
Prepare vertex data and buffer
In WebGL, all objects are in 3D space. Drawing a line requires two vertices, and each vertex has a 3D coordinate:
let vertices = [ -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ];
There are many types of buffers, and the type of vertex buffer object is gl.ARRAY_BUFFER .
/** * Set buffer * @param {*} gl WebGL context * @param {*} vertexData vertex data */ function setBuffers(gl, vertexData) { // Create a blank buffer object const buffer = gl.createBuffer(); // Bind target gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // WebGL does not support the direct use of JavaScript original array types, which requires conversion const dataFormat = new Float32Array(vertexData); // Initialize data store gl.bufferData(gl.ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW); },
bufferData Method will copy the data to the current binding buffer object. This method provides parameters for managing the given data:
- STATIC_DRAW: the contents of the buffer may be used frequently and will not be changed frequently.
- DYNAMIC_DRAW: the contents of the buffer may be frequently used and changed.
- STREAM_DRAW: the contents of the buffer may not be used often.
The data of the straight line will not change and will remain unchanged every rendering, so the type used here is STATIC_DRAW . Now that the vertex data is stored in the memory of the graphics card, start preparing the vertex shader.
Vertex Shader
Vertex shaders require GLSL ES Written in language, there are two forms of writing at the front end:
- The script tag package is used as a DOM object.
- 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>
Each vertex has a 3D coordinate, and a vec3 type input variable vertexPos is created. Vec3 represents a triple floating-point vector.
main is the entry function, gl_Position is a built-in variable of the shader. A variable in GLSL has up to 4 components, and the last component is used for perspective division. gl_ The value of the position setting becomes the output of the vertex shader. Think back here Basic concepts The state machine mentioned in.
The following 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 shader const shader = gl.createShader(gl.VERTEX_SHADER); // Set vertex shader code gl.shaderSource(shader, source); // compile gl.compileShader(shader); // Judge whether the compilation is successful if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Error compiling shader: " + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; }
In order for WebGL to use this shader, its source code must be compiled dynamically at run time.
- createShader The function creation type is gl.VERTEX_SHADER's shader object;
- compileShader Function.
Next, prepare the clip shader.
Fragment Shader
Fragment shaders are also used GLSL ES Language. What the clip shader does is calculate the final color output of the pixel. Here, it directly simplifies the specified output white. gl_FragColor is A built-in variable that represents color. The four components correspond to R, G, B and A respectively.
/** * Create clip 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 shader const shader = gl.createShader(gl.FRAGMENT_SHADER); // Set clip shader code gl.shaderSource(shader, source); // compile gl.compileShader(shader); // Judge whether the compilation is successful if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Error compiling shader: " + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; },
When both shaders are ready, they need to be linked and merged before they can be used.
Shader program
Shader program objects are versions of shaders that are merged and finally 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 will get a connection error.
- createProgram Function to create an object;
- attachShader Add shader;
- linkProgram Link added shaders.
When the shader needs to be activated, the object is called as a parameter useProgram Function.
/** * Initialize shader program * @param {*} gl WebGL context * @param {*} vertexShader Vertex Shader * @param {*} fragmentShader Fragment Shader */ function initShaderProgram(gl, vertexShader, fragmentShader) { // Create shader object const shaderProgram = gl.createProgram(); // Add shader 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("Unable to initialize shader program: " + gl.getProgramInfoLog(shaderProgram)); return null; } return shaderProgram; }
So far, the input vertex data has been sent to the GPU and indicates how the GPU processes it in vertex and fragment shaders. Finally, it's left to draw.
draw
- vertexAttribPointer Function tells WebGL how to interpret vertex data;
- enableVertexAttribArray Function enables vertex attributes, which are disabled by default;
- The useProgram function activates the shader;
- drawArrays Function to draw. The first parameter is the type of entity to be drawn. It is a straight line, so it is gl.LINE_STRIP .
/** * Initialize shader program * @param {*} gl WebGL context * @param {*} shaderProgram Shader program 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, which are disabled by default. gl.enableVertexAttribArray(vertexPos); // Activate shader gl.useProgram(shaderProgram); // draw gl.drawArrays(gl.LINE_STRIP, 0, 2); }
example
This is Example , the overall logic is as follows:
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 ); // Shader program object draw(glContext, shaderProgram); // draw
This involves a lot of methods and variables. I was really confused at the beginning. I will get used to it after I knock down the code several times.
Next, we will summarize some questions arising during the period, as shown in JavaScript WebGL Basics.