图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载webgl-fundamentals仓库的问答篇QA收录了一个非常典型的可视化需求如何绘制像 deck.gl 的 trip-routes行程路线示例那样、带有拖尾trail滚动效果且会拐弯的运动折线。本文以仓库中日语原文档 webgl-qna-how-to-achieve-moving-line-with-trail-effects.md英文原版见 webgl-qna-how-to-achieve-moving-line-with-trail-effects.md为核心骨架结合仓库中两个可运行的完整示例与 twgl 辅助库源码讲透沿线的 UV 坐标 时间滚动这一核心方案并给出可直接复制的着色器与 JavaScript 代码。读完本文你将掌握在 WebGL 中为任意折线生成滚动颜色/渐变纹理拖尾效果的标准做法。问题背景单条拖尾线容易会转弯的线怎么办原文档记录的提问者困惑非常具有代表性像下面这个 demo 里的线是怎么画出来的画一条带拖尾trailing effects效果的简单直线可能很容易但这些线还在不断转弯turning their directions。问题的参照对象是 deck.gl 官方示例中的custom-layers/trip-routes一条条公交/行车轨迹线在底图上运动轨迹尾部拖出渐变的光带并且整条轨迹是曲折的折线而非直线。为什么直线 拖尾容易而折线 拖尾难核心原因在于WebGL 的gl.LINES原语只负责把每两个顶点连成一条 1 像素宽的线段它本身不理解整条折线的路径方向更不知道这条线从起点到终点走了多远。要让颜色沿着整条折线而不是每段各自滚动就必须自己把沿线的距离/位置这个信息编码进顶点数据——这正是本问答案的核心。仓库中关于LINES原语的适用场景与局限可参考基础课 webgl-points-lines-triangles.mdLINES非常适合调试框、网格线或简单的发光效果但在大多数平台上受限于 1 像素宽度因此若需要粗线条或抗锯齿轮廓通常要改用三角形条带等方式渲染。核心思路把每条折线当成一张可滚动的UV 条带原文档的答案只有三句话却是整套方案的纲领你可以为线传入 UV 坐标或者生成一个 UV 坐标然后用它给线着色。再传入一个时间值time就能实现类似滚动的效果。拆解一下完整方案由三个要素组成为每个顶点传入一个标量u它本质上是沿线的一维 UV 坐标取值范围、变化速率完全由你通过顶点数据控制。整条折线共享同一套u进度就实现了颜色沿整条线流动。顶点着色器原样透传把u从attribute转成varying交给片段着色器做插值。片段着色器中用fract(v_u time)制造滚动time随时间连续变化fract把结果约束在[0, 1)区间循环颜色就会沿线段无限循环地跑起来。为什么是fract(v_u time)fract(x)返回x的小数部分即x - floor(x)。当time从 0 匀速增长时v_u time每跨过整数就归零重来视觉上就是颜色图案沿折线方向匀速平移每经过一个整数周期图案完全重复一次。这就是拖尾流动的数学本质——无需在 JavaScript 侧更新任何顶点数据动画完全在 GPU 上由 uniform 驱动。折线数据怎么组织仓库示例example-1使用gl.LINES绘制三段首尾相接的线段顶点与u数据一一对应position6 个二维顶点组成 3 条线段.5,-.5 → .7,-.3、.7,-.3 → -.1,.8、-.1,.8 → -.8,.2折线明显转弯u每段两个端点各一个值[0,1]、[0,2]、[0,3]——这些数字定义了颜色图案在每一段线段上重复的次数第 1 段从 0 到 1 重复 1 次第 2 段从 0 到 2 重复 2 次第 3 段从 0 到 3 重复 3 次。注释在示例源码里写得很直白// these numbers define how many times the pattern repeats for each line segment。由于 WebGL 会在同一图元内部对varying做线性插值u从每段起点的 0 平滑插值到终点的 Nfract(v_u time)就会在每一段内部生成 N 个周期的滚动条纹。方案一程序化颜色滚动example-1这是最轻量的实现颜色完全由 GLSL 计算不依赖任何纹理资源。完整可运行代码见 webgl-qna-how-to-achieve-moving-line-with-trail-effects-example-1.html核心部分如下。着色器// 顶点着色器透传沿线坐标 u attribute vec4 position; attribute float u; varying float v_u; void main() { gl_Position position; v_u u; } // 片段着色器用 fract(v_u time) 滚动绿色条纹 precision mediump float; varying float v_u; uniform float time; void main() { float green fract(v_u time); gl_FragColor vec4(0, green, 0, 1); }green在 01 之间随时间和沿线位置循环产生由黑到绿的彗尾渐变并在折线拐弯处自然延续——因为v_u是沿线全局插值的拐弯不会打断滚动的连续性。顶点数据与渲染循环const gl document.querySelector(canvas).getContext(webgl); // 编译着色器、链接 program、查找 uniform 位置 const programInfo twgl.createProgramInfo(gl, [vs, fs]); // 创建缓冲区内部依次调用 gl.createBuffer / bindBuffer / bufferData const bufferInfo twgl.createBufferInfoFromArrays(gl, { position: { numComponents: 2, data: [ -.5, -.5, .7, -.3, .7, -.3, -.1, .8, -.1, .8, -.8, .2, ], }, u: { numComponents: 1, data: [ 0, 1, // 每段线上图案重复 1 次 0, 2, // 每段线上图案重复 2 次 0, 3, // 每段线上图案重复 3 次 ], }, }); function render(time) { time * 0.001; // 毫秒转秒 gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(programInfo.program); // 内部调用 gl.bindBuffer / enableVertexAttribArray / vertexAttribPointer twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); // 内部调用 gl.uniformXXXtime 取负控制滚动方向 twgl.setUniforms(programInfo, { time: -time }); // 内部调用 gl.drawArrays以 gl.LINES 方式绘制 twgl.drawBufferInfo(gl, bufferInfo, gl.LINES); requestAnimationFrame(render); } requestAnimationFrame(render);两个值得注意的细节time: -time的负号fract(v_u time)中 time 递增会让图案向u增大的方向滚动取负后图案反向滚动。方向完全由你决定也可以做成 uniform 开关。requestAnimationFrame的入参是毫秒所以第一行time * 0.001将其换算成秒保证动画速度与帧率无关高刷新率屏幕下不会跑得更快。背后的 twgl 辅助函数源码级对照两个示例都使用仓库自带的 twgl 精简版库 3rdparty/twgl-full.module.js 来省略样板代码。逐条对照其实现可确认每一步对应的原生 WebGL 调用twgl 调用对应原生操作源码位置twgl.createProgramInfo(gl, [vs, fs])编译两个着色器、链接 program、生成 uniform/attribute setter3rdparty/twgl-full.module.js#L8420-L8445twgl.createBufferInfoFromArrays(gl, arrays)为每个数组gl.createBufferbindBufferbufferData并计算numElements3rdparty/twgl-full.module.js#L2569-L2584twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo)bindBuffer、enableVertexAttribArray、vertexAttribPointer3rdparty/twgl-full.module.js#L8335-L8344twgl.setUniforms(programInfo, {time})按名字查找 setter 并调用gl.uniform1f等3rdparty/twgl-full.module.js#L8174-L8193twgl.drawBufferInfo(gl, bufferInfo, gl.LINES)无索引时调用gl.drawArrays(type, offset, numElements)3rdparty/twgl-full.module.js#L8523-L8542可以看到整个示例用 twgl 把创建缓冲区 绑定属性 设置 uniform 发起绘制压缩到了约 10 行而核心的着色器逻辑fract(v_u time)正是本文要掌握的技术本身。方案二用渐变纹理代替程序化颜色example-2原文档紧接着指出当然你也可以用纹理texture代替颜色代码。 完整实现见 webgl-qna-how-to-achieve-moving-line-with-trail-effects-example-2.html。着色器的唯一变化与方案一相比顶点着色器和顶点数据完全不变只有片段着色器从用代码计算颜色改为采样纹理precision mediump float; varying float v_u; uniform float time; uniform sampler2D tex; void main() { gl_FragColor texture2D(tex, vec2(fract(v_u time), 0.5)); }这里把fract(v_u time)当作纹理的 U 坐标固定取 V0.5 采样一行纹理。由于纹理默认WRAP模式是重复repeatu越过 1.0 后纹理自然循环——这与方案一里fract的循环语义完全一致只是把循环色带从代码换成了美术资源。16×1 渐变纹理的构造示例用twgl.createTexture直接以像素数组创建一张 16×1 的横向渐变色带红 → 橙 → 黄 → 绿 → 青 → 蓝 → 紫 → 白每个像素以 RGBA 十六进制字面量给出const texture twgl.createTexture(gl, { height: 1, src: [ 0xFF, 0x00, 0x00, 0xFF, // 红 0xFF, 0x44, 0x00, 0xFF, // 橙 0xFF, 0x88, 0x00, 0xFF, 0xCC, 0xFF, 0x00, 0xFF, // 黄 // ... 青、蓝、紫渐变 ... 0xFF, 0xFF, 0xFF, 0xFF, // 白 ], });twgl.createTexture会处理纹理对象创建、绑定、texImage2D上传和参数设置见 3rdparty/twgl-full.module.js#L6527-L6579。渲染循环里只需把纹理一并作为 uniform 传入twgl.setUniforms(programInfo, { time: -time, tex: texture, // twgl 会自动完成 activeTexture bindTexture });两种方案的取舍程序化颜色方案一零资源开销、着色器内即可生成任意数学渐变适合颜色规律简单、需要极致轻量的场景缺点是复杂色带如多段插值、品牌色写起来冗长。纹理渐变方案二把色带独立为 16×1 像素的小纹理换色只需换纹理甚至可以让不同线路共享纹理但使用不同u缩放复杂渐变、既有设计资源可以无缝复用。两种方案共享同一套utime滚动机制可以随时互换。从示例到真实场景如何画会转弯的轨迹线把两个示例推广到真实的地图轨迹/数据流可视化只需做三件事几何上将一条轨迹任意多段折线拆成LINES顶点序列每段两个顶点由于gl.LINES不共享顶点拐点处的顶点需要重复出现示例中.7,-.3、-.1,.8各出现两次即是如此。属性上为每个顶点生成沿线累计的u值。最简单的做法是令每段从 0 开始、按该段在整条折线上的长度比例或预设重复次数递增如示例的[0,1]、[0,2]、[0,3]。若希望颜色流速全程一致可以按各段实际长度累计u。动画上传入单调递增的timeuniform配合fract(v_u time)完成 GPU 端滚动颜色既可以用方案一的 GLSL 计算也可以用方案二的色带纹理。注意LINES在多数平台只能得到 1 像素宽的线作为轨迹高亮往往偏细。仓库基础课 webgl-points-lines-triangles.md 明确提示若需要结构化、可变宽度的粗线类似 SVG/Illustrator 效果应改用三角形渲染方案但本文的utime滚动机制与线的宽度实现方式正交可以原样迁移到三角形条带方案中。相关文档与源码索引原文档日文webgl/lessons/ja/webgl-qna-how-to-achieve-moving-line-with-trail-effects.md原文档英文webgl/lessons/webgl-qna-how-to-achieve-moving-line-with-trail-effects.md示例一程序化颜色webgl/webgl-qna-how-to-achieve-moving-line-with-trail-effects-example-1.html示例二纹理渐变webgl/webgl-qna-how-to-achieve-moving-line-with-trail-effects-example-2.htmlQA 总索引webgl/lessons/webgl-qna.md基础概念点/线/三角形绘制方式 webgl/lessons/webgl-points-lines-triangles.mdWebGL 工作原理 webgl/lessons/webgl-how-it-works.md依赖的 twgl 辅助库3rdparty/twgl-full.module.js赞分享图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载相关推荐three.js AfterimagePass 深入解析实现运动拖尾Afterimage后期效果的原理与实践three.js AfterimagePass 深入解析实现运动拖尾Afterimage后期效果的原理与实践 本文围绕 three.js 官方文档 doc前端3D渲染图形学PixiJS v8 MeshRope 实战指南让纹理沿折线路径弯曲的绳索、蛇形与拖尾效果PixiJS v8 MeshRope 实战指南让纹理沿折线路径弯曲的绳索、蛇形与拖尾效果 MeshRope 是 PixiJS v8 场景系统中专用于纹理沿折前端图形学WebGL 纹理完全指南从纹理坐标、Mipmap 到纹理图集webgl-fundamentals 实战解析WebGL 纹理完全指南从纹理坐标、Mipmap 到纹理图集webgl fundamentals 实战解析 本文基于 webgl fundamentals图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考