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) 用于制作三角形、直线或点的顶点索引列表。在渲染一组三角形时,索引缓冲区允许将给定的顶点重用于不同的三角形。

Webgpu基础

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片段着色器

Webgpu基础

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 状态机中使用。

Webgpu基础

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联合程序

Webgpu基础

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纹理

Webgpu基础

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渲染

Webgpu基础

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

链接:https://indeex.club

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


发表评论

您的电子邮箱地址不会被公开。