WebGL基础
内容纲要
由于WebGl技术较为成熟,这里不再赘述。
资源初始化
let canvas: HTMLCanvasElement = document.getElementById('webgl') as HTMLCanvasElement;
let gl: WebGLRenderingContext = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL failed to initialize.');
}
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.colorMask(true, true, true, true)
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.cullFace(gl.BACK);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
Vertex Buffer Object顶点缓冲对象
Vertex Buffer Object是包含顶点数据的内存块。
const positions = new Float32Array([
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0,
0.0, 1.0, 0.0
]);
const colors = new Float32Array([
1.0, 0.0, 0.0, // 🔴
0.0, 1.0, 0.0, // 🟢
0.0, 0.0, 1.0 // 🔵
]);
let positionBuffer: WebGLBuffer = null;
let colorBuffer: WebGLBuffer = null;
let createBuffer = (arr) => {
let buf = gl.createBuffer();
let bufType =
arr instanceof Uint16Array || arr instanceof Uint32Array ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
gl.bindBuffer(bufType, buf);
gl.bufferData(bufType, arr, gl.STATIC_DRAW);
return buf;
};
positionBuffer = createBuffer(positions);
colorBuffer = createBuffer(colors);
Index Buffer Object索引缓冲区对象
索引缓冲区对象 (IBO) 用于制作三角形、直线或点的顶点索引列表。在渲染一组三角形时,索引缓冲区允许将给定的顶点重用于不同的三角形。

const indices = new Uint16Array([ 0, 1, 2 ]);
let indexBuffer: WebGLBuffer = null;
indexBuffer = createBuffer(indices);
注意16位Uint16Array比32位Uint32Array快。
Vertex Shader顶点着色器
顶点着色器是一个 GPU 程序,在绘制的每个顶点上执行。
attribute vec3 inPosition;
attribute vec3 inColor;
varying vec3 vColor;
void main()
{
vColor = inColor;
gl_Position = vec4(inPosition, 1.0);
}
创建顶点着色器:
let vertModule: WebGLShader = null;
const vertShaderCode = `
attribute vec3 inPosition;
attribute vec3 inColor;
varying vec3 vColor;
void main()
{
vColor = inColor;
gl_Position = vec4(inPosition, 1.0);
}
`;
let createShader = (source: string, stage) => {
let s = gl.createShader(stage);
gl.shaderSource(s, source);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error('An error occurred compiling the shader: ' + gl.getShaderInfoLog(s));
}
return s;
};
vertModule = createShader(vertShaderCode, gl.VERTEX_SHADER);
Fragment Shader片段着色器

precision mediump float;
varying highp vec3 vColor;
void main()
{
gl_FragColor = vec4(vColor, 1.0);
}
创建片段着色器:
let fragModule: WebGLShader = null;
const fragShaderCode = `
precision mediump float;
varying highp vec3 vColor;
void main()
{
gl_FragColor = vec4(vColor, 1.0);
}
`;
fragModule = createShader(fragShaderCode, gl.FRAGMENT_SHADER);
着色器有很多种,可以查看这里
Shader Program着色器程序
Shader Program将顶点着色器和片段着色器绑定在一起,并将它们设置在 WebGL 状态机中使用。

let program: WebGLProgram = null;
let createProgram = (stages: WebGLShader[]) => {
let p = gl.createProgram();
for (let stage of stages) {
gl.attachShader(p, stage);
}
gl.linkProgram(p);
return p;
};
program = createProgram([vertModule, fragModule]);
Uniforms联合程序

Uniform是连通CPU和GPU的桥梁,需要在着色器中声明:
uniform vec4 uColor;
并执行以下操作将请求的数据发送到着色器:
let color = new Float32Array([0.5, 0.5, 0.5, 1.0]);
let uniformLocation = gl.getUniformLocation(program, 'uColor');
gl.uniform4v(uniformLocation, color);
Textures纹理

function loadTexture(url: string) {
let tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
const pixel = new Uint8Array([ 255, 255, 255, 255 ]);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
let img = new Image();
img.src = url;
img.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
};
return tex;
}
创建后才可访问
uniform sampler2D tAlbedo;
并发送到着色器:
let fragAlbedoUniform: number = -1;
fragAlbedoUniform =
gl.getUniformLocation(program, "tAlbedo");
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tAlbedo);
gl.uniform1i(fragAlbedoUniform, 0);
Rendering渲染

let animationHandler: number = 0;
const render = () => {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.scissor(0, 0, canvas.width, canvas.height);
let setVertexBuffer = (buf: WebGLBuffer, name: string) => {
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
let loc = gl.getAttribLocation(program, name);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 4 * 3, 0);
gl.enableVertexAttribArray(loc);
};
setVertexBuffer(positionBuffer, 'inPosition');
setVertexBuffer(colorBuffer, 'inColor');
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 0);
animationHandler = requestAnimationFrame(render);
};
initializeAPI();
initializeResources();
render();
Destroy销毁
虽然浏览器里的JS有GC机制,但是。。。
function destroyResources()
{
gl.deleteBuffer(positionBuffer);
gl.deleteBuffer(colorBuffer);
gl.deleteBuffer(indexBuffer);
gl.deleteShader(vertModule);
gl.deleteShader(fragModule);
gl.deleteProgram(program);
}
code enjoy! 🐾🐾🐾🐾🐾🐾🐾🐾🐌🐝
作者:indeex
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。