我们先来看一下,最终实现的效果。。。。
效果看起来真的很简单。一句css的事,还写一篇文章???
别急着走,往下看,有干货 !!!
接下来,进入正题,一步步分解在WebGL世界如何绘制一个矩形。
这里不过多介绍,主要是使用canvas.getContext API,获取webgl上下文。
const createContext = (id: string, width: number, height: number) => {
let element: HTMLElement = document.getElementById("id");
if (element && element.tagName !== "canvas") {
console.error(`id ${id} element should be a canvas element`);
return;
}
let canvas: HTMLCanvasElement;
if (!element) {
canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
document.body.appendChild(canvas);
} else {
canvas = element as HTMLCanvasElement;
}
const gl = canvas.getContext("webgl");
if (!gl) {
console.error("webgl is not supported");
return;
}
return gl;
};
着色器,是一种运行在gpu上的小程序,负责图形渲染管线的特定环节。着色器本质上是一种输入输出程序。
创建着色器之前,先分别为顶点着色器和片元着色器编写一段最简单的GLSL代码(类C语言)。
const vertextSource = `
attribute mediump vec2 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition, 0, 1.0);
}
`;
首先,使用关键字attribute声明一个类型为vec2(包含2个float分量的向量类型)的顶点变量aVertexPosition。attribute关键字只能出现在顶点着色器,javascript代码可以通过相关的WebGL API把顶点的数据传递给着色器中attribute变量。
然后,把aVertexPosition转换成vec4类型,赋值给与着色器预置变量gl_Position,指定输出。
const fragmentSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
`;
首先,使用关键字uniform声明一个类型为vec4(包含4个float分量的向量类型)的普通变量u_color,这个变量的用处是:接受JavaScript通过webgl api传递过来的值,这里表示一个随机颜色值。
有了着色器代码之后,就可以使用webgl api创建着色器了。
const createShader = (
gl: WebGLRenderingContext,
type: number,
source: string
) => {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(
"a error occured compiling shader",
gl.getShaderInfoLog(shader)
);
return;
}
return shader;
};
有了着色器程序之后,需要通过program把它们组合起来。这里比较简单,不过多赘述。
const createProgram = (gl: WebGLRenderingContext, shaders: WebGLShader[]) => {
const program = gl.createProgram();
shaders.forEach((shader) => gl.attachShader(program, shader));
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(
"a error occured linking program ",
gl.getProgramInfoLog(program)
);
return;
}
gl.useProgram(program);
return program;
};
};
着色器程序准备好之后,我们需要通过JavaScript代码给着色器中的变量设置值。
首先,给顶点着色器的顶点属性aVertexPosition赋值,过程遵循如下规则:
/* ***** 顶点属性设置 ***** */
// 绑定缓冲对象到gl.ARRAY_BUFFER目标点
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 给缓冲对象填充数据,矩形的四个顶点坐标
const vertexData = [
-1.0,
-1.0,
1.0,
-1.0,
-1.0,
1.0,
1.0,
1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
// 获取顶点属性的在着色器中的索引,并激活它
const aVertexPositionLocation = gl.getAttribLocation(
program,
"aVertexPosition"
);
gl.enableVertexAttribArray(aVertexPositionLocation);
// 设置顶点属性如何从顶点缓冲对象中取值。每次从数组缓冲对象中读取2个值
gl.vertexAttribPointer(aVertexPositionLocation, 2, gl.FLOAT, false, 0, 0);
接下来,就是片元着色器的u_color变量赋值
/* ***** 颜色设置 ***** */
const colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4f(colorLocation, Math.random(), Math.random(), Math.random(), 1.0);
最终的绘制,只需要调用api WebGLRenderingContext.drawArrays。
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
完整代码,见这里。