[非官方，只是学习过程中添加中文注释]

=====================================================

[![构建状态](https://github.com/greggman/twgl.js/actions/workflows/test.yml/badge.svg)](https://github.com/greggman/twgl.js/actions/workflows/test.yml)

TWGL: 一个极简的 WebGL 辅助库。该库的唯一目的是让使用 WebGL API 更加简洁。


## 太长不看版 (TL;DR)

如果想快速完成任务，请使用 [three.js](http://threejs.org)。如果想用低级方式操作 WebGL，可以考虑使用 [TWGL](http://github.com/greggman/twgl.js/)。

## 最简单的例子

不包括着色器（这是一个简单的四边形着色器），下面是完整的代码：

```html
<canvas id="c"></canvas>
<script src="../dist/7.x/twgl-full.min.js"></script>
<script>
  const gl = document.getElementById("c").getContext("webgl");
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

  const arrays = {
    position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
  };
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  function render(time) {
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    const uniforms = {
      time: time * 0.001,
      resolution: [gl.canvas.width, gl.canvas.height],
    };

    gl.useProgram(programInfo.program);
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, uniforms);
    twgl.drawBufferInfo(gl, bufferInfo);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
</script>
```

[在线演示](http://twgljs.org/examples/tiny.html).

## 为什么？是什么？怎么做？

WebGL 是一个非常冗长的 API。设置着色器、缓冲区、属性和统一变量需要大量代码。一个简单的带光照的立方体在 WebGL 中可能会轻松调用超过 60 次 API。

其核心功能只有以下几个主要函数：

*   `twgl.createProgramInfo` 编译着色器并创建属性和统一变量的设置器
*   `twgl.createBufferInfoFromArrays` 创建缓冲区和属性设置
*   `twgl.setBuffersAndAttributes` 绑定缓冲区并设置属性
*   `twgl.setUniforms` 设置统一变量
*   `twgl.createTextures` 创建各种类型的纹理
*   `twgl.createFramebufferInfo` 创建帧缓冲区和附件

还有一些额外的辅助函数和较低级别的函数，但以上 6 个函数是 TWGL 的核心。

比较使用 TWGL 和原生 WebGL 实现带点光源的立方体的代码。

### 编译着色器并查找位置

TWGL

```javascript
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
```

WebGL

```javascript
// 注意：这里假设你已经有 30 行用于编译 GLSL 的代码
const program = twgl.createProgramFromScripts(gl, ["vs", "fs"]);

const u_lightWorldPosLoc = gl.getUniformLocation(program, "u_lightWorldPos");
const u_lightColorLoc = gl.getUniformLocation(program, "u_lightColor");
const u_ambientLoc = gl.getUniformLocation(program, "u_ambient");
const u_specularLoc = gl.getUniformLocation(program, "u_specular");
const u_shininessLoc = gl.getUniformLocation(program, "u_shininess");
const u_specularFactorLoc = gl.getUniformLocation(program, "u_specularFactor");
const u_diffuseLoc = gl.getUniformLocation(program, "u_diffuse");
const u_worldLoc = gl.getUniformLocation(program, "u_world");
const u_worldInverseTransposeLoc = gl.getUniformLocation(program, "u_worldInverseTranspose");
const u_worldViewProjectionLoc = gl.getUniformLocation(program, "u_worldViewProjection");
const u_viewInverseLoc = gl.getUniformLocation(program, "u_viewInverse");

const positionLoc = gl.getAttribLocation(program, "a_position");
const normalLoc = gl.getAttribLocation(program, "a_normal");
const texcoordLoc = gl.getAttribLocation(program, "a_texcoord");
```

### 为立方体创建缓冲区

TWGL

```javascript
const arrays = {
  position: [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1],
  normal:   [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1],
  texcoord: [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1],
  indices:  [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
```

WebGL
```javascript
const positions = [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1];
const normals   = [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1];
const texcoords = [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1];
const indices   = [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23];

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
const indicesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
```

### 设置立方体的属性和索引

TWGL

```javascript
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
```

WebGL

```javascript
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(normalLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(texcoordLoc);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
```

### 设置带光照立方体的统一变量

TWGL

```javascript
// 初始化时
const uniforms = {
  u_lightWorldPos: [1, 8, -10],
  u_lightColor: [1, 0.8, 0.8, 1],
  u_ambient: [0, 0, 0, 1],
  u_specular: [1, 1, 1, 1],
  u_shininess: 50,
  u_specularFactor: 1,
  u_diffuse: tex,
};

// 渲染时
uniforms.u_viewInverse = camera;
uniforms.u_world = world;
uniforms.u_worldInverseTranspose = m4.transpose(m4.inverse(world));
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);

twgl.setUniforms(programInfo, uniforms);
```

WebGL

```javascript
// 初始化时
const u_lightWorldPos = [1, 8, -10];
const u_lightColor = [1, 0.8, 0.8, 1];
const u_ambient = [0, 0, 0, 1];
const u_specular = [1, 1, 1, 1];
const u_shininess = 50;
const u_specularFactor = 1;
const u_diffuse = 0;

// 渲染时
gl.uniform3fv(u_lightWorldPosLoc, u_lightWorldPos);
gl.uniform4fv(u_lightColorLoc, u_lightColor);
gl.uniform4fv(u_ambientLoc, u_ambient);
gl.uniform4fv(u_specularLoc, u_specular);
gl.uniform1f(u_shininessLoc, u_shininess);
gl.uniform1f(u_specularFactorLoc, u_specularFactor);
gl.uniform1i(u_diffuseLoc, u_diffuse);
gl.uniformMatrix4fv(u_viewInverseLoc, false, camera);
gl.uniformMatrix4fv(u_worldLoc, false, world);
gl.uniformMatrix4fv(u_worldInverseTransposeLoc, false, m4.transpose(m4.inverse(world)));
gl.uniformMatrix4fv(u_worldViewProjectionLoc, false, m4.multiply(viewProjection, world));
```

### 加载 / 设置纹理

TWGL

```javascript
const textures = twgl.createTextures(gl, {
  // 2 的幂次图像
  hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
  // 非 2 的幂次图像
  clover: { src: "images/clover.jpg" },
  // 来自画布
  fromCanvas: { src: ctx.canvas },
  // 来自 6 张图像的立方体贴图
  yokohama: {
    target: gl.TEXTURE_CUBE_MAP,
    src: [
      'images/yokohama/posx.jpg',
      'images/yokohama/negx.jpg',
      'images/yokohama/posy.jpg',
      'images/yokohama/negy.jpg',
      'images/yokohama/posz.jpg',
      'images/yokohama/negz.jpg',
    ],
  },
  // 来自单张图像的立方体贴图（可以是 1x6、2x3、3x2、6x1）
  goldengate: {
    target: gl.TEXTURE_CUBE_MAP,
    src: 'images/goldengate.jpg',
  },
  // 来自 JavaScript 数组的 2x2 像素纹理
  checker: {
    mag: gl.NEAREST,
    min: gl.LINEAR,
    src: [
      255,255,255,255,
      192,192,192,255,
      192,192,192,255,
      255,255,255,255,
    ],
  },
  // 来自类型化数组的 1x8 像素纹理
  stripe: {
    mag: gl.NEAREST,
    min: gl.LINEAR,
    format: gl.LUMINANCE,
    src: new Uint8Array([
      255,
      128,
      255,
      128,
      255,
      128,
      255,
      128,
    ]),
    width: 1,
  },
});
```

WebGL

```javascript
// 假设我已经加载了所有图片

// 2 的幂次图像
const hftIconTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, hftIconImg);
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// 非 2 的幂次图像
const cloverTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, hftIconImg);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// 来自画布
const cloverTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
gl.generateMipmaps(gl.TEXTURE_2D);
// 来自 6 张图像的立方体贴图
const yokohamaTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posXImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negXImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posYImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negYImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posZImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negZImg);
gl.generateMipmaps(gl.TEXTURE_CUBE_MAP);
// 来自单张图像的立方体贴图（可以是 1x6、2x3、3x2、6x1）
const goldengateTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
const size = goldengate.width / 3;  // 假设是 3x2 纹理
const slices = [0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1];
const tempCtx = document.createElement("canvas").getContext("2d");
tempCtx.canvas.width = size;
tempCtx.canvas.height = size;
for (let ii = 0; ii < 6; ++ii) {
  const xOffset = slices[ii * 2 + 0] * size;
  const yOffset = slices[ii * 2 + 1] * size;
  tempCtx.drawImage(element, xOffset, yOffset, size, size, 0, 0, size, size);
  gl.texImage2D(faces[ii], 0, format, format, type, tempCtx.canvas);
}
gl.generateMipmaps(gl.TEXTURE_CUBE_MAP);
// 来自 JavaScript 数组的 2x2 像素纹理
const checkerTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([
    255,255,255,255,
    192,192,192,255,
    192,192,192,255,
    255,255,255,255,
  ]));
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 来自类型化数组的 1x8 像素纹理
const stripeTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 1, 8, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, new Uint8Array([
    255,
    128,
    255,
    128,
    255,
    128,
    255,
    128,
  ]));
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
```

### 创建帧缓冲区和附件

TWGL

```javascript
const attachments = [
  { format: RGBA, type: UNSIGNED_BYTE, min: LINEAR, wrap: CLAMP_TO_EDGE },
  { format: DEPTH_STENCIL, },
];
const fbi = twgl.createFramebufferInfo(gl, attachments);
```

WebGL

```javascript
const fb = gl.createFramebuffer(gl.FRAMEBUFFER);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb);
```

### 设置统一变量和统一块结构及数组

给定如下 GLSL 结构数组：

```glsl
struct Light {
  float intensity;
  float shininess;
  vec4 color;
}
uniform Light lights[2];
```

TWGL

```javascript
const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
...
twgl.setUniforms(progInfo, {
  lights: [
    { intensity: 5.0, shininess: 100, color: [1, 0, 0, 1] },
    { intensity: 2.0, shininess:  50, color: [0, 0, 1, 1] },
  ],
});
```

WebGL

```javascript
// 假设我们已经编译并链接了程序
const light0IntensityLoc = gl.getUniformLocation('lights[0].intensity');
const light0ShininessLoc = gl.getUniformLocation('lights[0].shininess');
const light0ColorLoc = gl.getUniformLocation('lights[0].color');
const light1IntensityLoc = gl.getUniformLocation('lights[1].intensity');
const light1ShininessLoc = gl.getUniformLocation('lights[1].shininess');
const light1ColorLoc = gl.getUniformLocation('lights[1].color');
...
gl.uniform1f(light0IntensityLoc, 5.0);
gl.uniform1f(light0ShininessLoc, 100);
gl.uniform4fv(light0ColorLoc, [1, 0, 0, 1]);
gl.uniform1f(light1IntensityLoc, 2.0);
gl.uniform1f(light1ShininessLoc, 50);
gl.uniform4fv(light1ColorLoc, [0, 0, 1, 1]);
```

如果你只想在 TWGL 中设置第二个光源，可以这样做：

```javascript
const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
...
twgl.setUniforms(progInfo, {
  'lights[1]': { intensity: 5.0, shininess: 100, color: [1, 0, 0, 1] },
});
```

### 对比

[TWGL 示例](http://twgljs.org/examples/twgl-cube.html) vs [WebGL 示例](http://twgljs.org/examples/webgl-cube.html)

## 示例

*   [最简单示例](http://twgljs.org/examples/tiny.html)
*   [TWGL 立方体](http://twgljs.org/examples/twgl-cube.html)
*   [纹理](http://twgljs.org/examples/textures.html)
*   [基本几何体](http://twgljs.org/examples/primitives.html)
*   [2D 线条](http://twgljs.org/examples/2d-lines.html)
*   [动态缓冲区](http://twgljs.org/examples/dynamic-buffers.html)
*   [缩放环绕](http://twgljs.org/examples/zoom-around.html)
*   [文字](http://twgljs.org/examples/text.html)
*   [万花筒](http://twgljs.org/examples/kaleidoscope.html)
*   [隧道](http://twgljs.org/examples/tunnel.html)
*   [GPGPU 粒子](http://twgljs.org/examples/gpgpu-particles.html)
*   [项目列表](http://twgljs.org/examples/itemlist.html)
*   [无盒天空盒](http://twgljs.org/examples/no-box-skybox.html)
*   [跨域](http://twgljs.org/examples/crossorigin.html)
*   [顶点数组对象](http://twgljs.org/examples/vertex-array-objects.html)
*   [实例化](http://twgljs.org/examples/instancing.html)

### WebGL 2 示例

*   [统一缓冲区对象](http://twgljs.org/examples/uniform-buffer-objects.html)
*   [3D 纹理色调映射](http://twgljs.org/examples/3d-textures-tone-mapping.html)
*   [采样器](http://twgljs.org/examples/samplers.html)
*   [WebGL 2 纹理](http://twgljs.org/examples/webgl2-textures.html)
*   [3D 纹理体积](http://twgljs.org/examples/3d-texture-volume.html)
*   [3D 纹理体积无缓冲区](http://twgljs.org/examples/3d-texture-volume-no-buffers.html)
*   [2D 数组纹理](http://twgljs.org/examples/2d-array-texture.html)
*   [变换反馈](http://twgljs.org/examples/transform-feedback.html)
*   [变换反馈粒子](http://twgljs.org/examples/transform-feedback-particles.html)
*   [变换反馈粒子顶点数组](http://twgljs.org/examples/transform-feedback-particles-va.html)

### OffscreenCanvas 示例

*   [OffscreenCanvas](http://twgljs.org/examples/offscreencanvas.html)

## ES6 模块支持

*   [模块](http://twgljs.org/examples/modules.html)

## AMD 支持

*   [amd](http://twgljs.org/examples/amd-compiled.html)

## CommonJS / Browserify 支持

*   [browserify](http://twgljs.org/examples/browserify.html)

## 其他特性

*   包含一些可选的 3D 数学函数（完整版）

    欢迎使用任何数学库，只要它以扁平的 Float32Array 或 JavaScript 数组存储矩阵即可。

*   包含一些可选的基本几何生成器（完整版）

    平面、立方体、球体等。只是为了让入门更容易。

## 使用方法

查看示例。否则有几种不同的版本：

*   [twgl-full.module.js](file://d:\cesium\source\twgl.js-main\twgl.js-main\dist\4.x\twgl-full.module.js) es6 模块版本
*   [twgl-full.min.js](file://d:\cesium\source\twgl.js-main\twgl.js-main\dist\twgl-full.min.js) 完整版的压缩版本
*   [twgl-full.js](file://d:\cesium\source\twgl.js-main\twgl.js-main\src\twgl-full.js) 完整版的连接版本
*   [twgl.min.js](file://d:\cesium\source\twgl.js-main\twgl.js-main\dist\twgl.min.js) 最小版本（无 3D 数学，无基本几何体）
*   [twgl.js](file://d:\cesium\source\twgl.js-main\twgl.js-main\dist\twgl.js) 连接后的最小版本（无 3D 数学，无基本几何体）

## 下载

*   从 github

    [http://github.com/greggman/twgl.js](http://github.com/greggman/twgl.js)

*   从 bower

    ```bash
    bower install twgl.js
    ```

*   从 npm

    ```bash
    npm install twgl.js
    ```

    或者

    ```bash
    npm install twgl-base.js
    ```

*   从 git

    ```bash
    git clone https://github.com/greggman/twgl.js.git
    ```

## 理由及其他闲聊

TWGL 的目标是通过提供一些小型辅助函数来使 WebGL 更加简洁，并减少冗余和消除乏味的操作。TWGL **不是**为了帮助处理着色器的复杂性或编写 GLSL。也不是像 [three.js](http://threejs.org) 那样的 3D 库。它只是试图让 WebGL 不那么冗长。

TWGL 可以被视为 [TDL](http://github.com/greggman/tdl) 的精神继承者。TDL 创建了几个 *类* 来包装 WebGL，而 TWGL 尽量不进行包装。实际上你可以手动创建几乎所有 TWGL 数据结构。

例如，函数 [setAttributes](file://d:\cesium\source\twgl.js-main\twgl.js-main\src\programs.js#L1889-L1896) 接收一个属性对象。
在 WebGL 中，你可能会这样写代码：

```javascript
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(normalLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(texcoordLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
gl.vertexAttribPointer(colorLoc, 4, gl.UNSIGNED_BYTE, true, 0, 0);
gl.enableVertexAttribArray(colorLoc);
```

[setAttributes](file://d:\cesium\source\twgl.js-main\twgl.js-main\src\programs.js#L1889-L1896) 只是最简单的代码来为你做这些。

```javascript
// 手动为 TWGL 创建属性
const attribs = {
  a_position: { buffer: positionBuffer, size: 3, },
  a_normal:   { buffer: normalBuffer,   size: 3, },
  a_texcoord: { buffer: texcoordBuffer, size: 2, },
  a_color:    { buffer: colorBuffer,    size: 4, type: gl.UNSIGNED_BYTE, normalize: true, },
};
twgl.setAttributes(attribSetters, attribs);
```
上面的例子的重点是，TWGL 是一个**轻量级**的封装。它所做的只是试图让常见的 WebGL 操作更加简单和不那么冗长。欢迎将其与原生 WebGL 混合使用。

## API 文档

[API 文档在此](http://twgljs.org/docs/).

## 想学习 WebGL？

试试 [webglfundamentals.org](http://webglfundamentals.org)