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

本文介绍了如何使用HTML5的<canvas>元素和WebGL进行基础绘图,包括透明度处理、颜色格式、2D/3D切换、着色器(顶点着色器和片元着色器)的应用,以及attribute和uniform变量在数据传递中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 第一个 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);
        });
      }
      
基本信息 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 ---------------------------------------- 目录 《WebGL编程指南》 第1 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(HelloCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4×4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(dr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值