# twjl **Repository Path**: nodets/twjl ## Basic Information - **Project Name**: twjl - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-12 - **Last Updated**: 2025-09-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [非官方,只是学习过程中添加中文注释] ===================================================== [![构建状态](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 更加简洁。 ## 太长不看版 如果想快速完成任务,请使用 [three.js](http://threejs.org)。如果想用低级方式操作 WebGL,可以考虑使用 [TWGL](http://github.com/greggman/twgl.js/)。 ## 最简单的例子 不包括着色器(这是一个简单的四边形着色器),下面是完整的代码: ```html ``` [在线演示](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)