WebGL 入门篇

290 阅读3分钟

webgl 入门

webgl 是什么?

wegbl 工作原理

3D 模型数据从诞生到最终显示在屏幕上,这期间经历了什么样的过程呢? 大家可以想象一下流水线的生产过程,流水线按照既定的步骤对原料进行加工,当前步骤只对前一步骤的结果进行处理,然后将处理后的结果传递给下一步骤,最终将原材料生产成完整的产品。WebGL 的工作方式和流水线类似,也是按照流水线的方式将 3D 模型数据渲染到 2D 屏幕上的,业界把这种渲染方式称为图形管线或者渲染管线,大家以后碰到这两个名词,应该能明白什么意思了,我们知道,WebGL 只能够绘制点、线段、三角形这三种基本图元,但是我们经常看到 WebGL 程序中含有立方体、球体、圆柱体等规则形体,甚至很多更复杂更逼真的不规则模型, 那么 WebGL 是如何绘制它们的呢? 其实这些模型本质上是由一个一个的点组成,GPU 将这些点用三角形图元绘制成一个个的微小平面,这些平面之间互相连接,从而组成各种各样的立体模型。因此,我们的首要任务是创建组成这些模型的顶点数据。 一般情况下,最初的顶点坐标是相对于模型中心的,不能直接传递到着色器中,我们需要对顶点坐标按照一系列步骤执行模型转换,视图转换,投影转换,转换之后的坐标才是 WebGL 可接受的坐标,即裁剪空间坐标。我们把最终的变换矩阵和原始顶点坐标传递给 GPU,GPU 的渲染管线对它们执行流水线作业。

  • GPU 渲染管线的主要处理过程如下:
  1. 首先进入顶点着色器阶段,利用 GPU 的并行计算优势对顶点逐个进行坐标变换。
  2. 然后进入图元装配阶段,将顶点按照图元类型组装成图形。
  3. 接下来来到光栅化阶段,光栅化阶段将图形用不包含颜色信息的像素填充。
  4. 在之后进入片元着色器阶段,该阶段为像素着色,并最终显示在屏幕上。

此外我们需要知道 GLSL 是什么? GLSL 是运行在GPU里面的程序语言,又称着色器语言。而运行在 GPU 里面的程序被称为着色器(Shader)

顶点着色器
attribute vec4 position;
    void main() {
    gl_Position = position;
}

片元着色器
void main(void) {
    gl_FragColor = vec4(1.0, 0.5, 1.0, 1.0);
}

gsls.jpg GLSL 是连接浏览器和 GPU 的桥梁,工程师通过编写 着色器(Shader) 来渲染出自己想要的效果。而复杂的图形都是有点,线,面这三种基本图元组成的。如下图茶壶所示: screenshot-20220620-204644.png

demo 源码分析

先看图,彩色三角形是有顶点着色器确定绘制位置,而片元着色器进行片元着色,最后显示在屏幕上。

screenshot-20220620-205227.png

    * @Descripttion:
    * @version:
    * @Author: Wang Ming
    * @Date: 2022-01-23 14:00:39
    * @LastEditors: Wang Ming
    * @LastEditTime: 2022-02-17 19:12:05
    -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script id="shader-vs" type="shader-source">
        //设置浮点数精度为中等精度。
        precision mediump float;
        attribute vec3 a_position;
        attribute vec3 a_color;
        varying vec3 v_color;
        void main(void){
            gl_Position  = vec4(a_position,1.0);
            v_color = a_color;
        }
        </script>
        <script id="shader-fs" type="shader-source">
        //设置浮点数精度为中等精度。
        precision mediump float;
        varying vec3 v_color;
        void main(void){
            gl_FragColor = vec4(v_color/vec3(255,255,255),1.0);
        }
        </script>
        <script>
        var webgl = null;
        var vertexshaderObj = null;
        var fragmentShaderObj = null;
        var programObject = null;
        var triangleBuffer = null;
        var a_positionInxex = 0;
        var a_colorInxex = 1;

        window.onload = () => {
            function getShaderSource(scriptID) {
            var shaderScript = document.getElementById(scriptID);
            if (shaderScript == null) return "";
            var sourceCode = "";
            var child = shaderScript.firstChild;
            while (child) {
                if (child.nodeType == child.TEXT_NODE)
                sourceCode += child.textContent;
                child = child.nextSibling;
            }
            return sourceCode;
            }
            let canvas = document.getElementById("canvas");
            webgl = canvas.getContext("webgl");
            webgl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);
            //创建着色器
            vertexshaderObj = webgl.createShader(webgl.VERTEX_SHADER);
            fragmentShaderObj = webgl.createShader(webgl.FRAGMENT_SHADER);
            console.log(fragmentShaderObj);
            //往着色器添加GLSL
            webgl.shaderSource(vertexshaderObj, getShaderSource("shader-vs"));
            webgl.shaderSource(fragmentShaderObj, getShaderSource("shader-fs"));
            //编译着色器
            webgl.compileShader(vertexshaderObj);
            webgl.compileShader(fragmentShaderObj);

            判断顶点着色器是否存在,否则返回。
            if (!webgl.getShaderParameter(vertexshaderObj, webgl.COMPILE_STATUS)) {
            console.log(webgl.getShaderInfoLog(vertexshaderObj));
            return;
            }
            判断片元着色器是否存在,否则返回。
            if (
            !webgl.getShaderParameter(fragmentShaderObj, webgl.COMPILE_STATUS)
            ) {
            alert("ERROR:fragmentShaderObj");
            return;
            }

            //创建绘制程序
            programObject = webgl.createProgram();
            //添加顶点着色器
            webgl.attachShader(programObject, vertexshaderObj);
            //添加片元着色器
            webgl.attachShader(programObject, fragmentShaderObj);




            GPU连接绘制程序
            webgl.linkProgram(programObject);
            判断GPU是否连接
            if (!webgl.getProgramParameter(programObject, webgl.LINK_STATUS)) {
            alert("ERROR:PROGRAMOBJ");
            return;
            }
            console.log(programObject);

            GPU使用绘制程序
            webgl.useProgram(programObject);

            {
            //创建顶点缓冲区
            let a_position = webgl.getAttribLocation(programObject, "a_position");
            var jsArrayData = [0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0];
            triangleBuffer = webgl.createBuffer(webgl, a_position, {
                size: 3,
            });

            绑定缓冲区
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
            往缓冲区写入数据
            webgl.bufferData(
                webgl.ARRAY_BUFFER,
                new Float32Array(jsArrayData),
                webgl.STATIC_DRAW
            );
            开启属性数组列表中指定索引处的通用顶点属性数组。
            webgl.enableVertexAttribArray(a_positionInxex);
            告诉显卡从当前绑定的缓冲区(bindBuffer() 指定的缓冲区)中读取顶点数据。
            webgl.vertexAttribPointer(
                a_positionInxex,
                3,
                webgl.FLOAT,
                false,
                0,
                0
            );
            }
            {
            let a_Color = webgl.getAttribLocation(programObject, "a_color");
            let colorData = [255, 99, 71, 77, 0, 71, 77, 254, 71];
            var colorBuffer = webgl.createBuffer(webgl, a_Color, {
                size: 3,
            });
            webgl.bindBuffer(webgl.ARRAY_BUFFER, colorBuffer);
            webgl.bufferData(
                webgl.ARRAY_BUFFER,
                new Float32Array(colorData),
                webgl.STATIC_DRAW
            );
            webgl.enableVertexAttribArray(a_colorInxex);
            webgl.vertexAttribPointer(a_colorInxex, 3, webgl.FLOAT, false, 0, 0);
            }
            清屏
            webgl.clearColor(0.0, 0.0, 0.1, 1.0);
            webgl.clear(webgl.COLOR_BUFFER_BIT);
            绘制
            webgl.drawArrays(webgl.TRIANGLES, 0, jsArrayData.length);
            console.log(webgl);
        };
        </script>
        <canvas id="canvas" width="600" height="450"></canvas>
    </body>
    </html>