WebGL 2工作原理

06-01 1078阅读

WebGL在GPU上的工作基本上分为两部分

  • 第一部分是将顶点(或数据流)转换到裁剪空间坐标
    • 就是将传入的位置坐标,转换为0-1的值,并绘制出来
    • 每个顶点的坐标(传入的值)通过顶点着色器计算转换为裁剪空间坐标
    • 转换后的值设置为一个特殊的gl_Position变量
    • 这个变量就是该顶点转换到裁剪空间中的坐标值

      假设你正在画三角形,顶点着色器每完成三次顶点处理,WebGL就会用这三个顶点画一个三角形 

      • 第二部分是基于第一部分的结果绘制像素点(填充颜色)
        •  计算出这三个顶点对应的像素后,就会光栅化这个三角形,
        • “光栅化”其实就是“用像素画出来” 的花哨叫法。

          对于每一个像素,它会调用你的片段着色器询问你使用什么颜色。

          你通过给片段着色器的一个特殊变量gl_FragColor设置一个颜色值,实现自定义像素颜色。


          处理每个像素时片段着色器可用信息很少,幸运的是我们可以给它传递更多信息

          想要从顶点着色器传值到片段着色器,我们可以定义“可变量(varyings)”。

          绘制一个三角形

          (在此代码仅作演示用,别想着直接复制就运行)

          js代码

          // 定义一个三角形坐标填充到缓冲里
          function setGeometry(gl) {
            gl.bufferData(
                gl.ARRAY_BUFFER,
                new Float32Array([
                       0, -100,
                     150,  125,
                    -175,  100]),
                gl.STATIC_DRAW);
          }
          // 绘制场景
          function drawScene() {
            ...
            // 绘制几何体
            var primitiveType = gl.TRIANGLES;
            var offset = 0;
            var count = 3; //说明要画几个点
            gl.drawArrays(primitiveType, offset, count);
          }

          GLSL代码

          在顶点着色器(vertex-shader)中定义一个varying(可变量)用来给片段着色器传值。

          varying vec4 v_color; //传递值的变量
          ...
          void main() {
            // 将位置和矩阵相乘
            gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
           
            // 从裁减空间转换到颜色空间
            // 裁减空间范围 -1.0 到 +1.0
            // 颜色空间范围 0.0 到 1.0
            v_color = gl_Position * 0.5 + 0.5;
          }

          在片段着色器中定义同名varying变量。

          precision mediump float;
           
          varying vec4 v_color; //传递值的变量
           
          void main() {
            gl_FragColor = v_color;
          }

          WebGL会将同名的可变量从顶点着色器输入到片段着色器中。颜色随位置变化。

          在这里可以这样理解,两个同名的变量指向了同一个内存地址,所以其值是共用的(自认为)

          在此两个着色器的运行逻辑:

          • 计算了三个顶点,调用了三次顶点着色器,所以也只计算出了三个颜色值
          • (必须先顶点再片段着色器吗)(并行运行的吗?不应该三角形只填充一种颜色吗)
            • WebGL先获得顶点着色器中计算的三个颜色值,在光栅化三角形时将会根据这三个值进行插值。 每一个像素在调用片段着色器时,可变量的值是与之对应的插值。

              运行之后的效果

              WebGL 2工作原理

              解析

              开始详细分析:

              首先这是我们输入的三个顶点值

              WebGL 2工作原理

               运行三次顶点着色器程序,转换后的三个裁剪空间坐标值

              WebGL 2工作原理

              通过这行代码 v_color = gl_Position * 0.5 + 0.5; 得到了三个颜色值。

              WebGL 2工作原理

              不想写了先歇会(2025.4.22)

              绘制两个三角形

              js

                // 寻找顶点着色器中需要的数据
                var positionLocation = gl.getAttribLocation(program, "a_position");
                var colorLocation = gl.getAttribLocation(program, "a_color");
                ...
                // 给颜色数据创建一个缓冲
                var colorBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
                // 设置颜色
                setColors(gl);
                ...
               
              // 给矩形的两个三角形
              // 设置颜色值并发到缓冲
              function setColors(gl) {
                // 生成两个随机颜色
                var r1 = Math.random();
                var b1 = Math.random();
                var g1 = Math.random();
               
                var r2 = Math.random();
                var b2 = Math.random();
                var g2 = Math.random();
               
                gl.bufferData(
                    gl.ARRAY_BUFFER,
                    new Float32Array(
                      [ r1, b1, g1, 1,
                        r1, b1, g1, 1,
                        r1, b1, g1, 1,
                        r2, b2, g2, 1,
                        r2, b2, g2, 1,
                        r2, b2, g2, 1]),
                    gl.STATIC_DRAW);
              }
              

              js(不同的逻辑部分)

              //*接下来渲染时,请从我绑定的缓冲区中读取颜色数据,并传递给着色器中的 aColor 属性。*
              gl.enableVertexAttribArray(colorLocation);
               
              // 绑定颜色缓冲
              gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
               
              // 告诉颜色属性怎么从 colorBuffer (ARRAY_BUFFER) 中读取颜色值
              var size = 4;          // 每次迭代使用4个单位的数据
              var type = gl.FLOAT;   // 单位数据类型是32位的浮点型
              var normalize = false; // 不需要归一化数据
              var stride = 0;        // 0 = 移动距离 * 单位距离长度sizeof(type) 
                                     // 每次迭代跳多少距离到下一个数据
              var offset = 0;        // 从绑定缓冲的起始处开始
              gl.vertexAttribPointer(
                  colorLocation, size, type, normalize, stride, offset)

              其中的关键是 gl.enableVertexAttribArray(colorLocation);将缓冲区中的值赋值给colorLocation

              在这里会有一个疑问,为什么 gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);  调用了两次


              叮!快来看看我和文心一言的奇妙对话~点击链接 https://yiyan.baidu.com/share/vUIFGNyXpM 

              第一次调用

              • 创建一个新的缓冲区对象 colorBuffer,用于存储顶点的颜色数据。
              • 通过 gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) 将 colorBuffer 绑定到 WebGL 的 ARRAY_BUFFER 目标。
              • 这一步告诉 WebGL:“接下来所有对 ARRAY_BUFFER 的操作(如 gl.bufferData)都会作用到 colorBuffer 上。”

                第二次调用

                • 在渲染每一帧时,需要重新绑定 colorBuffer 到 ARRAY_BUFFER,以便着色器能够从该缓冲区读取颜色数据。
                • 这一步是动态渲染的关键:每次调用 drawScene 时,WebGL 需要知道从哪里获取顶点的颜色数据。

                  为什么需要重新绑定?

                  • WebGL 的状态机特性要求每次渲染前显式设置所有必要状态(如绑定的缓冲区、启用的属性等)。
                  • 即使 colorBuffer 之前绑定过,其他操作(如绑定 positionBuffer)可能会覆盖当前绑定状态,因此需要重新绑定以确保正确性。
                    // 画几何体
                    var primitiveType = gl.TRIANGLES;
                    var offset = 0;
                    var count = 6;
                    gl.drawArrays(primitiveType, offset, count);

                    GLSL代码

                    顶点着色器

                    attribute vec2 a_position;
                    attribute vec4 a_color;
                    uniform mat3 u_matrix;
                    varying vec4 v_color;
                    void main() {
                      // Multiply the position by the matrix.
                      gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
                      // Copy the color from the attribute to the varying.
                      v_color = a_color;
                    }

                     片段着色器

                    precision mediump float;
                    varying vec4 v_color;
                    void main() {
                      gl_FragColor = v_color;
                    }

                    WebGL 2工作原理

                    buffer和attribute的代码是干什么的?

                            缓冲操作是在GPU上获取顶点和其他顶点数据的一种方式。 gl.createBuffer创建一个缓冲;gl.bindBuffer是设置缓冲为当前使用缓冲; gl.bufferData将数据拷贝到缓冲,这个操作一般在初始化完成。

                            一旦数据存到缓冲中,还需要告诉WebGL怎么从缓冲中提取数据传给顶点着色器的属性。

                     要做这些,首先需要获取WebGL给属性分配的地址,如下方代码所示

                    // 询问顶点数据应该放在哪里
                    var positionLocation = gl.getAttribLocation(program, "a_position");
                    var colorLocation = gl.getAttribLocation(program, "a_color");

                    这一步一般也是在初始化部分完成。

                    一旦知道了属性的地址,在绘制前还需要发出三个命令。

                    //这个命令是告诉WebGL我们想从缓冲中提供数据。
                    gl.enableVertexAttribArray(location);
                    //这个命令是将缓冲绑定到 ARRAY_BUFFER 绑定点,它是WebGL内部的一个全局变量。
                    gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer);
                    gl.vertexAttribPointer(
                        location,
                        numComponents,
                        typeOfData,
                        normalizeFlag,
                        strideToNextPieceOfData,
                        offsetIntoBuffer);
                    /`这个命令告诉WebGL从 ARRAY_BUFFER 绑定点当前绑定的缓冲获取数据。 
                    每个顶点有几个单位的数据(1 - 4),单位数据类型是什么(BYTE, FLOAT, INT, UNSIGNED_SHORT, 等等...), stride 是从一个数据到下一个数据要跳过多少位,最后是数据在缓冲的什么位置。
                    单位个数永远是 1 到 4 之间。
                    如果每个类型的数据都用一个缓冲存储,stride 和 offset 都是 0 。 
                    对 stride 来说 0 表示 “用符合单位类型和单位个数的大小”。 
                    对 offset 来说 0 表示从缓冲起始位置开始读取。 它们使用 0 以外的值时会复杂得多,虽然这样会取得一些性能能上的优势, 但是一般情况下并不值得,除非你想充分压榨WebGL的性能。
                    希望这些关于缓冲和属性的内容对你来说讲的足够清楚。`/

                    (此文章就渲染原理进行了简单的讲述,实际的实现代码请移步原文章)

                    参考:

                    WebGL 工作原理

                    WEBGL原理_webgl渲染原理-CSDN博客

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码