webgl 使用小结

134 阅读5分钟

对webgl 一些简单的使用方法做了示例展示,给出webgl 绘制功能制基本要素。要了解更多关于webgl内容查看详情

<template>
    <canvas id="parkingLot" ref="parkingLot"></canvas>
</template>
<script setup>
    import { ref, onMounted, onUnmounted } from "vue";
    const parkingLot = ref();

    onMounted(async () => {
        const scpt = document.createElement("script");
        scpt.src ="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js";
        document.head.appendChild(scpt);
        scpt.onload = () => {
            var then = 0;
            var squareRotation = 0.0;
            const DOMEl = parkingLot.value;
            console.log(DOMEl,"DOMEl")
            const gl = DOMEl.getContext("webgl")
            // 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
            // const width = DOMEl.clientWidth;
            // const height = DOMEl.clientHeight;

            gl.clearColor(0.0,0.0,1.0,1.0);

            gl.clear(gl.COLOR_BUFFER_BIT);
            
            // 以下的顶点着色器接收一个我们定义的属性(aVertexPosition)的顶点位置值。之后这个值与两个 4x4 的矩阵(uProjectionMatrix 和 uModelMatrix)相乘; 乘积赋值给 gl_Position。
            const vsSource = `
                attribute vec4 aVertexPosition;
                attribute vec4 aVertexColor;

                uniform mat4 uModelViewMatrix;
                uniform mat4 uProjectionMatrix;

                varying lowp vec4 vColor;

                void main(void) {
                gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
                vColor = aVertexColor;
                }
            `;
            // 定义一个白色的片段着色器
            const fsSource = `
                varying lowp vec4 vColor;

                void main(void) {
                gl_FragColor = vColor;
                }
            `;

            //  初始化着色器程序,让 WebGL 知道如何绘制我们的数据
            function initShaderProgram(gl, vsSource, fsSource) {
                const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
                const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

                // 创建着色器程序
                const shaderProgram = gl.createProgram();
                gl.attachShader(shaderProgram, vertexShader);
                gl.attachShader(shaderProgram, fragmentShader);
                gl.linkProgram(shaderProgram);

                // 如果创建失败,alert
                if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                    alert(
                    "Unable to initialize the shader program: " +
                        gl.getProgramInfoLog(shaderProgram),
                    );
                    return null;
                }

                return shaderProgram;
            }

            // 创建指定类型的着色器,上传 source 源码并编译
            function loadShader(gl, type, source) {
                // 调用gl.createShader().创建一个新的着色器。
                const shader = gl.createShader(type);

                // 调用gl.shaderSource().将源代码发送到着色器。
                gl.shaderSource(shader, source);

                // 一旦着色器获取到源代码,就使用gl.compileShader().进行编译。
                gl.compileShader(shader);

                // 为了检查是否成功编译了着色器,将检查着色器参数 gl.COMPILE_STATUS 状态。通过调用gl.getShaderParameter()获得它的值,并指定着色器和我们想要检查的参数的名字(gl.COMPILE_STATUS)。如果返回错误,则着色器无法编译,因此通过gl.getShaderInfoLog()从编译器中获取日志信息并 alert,然后删除着色器返回 null,表明加载着色器失败。
                if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                    alert(
                    "An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader),
                    );
                    gl.deleteShader(shader);
                    return null;
                }
                // 如果着色器被加载并成功编译,则返回编译的着色器。
                return shader;
            }

            const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

            const programInfo = {
                program: shaderProgram,
                attribLocations: {
                    // 获取 shaderProgram 中的 aVertexPosition
                    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
                    vertexColor: gl.getAttribLocation(shaderProgram, "aVertexColor"),
                },
                uniformLocations: {
                    // 获取 shaderProgram 中的对应属性
                    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
                    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
                },
            };

            function initBuffers(gl) {
                // 初始化位息信息
                const positionBuffer = initPositionBuffer(gl);
                const colorBuffer = initColorBuffer(gl);
                // 返回位置信息
                return {
                    position: positionBuffer,
                    color: colorBuffer
                };
            };

            function initColorBuffer(gl) {
                const colors = [
                    1.0,
                    1.0,
                    1.0,
                    1.0, // 白
                    1.0,
                    0.0,
                    0.0,
                    1.0, // 红
                    0.0,
                    1.0,
                    0.0,
                    1.0, // 绿
                    0.0,
                    0.0,
                    1.0,
                    1.0, // 蓝
                ];

                const colorBuffer = gl.createBuffer();
                // 通过这两个步骤,颜色数据就存储在 colorBuffer 缓冲区中,可以被GPU访问,并在后续的绘制操作中使用这些颜色数据渲染图形。
                gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);// gl.bindBuffer 是将一个缓冲区绑定到WebGL的目标(ARRAY_BUFFER)。
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);// gl.bufferData 则是将颜色数据加载到绑定的缓冲区中,这些数据将被发送到GPU,用于着色器中的颜色属性。

                return colorBuffer;
            }

            function initPositionBuffer(gl) {
                // 创建一个 gl buffer
                const positionBuffer = gl.createBuffer();

                // 将 gl 绑定 为 array buffer 信息
                gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

                // 给出一个正方形的位置数据信息
                const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
                
                // 将 gl buffer 设置为 信息
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

                return positionBuffer;
            };

            function setColorAttribute(gl, buffers, programInfo) {
                const numComponents = 4;
                const type = gl.FLOAT;
                const normalize = false;
                const stride = 0;
                const offset = 0;
                gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
                gl.vertexAttribPointer(
                    programInfo.attribLocations.vertexColor,
                    numComponents,
                    type,
                    normalize,
                    stride,
                    offset,
                );
                gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
            }

            function drawScene(gl, programInfo, buffers, deltaTime) {
                gl.clearColor(0.0, 0.0, 0.0, 1.0); // 将场影颜色初始化为 黑色
                gl.clearDepth(1.0); // 清除深度信息
                gl.enable(gl.DEPTH_TEST); // 启用深度测试
                gl.depthFunc(gl.LEQUAL); // Near things obscure far things

                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

                
                const fieldOfView = (45 * Math.PI) / 180; // 观查角度
                const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;// 宽度比
                const zNear = 0.1; // 近截剪距离
                const zFar = 100.0; // 远截剪距离
                const projectionMatrix = mat4.create(); // 创建矩阵

                // 设置矩阵信息
                mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);

                // 创建矩阵
                const modelViewMatrix = mat4.create();

                // 设置移动位置
                mat4.translate(
                    modelViewMatrix,
                    modelViewMatrix,
                    [-0.0, 0.0, -6.0],
                );

                mat4.rotate(
                    modelViewMatrix, // destination matrix
                    modelViewMatrix, // matrix to rotate
                    squareRotation, // amount to rotate in radians
                    [0, 0, 1],
                ); // axis to rotate around


                {
                    const numComponents = 2; // 每次从缓冲区读取的值数量,这里设置为2,表示每次读取2个数值。这个常用于2D坐标,比如一个顶点的(x, y)位置。
                    const type = gl.FLOAT; // 指定缓冲区数据的类型,这里是32位浮点数。顶点位置的每个组件都是浮点数。
                    const normalize = false; // 是否将数据归一化,设置为 false 表示不归一化。如果数据本身已经是正确的值(如坐标),则不需要归一化。
                    const stride = 0; // 步幅,表示从一个顶点到下一个顶点需要跳过多少字节。 这里设置为 0 表示顶点数据是紧密排列的,一个接着一个。
                    const offset = 0; // 缓冲区中从哪个字节开始读取数据。这里从头开始读取,所以是0。
                    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); // 将包含顶点位置数据的缓冲区绑定到 ARRAY_BUFFER。 绑定了 buffers.position 缓冲区,WebGL会接下来使用这个缓冲区。
                    // 告诉WebGL如何从绑定的缓冲区中获取数据。
                    gl.vertexAttribPointer(
                        programInfo.attribLocations.vertexPosition,
                        numComponents,
                        type,
                        normalize,
                        stride,
                        offset,
                    );
                    // 启用顶点属性数组。 WebGL默认禁用顶点属性,需要显式启用这个属性数组,才能进行渲染。
                    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
                }

                setColorAttribute(gl, buffers, programInfo);
                
                // 告诉WebGL使用 programInfo.program 着色器程序进行绘制。  programInfo.program 是编译并链接好的着色器程序,WebGL在绘制时会使用这个程序。这个程序中包含顶点着色器和片段着色器。
                gl.useProgram(programInfo.program);

                // 将矩阵数据传递给着色器中的uniform变量。这里的 uniformMatrix4fv 用来传递4x4的浮点数矩阵,主要用于在顶点着色器中进行坐标变换。
                gl.uniformMatrix4fv(
                    // programInfo.uniformLocations.projectionMatrix,这是着色器中投影矩阵的uniform变量的位置。
                    programInfo.uniformLocations.projectionMatrix,
                    // false 表示矩阵不需要转置。
                    false,
                    // projectionMatrix 是传递给着色器的实际矩阵数据。
                    projectionMatrix,
                );
                // 第二次调用传递的是 modelViewMatrix,这是模型视图矩阵。总的来说,这两个矩阵会用来进行顶点的变换,从局部坐标转换到屏幕坐标。
                gl.uniformMatrix4fv(
                    programInfo.uniformLocations.modelViewMatrix,
                    false,
                    modelViewMatrix,
                );

                {
                    // 绘制时从第一个顶点开始。
                    const offset = 0;
                    // 绘制四个顶点。
                    const vertexCount = 4;
                    // 通过绘制方法 gl.drawArrays 来实际渲染图形 这里的 gl.TRIANGLE_STRIP 指定了要绘制的图元类型,TRIANGLE_STRIP 是一种可以用最少的顶点创建多个三角形的方法:
                    gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
                };
                squareRotation += deltaTime;
            };
            // 初始化buffer数据
            const buffers = initBuffers(gl);

            // Draw the scene repeatedly
            function render(now) {
                now *= 0.001; // convert to seconds
                const deltaTime = now - then;
                then = now;

                drawScene(gl, programInfo, buffers, deltaTime);

                requestAnimationFrame(render);
            }
            requestAnimationFrame(render);
        }
    });
</script>
<style lang="scss" scoped="scoped">

    #parkingLot {
        width: 940px;
        height: 940px;
        border: 1px solid #ccc;
        margin: 30px auto;
    }

</style>