2、WebGL基础:WebGL是什么、WebGL与OpenGL的关系、WebGL的渲染管线、第一个WebGL程序

2.1 WebGL到底是什么?

WebGL,全称是Web Graphics Library。说白了,它就是一套能在浏览器里跑3D图形的标准。

我刚开始接触WebGL时,觉得它特别神奇。你想想看,不用装任何插件,打开浏览器就能渲染出3D模型。这在十年前简直是科幻片里的场景。

WebGL基于OpenGL ES 2.0。它把GPU的能力直接暴露给了JavaScript。嗯,这里要注意——它不是模拟3D,而是真正调用了你电脑的显卡。

核心要点:WebGL是浏览器与GPU之间的桥梁。它让JavaScript可以直接操作显卡,完成3D渲染。

2.2 WebGL与OpenGL的关系

很多新手会问:WebGL和OpenGL到底啥关系?

我简单解释一下。OpenGL是桌面端的3D图形标准。它很强大,但也很复杂。OpenGL ES是OpenGL的嵌入式版本,专门给手机、平板这类设备用的。

WebGL,其实就是基于OpenGL ES 2.0的浏览器实现。你可以把它理解成「浏览器版的OpenGL ES」。

特性 OpenGL OpenGL ES WebGL
运行环境 桌面系统 嵌入式设备 浏览器
版本基础 4.x 2.0 / 3.0 1.0 (基于ES 2.0)
语言绑定 C/C++ C/C++ JavaScript
跨平台 需编译 需编译 天然跨平台

我在项目中遇到过一个问题:同样的着色器代码,在桌面OpenGL上跑得好好的,搬到WebGL上就报错。后来发现是WebGL对精度声明要求更严格。这种坑,踩过一次就记住了。

我的建议:如果你熟悉OpenGL,学WebGL会很快。但要注意,WebGL删掉了一些桌面OpenGL的特性,比如几何着色器、细分曲面等。别想着把桌面端的代码直接搬过来用。

2.3 WebGL的渲染管线

渲染管线,说白了就是GPU处理数据的流水线。你给它一堆数据,它经过几个步骤,最后变成屏幕上的像素。

WebGL的渲染管线分为两大阶段:

  1. 顶点处理阶段——处理3D模型的顶点位置
  2. 片元处理阶段——计算每个像素的颜色

具体来说,流程是这样的:

  • 第一步:顶点着色器。它把3D坐标转换成屏幕坐标。我习惯把这一步叫做「摆位置」。
  • 第二步:图元装配。把顶点连成三角形。GPU只认识三角形,你给它四个点,它也会拆成两个三角形。
  • 第三步:光栅化。把三角形转换成像素点。这一步是自动的,你不需要写代码。
  • 第四步:片元着色器。计算每个像素的颜色。这是最灵活的一步,各种特效都在这里实现。
  • 第五步:逐片元操作。深度测试、模板测试、颜色混合。最终输出到屏幕。

记住:WebGL只做两件事——运行顶点着色器和片元着色器。其他步骤都是GPU自动完成的。你只需要写好这两个着色器程序。

为什么会这样设计?因为着色器是运行在GPU上的,而GPU是并行计算的王者。你把计算任务交给它,它用成百上千个核心同时算,效率极高。

2.4 第一个WebGL程序

好了,理论说完了。我们来写一个真正的WebGL程序。这个程序很简单——在画布上画一个点。

嗯,这里要注意。WebGL不能直接画点。它只能画点、线、三角形。我们这里用点来演示。

<!DOCTYPE html>
<html>
<head>
  <title>我的第一个WebGL程序</title>
</head>
<body>
  <canvas id="glCanvas" width="500" height="500"></canvas>

  <script>
    // 1. 获取canvas元素
    const canvas = document.getElementById('glCanvas');
    
    // 2. 获取WebGL上下文
    const gl = canvas.getContext('webgl');
    if (!gl) {
      alert('你的浏览器不支持WebGL!');
    }

    // 3. 定义顶点着色器
    const vsSource = `
      void main() {
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        gl_PointSize = 10.0;
      }
    `;

    // 4. 定义片元着色器
    const fsSource = `
      void main() {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    `;

    // 5. 编译着色器
    function createShader(gl, source, type) {
      const shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error('着色器编译错误:', gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    }

    const vertexShader = createShader(gl, vsSource, gl.VERTEX_SHADER);
    const fragmentShader = createShader(gl, fsSource, gl.FRAGMENT_SHADER);

    // 6. 创建着色器程序
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      console.error('程序链接错误:', gl.getProgramInfoLog(program));
    }

    gl.useProgram(program);

    // 7. 清空画布并绘制
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    
    gl.drawArrays(gl.POINTS, 0, 1);
  </script>
</body>
</html>

这段代码做了七件事:

  1. 拿到canvas元素
  2. 获取WebGL上下文——这是最关键的一步
  3. 定义顶点着色器——告诉GPU点的位置在哪
  4. 定义片元着色器——告诉GPU点的颜色是什么
  5. 编译着色器——把GLSL代码转成GPU能执行的指令
  6. 链接程序——把两个着色器组合成一个可执行程序
  7. 绘制——清空画布,画一个点

我曾经踩过的坑:第一次写WebGL时,我忘了检查着色器编译状态。结果页面一片空白,我找了半天才发现是着色器代码写错了。记住:一定要检查编译状态!不然调试起来很痛苦。

运行这段代码,你会看到一个黑色画布上出现一个红色的小点。位置在画布正中央。

为什么是正中央?因为gl_Position = vec4(0.0, 0.0, 0.0, 1.0)这个坐标,在WebGL的坐标系里就是中心点。x轴向右为正,y轴向上为正,范围都是-1到1。

你想想看,就这么几行代码,我们就让GPU帮我们画了一个点。虽然简单,但背后是整个渲染管线在运作。顶点着色器处理了位置,光栅化把点转成像素,片元着色器给了颜色。每一步都不可或缺。

我的习惯:每写一个新程序,我都会先跑通这个「画点」的例子。它能验证你的开发环境是否正常,也能帮你理解WebGL的基本流程。别嫌它简单,这是所有3D程序的起点。

好了,第一个WebGL程序就到这里。下一章我们会深入顶点着色器,看看怎么处理更复杂的3D模型。