Introduction
Then WebGL basic concept , do a simple example of drawing a straight line.
Mainly refer to the following two articles:
Draw a line
The following will not give a detailed explanation of each function used. I personally prefer to have a feeling for the overall logic first, and then check the information as needed when actually using it.
Create a WebGL context
In based on the concept have mentioned is the use of WebGL 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("浏览器不支持 WebGL");
return;
}
Then prepare the vertex data.
Prepare vertex data and buffer
All objects in WebGL 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. The type of vertex buffer object is gl.ARRAY_BUFFER
/**
* 设置缓冲
* @param {*} gl WebGL 上下文
* @param {*} vertexData 顶点数据
*/
function setBuffers(gl, vertexData) {
// 创建空白的缓冲对象
const buffer = gl.createBuffer();
// 绑定目标
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// WebGL 不支持直接使用 JavaScript 原始数组类型,需要转换
const dataFormat = new Float32Array(vertexData);
// 初始化数据存储
gl.bufferData(gl.ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW);
},
bufferData method will copy the data to the currently bound 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 used frequently and change frequently.
- STREAM_DRAW: The contents of the buffer may not be used frequently.
The data of the straight line will not change, and will remain the same every time you render, so the type used here is STATIC_DRAW
. Now that the vertex data has been stored in the memory of the graphics card, start preparing the vertex shader.
Vertex shader
The vertex shader needs to be written in the GLSL ES language, and there are two forms of writing on the front end:
- The script tag wraps, when used, it is like getting 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, creating a vec3
type input variable vertexPos
, vec3
represents a triple floating point vector.
main
is the entry function, gl_Position
is the 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_Position
will become the output of the vertex shader. Here, please recall the state machine mentioned in basic concept
The following is the pure character form:
/**
* 创建顶点着色器
* @param {*} gl WebGL 上下文
*/
function createVertexShader(gl) {
// 顶点着色器 glsl 代码
const source = `
attribute vec3 vertexPos;
void main(void){
gl_Position = vec4(vertexPos, 1);
}
`;
// 创建着色器
const shader = gl.createShader(gl.VERTEX_SHADER);
// 设置顶点着色器代码
gl.shaderSource(shader, source);
// 编译
gl.compileShader(shader);
// 判断是否编译成功
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("编译着色器报错: " + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
In order for WebGL to use this shader, its source code must be dynamically compiled at runtime.
- function creates a shader object of
gl.VERTEX_SHADER
- compileShader function to compile.
Then prepare the fragment shader.
Fragment shader
The fragment shader is also written in GLSL ES language. What the fragment shader does is to calculate the final color output of the pixel, which is directly simplified to specify the output white. gl_FragColor
is a built-in variable that represents color. The 4 components correspond to R, G, B, and A respectively.
/**
* 创建片段着色器
* @param {*} gl WebGL 上下文
*/
function createFragmentShader(gl) {
// 片段着色器 glsl 代码
const source = `
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
// 创建着色器
const shader = gl.createShader(gl.FRAGMENT_SHADER);
// 设置片段着色器代码
gl.shaderSource(shader, source);
// 编译
gl.compileShader(shader);
// 判断是否编译成功
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("编译着色器报错: " + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
},
After the two shaders are ready, they need to be linked and merged before they can be used.
Shader program
The shader program object is the final linked version after multiple shaders are merged. When linking shaders to a program, it will link 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 shader.
When the shader needs to be activated, call the useProgram function with the object as a parameter.
/**
* 初始化着色器程序
* @param {*} gl WebGL 上下文
* @param {*} vertexShader 顶点着色器
* @param {*} fragmentShader 片段着色器
*/
function initShaderProgram(gl, vertexShader, fragmentShader) {
// 创建着色器对象
const shaderProgram = gl.createProgram();
// 添加着色器
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
// 多个着色器合并链接
gl.linkProgram(shaderProgram);
// 创建是否成功检查
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("无法初始化着色器程序: " + gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
So far, the input vertex data has been sent to the GPU and instructed the GPU how to process it in the vertex and fragment shaders. In the end, it is left to draw.
draw
- vertexAttribPointer function tells WebGL how to interpret the vertex data;
- enableVertexAttribArray The function enables vertex attributes, which are disabled by default;
useProgram
function activates the shader;drawArrays function to draw, the first parameter is the type of primitive to be drawn, the drawing is a straight line, so it is
gl.LINE_STRIP
./** * 初始化着色器程序 * @param {*} gl WebGL 上下文 * @param {*} shaderProgram 着色器程序对象 */ function draw(gl, shaderProgram) { // 获取对应数据索引 const vertexPos = gl.getAttribLocation(shaderProgram, "vertexPos"); // 解析顶点数据 gl.vertexAttribPointer(vertexPos, 3, gl.FLOAT, false, 0, 0); // 启用顶点属性,顶点属性默认是禁用的。 gl.enableVertexAttribArray(vertexPos); // 激活着色器 gl.useProgram(shaderProgram); // 绘制 gl.drawArrays(gl.LINE_STRIP, 0, 2); }
example
This is example , the overall logic of 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]; // 顶点数据
setBuffers(glContext, vertices); // 缓冲数据
const vertexShader = createVertexShader(glContext); // 顶点着色器
const fragmentShader = createFragmentShader(glContext); // 片段着色器
const shaderProgram = initShaderProgram(
glContext,
vertexShader,
fragmentShader
); // 着色器程序对象
draw(glContext, shaderProgram); // 绘制
There are a lot of methods and variables involved. At the beginning, I was really confused. After reading the code several times, I will get used to it.
Next, I will summarize some of the questions that occurred during the period, see JavaScript WebGL basic doubts .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。