WebGL 概念和基础入门

WebGL 概念和基础入门

WebGL 是什么

对于 WebGL 百度百科给出的解释是 WebGL 是一种 3D 绘图协议,而对此维基百科给出的解释却是一种 JavaScript API。由于 WebGL 技术旨在帮助我们在不使用插件的情况下在任何兼容的网页浏览器中开发交互式 2D 和 3D 网页效果,我们可以将其理解为一种帮助我们开发 3D 网页的绘图技术,当然底层还是 JavaScript API。

WebGL 发展史

WebGL 的发展最早要追溯到 2006 年,WebGL 起源于 Mozilla 员工弗拉基米尔·弗基西维奇的一项 Canvas 3D 实验项目,并于 2006 年首次展示了 Canvas 3D 的原型。这一技术在 2007 年底在 FireFox 和 Opera 浏览器中实现。2009 年初 Khronos Group 联盟创建了 WebGL 的工作组最初的工作成员包括 Apple、Google、Mozilla、Opera 等。 2011 年 3 月 WebGL 1.0 规范发布,WebGL 2 规范的发展始于 2013 年,并于 2017 年 1 月最终完成,WebGL 2 的规范,首度在 Firefox 51、Chrome 56 和 Opera 43 中被支持。

WebGL 中的基本概念

WebGL 运行在电脑的 GPU 中,因此需要使用能在 GPU 上运行的代码,这样的代码需要提供成对的方法,每对方法中的一个叫顶点着色器而另外一个叫做片元着色器,并且使用 GLSL 语言。将顶点着色器和片元着色器连接起来的方法叫做着色程序。

  • 顶点着色器 顶点着色器的作用是计算顶点的位置,即提供顶点在裁剪空间中的坐标值
    在这里插入图片描述

此块内容参考文章webglfundamentals

  • 片元着色器 片元着色器的作用是计算图元的颜色值,我们可以将片元着色器大致理解成网页中的像素
  • 数据获取方式 在前面我们提到了顶点着色器和片元着色器的概念,而顶点着色器和片元着色器这两个方法的运行都需要有对应的数据,接下来我们一起来了解一下着色器获取数据的四种方式:
    • 属性和缓冲 缓冲是发送到 GPU 的一些二进制数据序列,通常情况下缓冲数据包括位置、方向、纹理坐标、顶点颜色值等。 当然你可以根据自己的需要存储任何你想要的数据。 属性用于说明如何从缓冲中获取所需数据并将它提供给顶点着色器。
    • 全局变量 全局变量在着色程序运行前赋值,在运行过程中全局有效。全局变量在一次绘制过程中传递给着色器的值都一样。
    • 纹理 纹理是一个数据序列,可以在着色程序运行中随意读取其中的数据。一般情况下我们在纹理中存储的大都是图像数据,但你也可以根据自己喜欢存放除了颜色数据以外的其它数据
    • 可变量 可变量是一种顶点着色器给片元着色器传值的方式

小结

WebGL 只关心两件事:裁剪空间中的坐标值和颜色值。使用 WebGL 只需要给它提供这两个东西。 因此我们通过提供两个着色器来做这两件事,一个顶点着色器提供裁剪空间坐标值,一个片元着色器提供颜色值。

WebGL 工作原理

了解完 WebGL 的一些基本概念,我们可以一起来看看 WebGL 在 GPU 上的工作都做了些什么。正如我们之前了解到的 WebGL 在 GPU 上的工作主要分为两个部分,即顶点着色器所做的工作(将顶点转换为裁剪空间坐标)和片元着色器所做的工作(基于顶点着色器的计算结果绘制像素点)。假如我们需要绘制一个三角形,此时 GPU 上进行的工作便是先调用三次顶点着色器计算出三角形的 3 个顶点在裁剪空间坐标系中的对应位置,并通过变量 gl_Position 保存在 GPU 中,然后调用片元着色器完成每个顶点颜色值的计算,并通过变量 gl_FragColor 将对应的颜色值存储在 GPU 中。完成这些工作后我们已经得到了绘制三角形所需的像素点,最后便是光栅化三角形了。

原生 WebGL API 绘制三角形

前面我们已经学习了 WebGL 的发展史、基本概念和工作原理等内容,接下来我们就该实践出真知了,所以我们来看看如何通过 WebGL 在网页中绘制一个简单的三角形。我们知道 WebGL 作为一种 3D 绘图技术本身就是依托于 HTML5 中的 canvas 元素而存在的,所以再正式开始绘制之前我们需要进行一系列的准备工作:

  • 首先我们需要创建一个 canvas 元素作为绘制三角形所需的画布,并完成浏览器对 canvas 元素兼容性的测试。

    function webglInit () {
      const canvasEl = document.createElement('canvas'); // canvas 元素创建
      canvasEl.width = document.body.clientWidth; // 设置 canvas 画布的宽度
      canvasEl.height = document.body.clientHeight; // 设置 canvas 画布的高度
      document.body.append(canvasEl); // 将创建好的 canvas 画布添加至页面中的 body 元素下
      // 接下来我们需要判断浏览器对于 WebGL 的兼容性,如果浏览器不支持 WebGL 那么我们就不需要再进行下去了
      if(!canvasEl.getContext("webgl") && !canvasEl.getContext("experimental-webgl ")) {
        alert("Your Browser Doesn't Support WebGL");
        return;
      }
      // 如果浏览器支持 WebGL,那么我们就获取 WebGL 的上下文对象并复制给变量 gl
      const context = (canvasEl.getContext("webgl"))
      ? canvasEl.getContext("webgl") 
      : getContext("experimental-webgl");
      /* 
        设置视口 context.viewport(x, y, width, height);
        x: 用来设定视口的左下角水平坐标。默认值:0
        y: 用来设定视口的左下角垂直坐标。默认值:0
        width: 用来设定视口的宽度。默认值:canvas 的宽度
        height: 用来设定视口的高度。默认值:canvas 的高度
        当你第一次创建 WebGL 上下文的时候,视口的大小和 canvas 的大小是匹配的。然而,如果你重新改变了canvas的大小,你需要告诉 WebGL 上下文设定新的视口,因此这里作为初次创建这行代码可以省略
      */
      context.viewport(0, 0, context.canvas.width, context.canvas.height);
      return context;
    }
    
  • 准备好了 canvas 画布下一步就可以开始画三角形了,正如我们平常画画一般,我们需要准备画三角形所需的顶点即顶点着色器,以及三角形对应的填充色即片元着色器

    const gl =  webglInit();
    // 创建顶点着色器 语法 gl.createShader(type) 此处 type 为枚举型值为 gl.VERTEX_SHADER 或 gl.FRAGMENT_SHADER 两者中的一个
    const vShader = gl.createShader(gl.VERTEX_SHADER) 
    // 编写顶点着色器的 GLSL 代码 语法 gl.shaderSource(shader, source); shader - 用于设置程序代码的 webglShader(着色器对象) source - 包含 GLSL 程序代码的字符串
    gl.shaderSource(vShader, `
      attribute vec4 v_position;
    
      void main() {
        gl_Position = v_position; // 设置顶点位置
      }
    `)
    gl.compileShader(vShader) // 编译着色器代码
    
    const fShader = gl.createShader(gl.FRAGMENT_SHADER) 
    gl.shaderSource(fShader, `
      precision mediump float;
      uniform vec4 f_color;
      void main() {
        gl_FragColor = f_color; // 设置片元颜色
      }
    `) // 编写片元着色器代码 
    gl.compileShader(fShader) // 编译着色器代码
    
  • 前面我们已经完成了顶点着色器和片元着色器的配置,做好了一切绘制前的准备工作接下来,接下来我们就需要创建一个程序用来连接我们的顶点着色器和片元着色器完成最终的三角形绘制工作。

// 创建一个程序用于连接顶点着色器和片元着色器
const program = gl.createProgram() 
gl.attachShader(program, vShader) // 添加顶点着色器
gl.attachShader(program, fShader) // 添加片元着色器
gl.linkProgram(program) // 连接 program 中的着色器

gl.useProgram(program) // 告诉 WebGL 用这个 program 进行渲染

const color = gl.getUniformLocation(program, 'f_color') 
// 获取 f_color 变量位置
gl.uniform4f(color, 0.93, 0, 0.56, 1) // 设置它的值

const position = gl.getAttribLocation(program, 'v_position') 
// 获取 v_position 位置
const pBuffer = gl.createBuffer() 
// 创建一个顶点缓冲对象,返回其 id,用来放三角形顶点数据,
gl.bindBuffer(gl.ARRAY_BUFFER, pBuffer) 
// 将这个顶点缓冲对象绑定到 gl.ARRAY_BUFFER
// 后续对 gl.ARRAY_BUFFER 的操作都会映射到这个缓存
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0.5,
    0.5, 0,
    -0.5, -0.5
]),  // 三角形的三个顶点
     // 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据
gl.STATIC_DRAW // 表示缓冲区的内容不会经常更改
)
// 将顶点数据加入的刚刚创建的缓存对象

gl.vertexAttribPointer( // 告诉 OpenGL 如何从 Buffer 中获取数据
    position, // 顶点属性的索引
    2, // 组成数量,必须是 1,2,3 或 4。我们只提供了 x 和 y
    gl.FLOAT, // 每个元素的数据类型
    false, // 是否归一化到特定的范围,对 FLOAT 类型数据设置无效
    0, // stride 步长 数组中一行长度,0 表示数据是紧密的没有空隙,让 OpenGL 决定具体步长
    0 // offset 字节偏移量,必须是类型的字节长度的倍数。
)
gl.enableVertexAttribArray(position);
// 开启 attribute 变量额,使顶点着色器能够访问缓冲区数据

gl.clearColor(0, 1, 1, 1) // 设置清空颜色缓冲时的颜色值
gl.clear(gl.COLOR_BUFFER_BIT) // 清空颜色缓冲区,也就是清空画布
// 语法 gl.drawArrays(mode, first, count); mode - 指定绘制图元的方式 first - 指定从哪个点开始绘制 count - 指定绘制需要使用到多少个点
gl.drawArrays( gl.TRIANGLES, 0, 3 )

配合 HTML 文件运行上述代码后我们可以在网页中看到如图所示的三角形,且三角形大小根据浏览器窗口大小自适应。
在这里插入图片描述

可以看到仅仅是绘制一个简单的三角形我们就已经写了一大长串的 JS 代码,如果真的用原生 WebGL API 编写一个动态的 3D 交互式网页,那么开发成本可见是极其昂贵的。

WebGL 原生 API 开发的不足

上面原生 WebGL API 绘制三角形的例子,充分向我们展示了使用原生 WebGL API 开发 3D 交互式网页存在的问题。尽管从功能上而言原生 WebGL API 可以满足我们任意场景的开发需要但是,其开发和学习的成本极其昂贵。对于 WebGL 的初学者而言是极度不友好的,我们需要配置顶点着色器用于计算绘制顶点所在的位置,而这对于开发者而言需要一定的数学基础熟悉矩阵的运算,同时也要有空间几何的概念熟悉 3D 物体的空间分布。而场景的光照,纹理等的设计也都需要对颜色的配置有自己的见解。所以为了给初学者降低难度,下面我将介绍一些 WebGL 开发的常用框架。

几种 WebGL 开发的框架

  • Three.js
    • Three.js 是 WebGL 的综合库,其应用范围比较广泛,美中不足的一点是,Three.js 库没有比较全面详细的官方文档,对于使用者而言不是特别友好
  • Cesium.js
    • Cesium.js 是专用于 3D 地图开发的 WebGL 库,其拥有较为全面的 3D 地图开发 API,对于需要开发 3D 地图的开发者而言是一个不错的选择,但针对其他场景的应用开发覆盖的就不是很全面了
  • Babylon.js
    • Babylon.js 是一款国外应用较广泛的 WebGL 库,感兴趣的小伙伴可以自己去了解一下,这里就不做详细介绍了

Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,同时 Three.js 也是一个综合性的 WebGL 库。如果你需要进行 3D 地图网页的开发那就可以用到 Cesium.js 了,Cesium.js 是一款专用于地图开发的 WebGL 库。而 Babylon.js 则是国外较火的 WebGL 库。

基于 Three.js 绘制旋转立方体

  • 运用 Three.js 绘制旋转立方体的第一步同原生 WebGl 一样,首先便是要准备 Three.js 运行所需的环境。

    // 创建 renderer 变量用于存储渲染器对象
    var renderer;
    // initThree 函数用来初始化 Three.js 运行所需的环境
    function initThree() {
      // 同原生 WebGL 环境搭建过程一样,Three.js 也需要先设置画布 canvas 元素的大小
      width = document.getElementById('canvas-frame').clientWidth; // 设置宽度属性为浏览器窗口宽度
      height = document.getElementById('canvas-frame').clientHeight; // 设置高度属性为浏览器窗口高度
      // 新建一个 WebGL 渲染器并赋值给 renderer 变量
      renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      // 设置画布大小为浏览器窗口大小
      renderer.setSize(width, height);
      // 将画布元素挂载到页面
      document.getElementById('canvas-frame').appendChild(renderer.domElement);
      // 设置清空画布的颜色为白色
      renderer.setClearColor(0xFFFFFF, 1.0);
    }
    
  • 接下来不同于原生 WebGL 需要准备顶点着色器和片元着色器,Three.js 需要准备的是相机。Three.js 绘制 3D 网页所需的 3 大基本要素便是 相机、场景和物体,当然如果有需要设置明暗效果我们还需要加入第 4 要素光源,光源并不一定需要设置,但是相机、场景和物体是一定有的。

    // 创建 camera 变量用于存储相机对象
    var camera;
    // 初始化相机函数 Three.js 中相机的类型有好几种可以根据具体需要进行选择这里我们要创建的是一个旋转的立方体所以采用的是透视相机,而如果需要创建 3D 阴影效果的场景则需要使用正交相机
    function initCamera() {
      /* 
        创建透一个视相机的实例语法 PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number ) 
        fov - 视角
        aspect - 物体的长宽比
        near - 相机近点截图
        far - 相机远点截图
      */
      camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
      camera.position.x = 0; // 设置相机在三维空间坐标中 x 轴的位置
      camera.position.y = 10; // 设置相机在三维空间坐标中 y 轴的位置
      camera.position.z = 5; // 设置相机在三维空间坐标中 z 轴的位置
      camera.up.x = 0;
      camera.up.y = 0;
      camera.up.z = 1;
      camera.lookAt(new THREE.Vector3(0,0,0));// 设置相机的观察点
    }
    
  • 上一步我们完成了相机的设置,下面我们来准备 Three.js 绘制 3D 网页所需的第二要素场景。

    // 创建 scene 变量用于存储场景对象
    var scene;
    // initScene 函数创建一个场景并赋值给 scene 变量
    function initScene() {
      scene = new THREE.Scene();
    }
    
  • 准备好了相机和场景下面我们就需要设置拍摄的物体了,完成物体的绘制后将其添加到场景中。

    // 创建一个 cube 变量用于存放几何立方体
    var cube;
    
    // initObject 函数就是我们创建场景的核心了
    function initObject() {
      // 首先创建一个一个几何类的实例并赋值给 geometry 变量
      var geometry = new THREE.BoxGeometry(1, 1, 1); 
      // 然后创建一种材质的实例 MeshBasicMaterial 材质的构造函数能够创建一种简单的不受场景灯光效果影响的材质
      var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
      // Mesh 是一种三角形网格基本单元的构造函数,类似于我们原生 WebGL 中的片元着色器它用于连接几何体和材质
      cube = new THREE.Mesh( geometry, material );
      // 最后将创建好的几何立方体添加到场景中
      scene.add(cube);
    }
    
  • 到这里我们已经完成了 Three.js 绘制 3D 网页所需的基本配置,当然如果有需要对 3D 网页的明暗效果,灯光颜色做处理的我们还可以在场景中加入灯光的配置,这里由于我们的旋转立方体对于灯光并未有什么特殊的要求,所以我们便直接进入最后一步场景的渲染。

    // render 函数提供了浏览器的循环渲染功能
    function render() {
      cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
    }
    // 最后将 Threee.js 环境初始化,场景创建,相机创建渲染器创建以及渲染初始化等函数合成到一起执行我们就完成了一个旋转立方体的绘制
    function threeStart() {
    initThree();
    initCamera();
    initScene();
    initObject();
    render();
    }
    document.addEventListener('DOMContentLoaded',function(){
      threeStart();
    });
    
  • Three.js 的旋转立方体的绘制还需要配合 HTML 文件使用才能看到效果

    <!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>
      <script type="text/javascript" src="../utils/three.js"></script>
      <style type="text/css">
        div#canvas-frame {
          border: none;
          cursor: pointer;
          width: 100%;
          height: 600px;
          background-color: #EEEEEE;
        }
      </style>
    </head>
    <body>
      <div id="canvas-frame"></div>
    </body>
    </html>
    

    配合 HTML 文件运行上述代码后我们可以在网页中看到,一个旋转的绿色立方体

在这里插入图片描述

小结

通过对比我们发现尽管我们通过 Three.js 创建了更为复杂的场景,但是代码量相对 WebGL 原生 API 绘制三角形时反而要少了。由此可见对于初学者而言,直接使用 WebGL 原生 API 进行 3D 网页的开发,显然是不合适的。这时候我们就可以借助像 Three.js 这样的 WebGL 封装库进行开发。相较之原生 API 的开发,这类第三方封装好的 WebGL 库大大降低了我们的开发成本,同时也能帮助我们开发出更加炫酷的页面效果。当然也不是说原生 API 不好,毕竟如果有能力学透 WebGL 原生 API 的开发还是能够帮助我们在开发 3D 网页的时候实现更加随心所欲的功能,且 Three.js 本身的文档并不是特别完善所以想要顺利的使用同样需要摸透 WebGL 原生 API。

总结

WebGL 技术出现的时间并不算短,然而尽管能够开发出拥有炫酷效果的 3D 网页却一直未能大火,现今应用的最多的也不过是 3D 网页游戏的开发。这其中很大一部分的原因便是受到网速发展的制约,在当今这个快节奏的社会中人们对于网页加载速度的忍耐度是极低的,一个 WebGL 开发的 3D 网页动辄需要三四秒的打开时间对用户而言无疑是极度不友好的。但是相信随着 5G 通信技术的发展,网络通信技术飞速发展下,WebGL 技术的明天可能会迎来新的发展契机。

03-14
<think>好的,用户想了解WebGL入门教程或使用指南。首先,我需要整理已有的引用内容,看看有哪些信息可以利用。根据引用[2],有一个B站的视频教程,可能适合初学者。另外,引用[1]、[3]、[4]提到了WebGL基础概念、作用、与传统技术的比较,以及基本绘图流程,这些都可以作为教程的组成部分。 接下来,我需要按照用户提供的系统级指令来组织回答。用户要求使用中文,结构清晰,分步骤说明,并生成相关问题。同时,所有数学表达式要用$...$,独立公式用$$...$$,但根据现有引用内容,可能暂时用不上数学公式,不过要注意如果有的话要正确应用。 首先,回答的结构应该包括以下几个部分: 1. WebGL概述:定义、作用、与传统技术的比较(引用[1]、[3])。 2. 入门教程步骤:环境准备、获取上下文、着色器编写、渲染流程(引用[2]、[4])。 3. 示例代码:可能引用[4]中的代码片段,但用户提供的引用中没有具体代码,需要确认是否有其他来源,但根据现有资料,可能只能提到获取上下文的例子。 4. 学习资源推荐:比如引用[2]中的视频教程,引用[3]提到的移动端应用前景。 5. 相关问题:根据用户示例,生成相关问题,如性能优化、应用场景等。 需要注意的是,用户提供的引用[2]中有视频地址课前准备的内容,比如安装开发工具、live-server,以及需要ES6、HTML、CSS基础,数学基础如三角函数向量。这些应该在入门步骤中提及。 另外,引用[4]提到获取WebGL上下文着色器的作用,这部分需要详细说明,可能分步骤讲解如何初始化WebGL,编写着色器,渲染图形等流程。 可能需要检查是否有遗漏的信息点,比如WebGL的应用场景(引用[3]提到移动终端)、与传统图形技术的比较(引用[1]),这些可以作为概述部分的内容。 现在,组织回答的结构: 一、WebGL概述 - 定义作用(引用[1]、[3]) - 与传统技术的比较(引用[1]) - 应用场景(引用[3]) 二、WebGL入门教程步骤 1. 环境准备(引用[2]) - 安装开发工具 - 安装live-server - 基础知识要求 2. 初始化WebGL上下文(引用[4]) 3. 编写着色器(顶点片元,引用[4]) 4. 渲染流程(引用[2]、[4]) 三、学习资源推荐 - 视频教程(引用[2]) - 进一步学习建议 然后,生成相关问题,参考用户示例中的快速排序相关问题,可能涉及基础概念、优化、应用、数学基础等。 需要确保回答中的引用标识正确,例如[^1]、[^2]等,放在相关段落末尾。同时,注意代码块的使用,如果有代码的话,但用户提供的引用中只有获取上下文的代码片段,可以放在步骤中。 现在,检查是否覆盖了所有必要的信息,并符合格式要求。确保没有使用Markdown格式,除了代码块(如果有的话),但用户示例中允许代码块。此外,数学公式可能需要用到,但当前内容可能不涉及,如果有的话需要正确使用$符号。 最终,确保回答结构清晰,分步骤,引用正确,并且生成的相关问题与WebGL相关,帮助用户进一步学习。</think>### WebGL 入门指南 #### 一、WebGL 基础概念 WebGL 是一种基于 OpenGL ES 的 Web 图形库,允许在浏览器中直接渲染交互式 3D/2D 图形,无需插件。与传统图形技术(如 Flash)相比,WebGL 直接利用 GPU 加速,性能更高且兼容现代浏览器标准[^3]。其核心应用场景包括游戏开发、数据可视化、工业设计等,尤其在移动端(如手机、平板)潜力巨大[^3]。 #### 二、入门步骤详解 1. **环境准备** - 安装开发工具:推荐使用 WebStorm 或 VSCode。 - 安装 `live-server`:通过命令 `npm i -g live-server` 快速搭建本地服务器[^2]。 - 基础知识:需掌握 HTML、CSS、ES6 语法,建议了解基础三角函数向量运算(非必需但有益)。 2. **初始化 WebGL 上下文** 在 HTML 中创建 `<canvas>` 元素,并通过 JavaScript 获取 WebGL 上下文: ```javascript const canvas = document.getElementById('glCanvas'); const gl = canvas.getContext('webgl'); if (!gl) { console.error('WebGL 不支持'); } ``` 此步骤是后续所有操作的基础[^4]。 3. **编写着色器程序** - **顶点着色器**:定义图形顶点位置,例如: ```glsl attribute vec4 a_position; void main() { gl_Position = a_position; } ``` - **片元着色器**:定义颜色纹理,例如: ```glsl precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色 } ``` 着色器通过 GLSL 编写,需编译后链接到 WebGL 程序中[^4]。 4. **渲染流程** - 将顶点数据传入缓冲区。 - 调用 `gl.drawArrays()` 绘制图形。 - 示例:绘制一个点(WebGL 最简单图形)需配置着色器并调用绘制命令[^2]。 #### 三、学习资源推荐 - **视频教程**:B 站系列教程《WebGL 最短教程》涵盖从环境配置到基础渲染。 - **官方文档**:MDN WebGL 指南提供 API 详解示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值