【笔记】《WebGL 编程指南》第 2 章 WebGL 入门

发布时间:2024年01月18日
  • 第一个 WebGL 程序

    • 【P42】 默认情况下,<canvas>是透明的

    • 【P44】 它不直接提供绘图方法,而是提供一种叫上下文(context)的机制来进行绘图。

    • 【P45】 计算机系统通常使用红、绿、蓝这三原色组合来表示颜色,这种颜色表示方式被称为RGB格式,当a (透明度)加进来之后,就成为RGBA格式。

    • <!DOCTYPE html>
      <html>
        <head>
          <script src="index.js"></script>
        </head>
        <body onload="main()">
          <canvas id="canvas" width="400" height="400">
            你的浏览器不支持 canvas。
          </canvas>
        </body>
      </html>
      
      
      function main() {
        var canvas = document.getElementById('canvas');
        // 错误检查
        if (!canvas) {
          alert('无法获取 <canvas> 标签。');
          return;
        }
      
        var ctx = canvas.getContext('2d'); // 参数指定 2D/3D
        ctx.fillRect(120, 10, 150, 150); // 参数 x y width height
      }
      
  • 清空绘图区

    • 本节代码
      var gl = getWebGLContext(canvas);
      if (!gl) {
        alert('无法获取初始化 WebGL。');
        return;
      }
      
      gl.clearColor(0.5, 0.5, 0.5, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
      
    • 书中引用的一些库:
      <script src="https://rodger.global-linguist.com/webgl/lib/webgl-utils.js"></script>
      <script src="https://rodger.global-linguist.com/webgl/lib/webgl-debug.js"></script>
      <script src="https://rodger.global-linguist.com/webgl/lib/cuon-utils.js"></script>
      
    • 指定背景色
      • gl.clearColor(r, g, b, a)
        // 三个参数的值都是 [0.0, 1.0]
        
      • 【P50】 一旦指定了背景色之后,背景色就会驻存在WebGL系统(WebGL System)中,在下一次调用gl?clearcols()方法前不会改变。
    • 清空 <canvas>
      • gl.clear(gl.COLOR_BUFFER_BIT);
        
      • 【P51】 清空绘图区域,实际上是在清空颜色缓冲区(color buffer),传递参数gi-color_buffer_bit就是在告诉WebGL清空颜色缓冲区。
  • 绘制一个点 1(着色器介绍)

    • JS 代码
    • 不像之前用 canvas 绘制 2D 矩形那样简单,用 WebGL 绘制一个点必须要用着色器
    • WebGL 需要的两种着色器
      • 【P55】 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点(vertex)是指二维或三维空间中的一个点,比如二维或三维图形的端点或交点。绘制一个点(版本1) 25
      • 【P56】 片元着色器(Fragment shader):进行逐片元处理过程如光照(见第8章“光照”)的程序。片元(fragment)是一个WebGL术语,你可以将其理解为像素(图像的单元)。
      • 658d2d34-f15a-4de4-85b5-d92f22872e68
      • TODO 两者的区别?
    • 【P61】 WebGL程序包括运行在浏览器中的JavaScript和运行在WebGL系统的着色器程序这两个部分。
    • 【P58】 着色器使用类似于c的OpenGL ES着色器语言(GLSLES)来编写。
    • 使用着色器的基本框架
      gl = getWebGLContext(); // 获取 WebGL 上下文
      initShaders(); // 初始化着色器
      gl.clearColor(0, 0, 0, 0) // 设置背景色
      // 开始绘图
      
    • 【P61】 顶点着色器先执行,它对gl_Position变量和gl_PointSize变量进行赋值,并将它们传入片元着色器,然后片元着色器再执行。
    • 分析着色器

      • 顶点着色器:
        // `main()` 函数不能有参数,返回值必须为 `void`
        void main() {
        	// `gl_Position`:内置变量,顶点位置,类型 `vec4`,必选。
        	// vec4() 函数用于构造一个 vec4 实例。
        	gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        
            // `gl_PointSize`:内置变量,顶点大小,类型 `float`,可选。
            gl_PointSize = 10.0;
        }
        
        • (PS:OpenGL ES 语言的 Markdown 代码块缩写为 glsl,但不一定支持。可以用 c 作为代替。 )
      • 注意:OpenGL ES 中整数不会自动转换为浮点数,gl_PointSize = 10 会报错!
      • vec4 类型 / 齐次坐标
        • 【P62】 vec4 表示由四个浮点数组成的矢量
        • 【P63】 由4个分量组成的矢量被称为齐次坐标
        • 【P63】 齐次坐标(x, y, z, w)等价于三维坐标(x∕w, y∕w, z/w)。所以如果齐次坐标的第4个分量是1,你就可以将它当做三维坐标来使用。
      • 片元着色器:
        void main() {
        	// `gl_FragColor`:内置变量,顶点颜色,类型 `vec4`。
        	gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
        
    • 绘制操作

      • 绘制操作使用 gl.drawArrays() 进行。
        658d3668-96da-4315-b87c-8453bfabf077
      • gl.drawArrays(gl.POINTS, 0, 1);
        
    • WebGL 坐标系统

      • WebGL 使用右手坐标系。X 轴从左到右,Y 轴从下到上,Z 轴从你到屏幕。
      • 【P65】 观察者的眼睛位于原点(0Q 0.0, 0.0)处,视线则是沿着Z轴的负方向,从你指向屏幕
      • 658d38ed-c618-4a6e-89ad-0028b14981f4
      • WebGL 中坐标轴的范围是 [-1.0, 1.0]。
  • 绘制一个点 2(GLES -> JS 通信)

    • attribute 变量uniform 变量 能用来实现 GLES -> Javascript 的通信。
      • attribute 变量
        • 【P70】 关键词attribute被称为存储限定符(storage qualifier
        • 【P68】 attribute变量传输的是那些与顶点相关的数据
        • 【P68】 attribute变量是一种GLSLES变量,被用来从外部向顶点着色器内传输数据,只有顶点着色器能使用它。
      • uniform 变量
        • 【P68】 uniform变量传输的是那些对于所有顶点都相同(或与顶点无关)的数据
    • 【P70】 attribute变量必须声明成全局变量
    • 使用 attribute 变量通信的流程:获取变量地址 -> 设置变量值
    • GLES 代码:
      attribute vec4 aPos; // 外部参数
      
      void main() {
        gl_Position = aPos;
        gl_PointSize = 20.0;
      }
      
      JS 代码:
      // ...
      // 初始化 Shader
      if (!initShaders(gl, VERTEX_SHADER, FRAGMENT_SHADER)) {
        console.error('无法初始化着色器');
      }
      
      // 获取 aPos 变量地址
      // gl.program 在 initShaders() 调用之后自动创建
      var pos = gl.getAttribLocation(gl.program, 'aPos');
      // 设置 aPos 的值
      gl.vertexAttrib3f(pos, 0.5, 0.5, 0);
      
      gl.clearColor(0.5, 0.5, 0.5, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
      // ...
      
    • #+BEGIN_IMPORTANT
      注意:在 GLES 中声明的变量如果不用,会被编译器优化掉,导致在后面的 Javascript 中获取变量地址时失败!
      #+END_IMPORTANT
    • 658e232b-969c-4ba0-835f-24678d1ca94a
    • 658e2681-061a-4d6b-88e3-4bc4d9a8cfce
    • gl.vertexAttrib3f() 函数是其一系列函数中的一个
      • 3 表示矢量的元素个数,f 表示浮点数,i 表示整数,v 表示传入参数为矢量(数组)。
      • 如果传入的元素个数 < 目标变量的元素个数,那么 [v2, v3, v4] 的会被赋予默认值 [0, 0, 1](哪个少就补上哪个的默认值)。
      • gl.vertexAttribf1f(location, v1);
        gl.vertexAttribf2f(location, v1, v2);
        gl.vertexAttribf3f(location, v1, v2, v3);
        gl.vertexAttribf4f(location, v1, v2, v3, v4);
        
        gl.vertexAttribf1i(location, v1);
        gl.vertexAttribf2i(location, v1, v2);
        gl.vertexAttribf3i(location, v1, v2, v3);
        gl.vertexAttribf4i(location, v1, v2, v3, v4);
        
        gl.vertexAttribf1fv(location, arr);
        gl.vertexAttribf2fv(location, arr);
        gl.vertexAttribf3fv(location, arr);
        gl.vertexAttribf4fv(location, arr);
        
  • 通过鼠标点击绘点(响应点击事件)

    • 在线 Demo
      Javascript 源码
    • 坐标转换

      • var points = [];
        // 响应点击事件
        canvas.onmousedown = (event) => {
            console.clear();
            // 获取点击坐标
            // 转换步骤:页面坐标 -> canvas 坐标 -> WebGL 坐标
            // 这个坐标是页面坐标
            var x = event.clientX;
            var y = event.clientY;
        
            // 转换为 <canvas> 坐标
            // = 页面坐标 - <canvas> 左上角在页面中的坐标
            var rect = canvas.getBoundingClientRect();
            x = x - rect.left;
            y = y - rect.top;
        
            // 转换为 WebGL 坐标
            // 将原点从左上角移到中间位置
            console.log(x, y);
            y = -y;
            x = x + (-canvas.width / 2);
            y = y + (canvas.height / 2)
            // 缩放坐标轴
            x /= canvas.width / 2;
            y /= canvas.height / 2;
        
            console.log(x, y);
            points.push([x, y]);
            // 画点
            points.forEach((value) => {
                gl.vertexAttrib2fv(locPos, value);
                gl.drawArrays(gl.POINTS, 0, 1);
            });
        
        };
        
      • Canvas 坐标 -> WebGL 坐标 变换示意图
        (建议放大查看)
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    • 同时画多个点

      • gl.drawArrays() 函数是可以重复调用的,会在颜色缓冲区上继续绘制,也就是和之前的结果叠加。
      • var points = [];
        
        canvas.onmousedown = (event) => {
            // ...
            console.log(x, y);
            // 前面是一样的
        
            // 保存坐标
            points.push([x, y]);
            
            // 画点
            gl.clear(gl.COLOR_BUFFER_BIT);
            points.forEach((value) => {
                gl.vertexAttrib2fv(locPos, value);
                gl.drawArrays(gl.POINTS, 0, 1);
            });
        
        };
        
  • 改变点的颜色

    • 在线 Demo
      JS 源代码

    • 从 Javascript 向片元着色器中传递数据需要用 uniform 变量

      • 【P88】 uniform变量用来从JavaScript程序向顶点着色器和片元着色器传输“一致的”(不变的)数据。
    • 658f74d5-67e0-44d1-81c5-f3a8d417e99a

    • gl.uniform4f() 函数和 gl.VertexAttrub4f() 函数类似,也是一个系列的函数:
      658f74ff-4040-4cf7-ac0a-4d7ad40cf046

    • GLES 代码(片元着色器):

      precision mediump float; // 指定浮点数的精度为中等精度(包括范围和有效小数位)
      // 第五章讨论精度问题
      uniform vec4 uColor; // 外部参数
      void main(){
        gl_FragColor = uColor;
      }
      

      JS 代码:

      // 获取 uColor 变量地址
      var uColor = gl.getUniformLocation(gl.program, 'uColor');
      
      // ...
      canvas.onmousedown = function (event) {
        // 画点
        gl.clear(gl.COLOR_BUFFER_BIT);
        points.forEach((value) => {
          gl.vertexAttrib2fv(aPos, value);
          // 设置颜色(uColor)
          // 这里每次绘制都随机生成颜色
          var uColor = gl.getUniformLocation(gl.program, 'uColor');
          gl.drawArrays(gl.POINTS, 0, 1);
        });
      }
      gl.clear(gl.COLOR_BUFFER_BIT);
        points.forEach((value) => {
          gl.vertexAttrib2fv(aPos, value);
          // 设置颜色(uColor)
          // 这里每次绘制都随机生成颜色
          var uColor = gl.getUniformLocation(gl.program, 'uColor');
          gl.drawArrays(gl.POINTS, 0, 1);
        });
      }
      
文章来源:https://blog.csdn.net/XcantloadX/article/details/135684024
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。