尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
WebGL Fundamentals:二维图形缩放(2D Scale)实战详解
图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载本教程出自 webgl-fundamentals 开源仓库的 WebGL 基础系列日语版见 webgl/lessons/ja/webgl-2d-scale.md英文版见 webgl/lessons/webgl-2d-scale.md讲解如何在 WebGL 中对二维图形进行放大与缩小。读完本文你将掌握在顶点着色器中通过一个uniform vec2 u_scale与顶点坐标相乘实现缩放的核心思路、对应的 JavaScript 侧 uniform 状态管理完整代码以及负倍率带来的镜像翻转现象并为后续用矩阵统一三种变换打好基础。教程定位WebGL 基础系列中的「缩放」篇缩放Scale是 webgl-fundamentals 二维变换三连击平移 → 旋转 → 缩放的收官篇。该系列自 WebGL 的入门基础 开始前两篇分别是二维图形的平移与二维图形的旋转。本教程在此基础上继续演进最终引出下一篇文章二维矩阵数学——用矩阵把平移、旋转、缩放三种变换统一成更简单通用的形式。与前两篇一脉相承本篇依然采用「在顶点着色器中变换顶点、在 JavaScript 中通过 uniform 传入参数」的架构顶点数据只上传一次每次绘制只需更新 uniform 值无需在 CPU 侧重新计算顶点坐标。这正是平移教程末尾强调的核心思想——几何体哪怕有成千上万个顶点JavaScript 每帧也只需设置几个 uniform 值剩下的全部交给 GPU。缩放的核心思想把顶点坐标乘以倍率缩放与平移同样简单核心只有一句话把顶点坐标乘以缩放倍率。平移是对坐标做加法a_position u_translation而缩放是对坐标做乘法a_position * u_scale。这里u_scale是一个vec2分别代表 X 轴和 Y 轴的缩放倍率u_scale (1, 1)时图形大小不变乘以 1 等于原值u_scale (2, 2)时图形在两个方向都放大一倍u_scale (0.5, 0.5)时图形缩小为一半两个分量可以不同例如(2, 1)实现只在水平方向拉宽的「压扁」效果。注意 GLSL 中两个vec2直接相乘是逐分量乘法component-wise multiply因此a_position * u_scale等价于(a_position.x * u_scale.x, a_position.y * u_scale.y)与矩阵乘法的含义不同。缩放发生在像素空间从仓库中的完整示例 webgl/webgl-2d-geometry-scale.html 可以看到缩放是在顶点坐标尚未转换到裁剪空间clip space之前进行的。此时坐标仍处于「像素单位」的本地空间即教程中u_resolution对应的坐标系所以倍率 2 意味着图形在屏幕上占据两倍的像素尺寸。这一点与 3D 世界中通常以模型空间为单位、再叠加观察/投影变换的缩放有所不同但在 2D 教程阶段它是最直观的。顶点着色器的完整实现在上一篇旋转示例的基础上着色器的改动非常小新增一个uniform vec2 u_scale并在旋转之前先对坐标做缩放。仓库中 webgl/webgl-2d-geometry-scale.html 的完整顶点着色器如下script idvertex-shader-2d typex-shader/x-vertex attribute vec2 a_position; uniform vec2 u_resolution; uniform vec2 u_translation; uniform vec2 u_rotation; uniform vec2 u_scale; void main() { // 先缩放坐标 vec2 scaledPosition a_position * u_scale; // 再旋转坐标 vec2 rotatedPosition vec2( scaledPosition.x * u_rotation.y scaledPosition.y * u_rotation.x, scaledPosition.y * u_rotation.y - scaledPosition.x * u_rotation.x); // 最后叠加平移 vec2 position rotatedPosition u_translation; // 像素坐标 → 0~1 vec2 zeroToOne position / u_resolution; // 0~1 → 0~2 vec2 zeroToTwo zeroToOne * 2.0; // 0~2 → -1~1裁剪空间 vec2 clipSpace zeroToTwo - 1.0; gl_Position vec4(clipSpace * vec2(1, -1), 0, 1); } /script新增的只有两处其余来自前两篇uniform vec2 u_scale; // Scale the position vec2 scaledPosition a_position * u_scale;变换顺序缩放 → 旋转 → 平移着色器内依次执行缩放、旋转、平移三个步骤其中旋转的计算直接复用了scaledPosition而非原始a_position。这意味着缩放先于旋转发生图形先在自身坐标轴上被拉伸再整体旋转。如果交换顺序先旋转再缩放结果会完全不同——这正是矩阵篇中要重点讨论的「变换顺序决定结果」问题的雏形详见二维矩阵数学中对「先缩放后旋转再平移」与「先平移后旋转再缩放」两种顺序的对比说明。片段着色器保持不变片段着色器无需任何改动仍然只是输出一个uniform vec4 u_color的纯色precision mediump float; uniform vec4 u_color; void main() { gl_FragColor u_color; }JavaScript 端三个增量改动对应着色器新增的 uniformJavaScript 侧需要完成三件事查询 uniform 位置、维护缩放状态、每帧上传数值。1. 查询 uniform 位置在程序链接近成功后通过gl.getUniformLocation拿到u_scale的地址var scaleLocation gl.getUniformLocation(program, u_scale);2. 初始化缩放状态默认值为[1, 1]表示初始不缩放乘以 1 等于原值var translation [100, 150]; var rotation [0, 1]; var scale [1, 1]; var color [Math.random(), Math.random(), Math.random(), 1];3. 每帧设置 uniform在drawScene()中与平移、旋转并列调用gl.uniform2fv将两个浮点数一次性上传给vec2类型的u_scale。仓库中完整的drawScene如下见 webgl/webgl-2d-geometry-scale.htmlfunction drawScene() { webglUtils.resizeCanvasToDisplaySize(gl.canvas); // 设置 WebGL 视口从裁剪空间映射到像素空间 gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // 清空画布 gl.clear(gl.COLOR_BUFFER_BIT); // 使用当前着色器程序 gl.useProgram(program); // 打开顶点属性数组 gl.enableVertexAttribArray(positionLocation); // 绑定顶点缓冲 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // 告诉属性如何从缓冲区取数据 var size 2; // 每次迭代读取 2 个分量 var type gl.FLOAT; // 数据是 32 位浮点数 var normalize false; // 不归一化 var stride 0; // 0 每次按 size * sizeof(type) 前进 var offset 0; // 从缓冲区头部开始 gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset); // 设置分辨率像素 → 裁剪空间转换参数 gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); // 设置颜色 gl.uniform4fv(colorLocation, color); // 设置平移 gl.uniform2fv(translationLocation, translation); // 设置旋转 gl.uniform2fv(rotationLocation, rotation); // 设置缩放 ← 本篇新增 gl.uniform2fv(scaleLocation, scale); // 绘制几何体 var primitiveType gl.TRIANGLES; var offset 0; var count 18; // F 由 6 个三角形组成每个三角形 3 个顶点 gl.drawArrays(primitiveType, offset, count); }需要强调的是setGeometry(gl)只在初始化时调用一次把 F 的 18 个顶点上传到 GPUdrawScene中不再触碰顶点数据。每次拖动滑杆只是修改 JavaScript 数组、调用gl.uniform2fv重新上传 uniform随后gl.drawArrays即可让 GPU 用新倍率重绘——这正是「顶点上传一次、变换全部在着色器完成」架构的优势。交互示例与滑杆配置教程配套的可运行示例是 webgl/webgl-2d-geometry-scale.html页面描述为 Drag sliders to translate, rotate, and scale.可直接在浏览器中打开体验。它借助仓库工具 webgl/resources/webgl-lessons-ui.js 中的webglLessonsUI.setupSlider生成滑杆 UIwebglLessonsUI.setupSlider(#x, {value: translation[0], slide: updatePosition(0), max: gl.canvas.width }); webglLessonsUI.setupSlider(#y, {value: translation[1], slide: updatePosition(1), max: gl.canvas.height}); webglLessonsUI.setupSlider(#angle, {slide: updateAngle, max: 360}); webglLessonsUI.setupSlider(#scaleX, {value: scale[0], slide: updateScale(0), min: -5, max: 5, step: 0.01, precision: 2}); webglLessonsUI.setupSlider(#scaleY, {value: scale[1], slide: updateScale(1), min: -5, max: 5, step: 0.01, precision: 2});注意 X/Y 轴缩放滑杆的配置参数min: -5, max: 5倍率取值范围为 -5 到 5刻意放开到负数以便演示镜像翻转step: 0.01, precision: 2步进 0.01、显示两位小数允许精细调节平移与旋转滑杆则分别限定在画布尺寸内max: gl.canvas.width/height和 0~360 度。回调函数updateScale本质上是闭包把滑杆值写入对应轴再触发重绘function updateScale(index) { return function(event, ui) { scale[index] ui.value; drawScene(); }; }负倍率镜像翻转教程特别提醒一个观察点当缩放倍率为负数时图形会发生镜像翻转。例如u_scale (-1, 1)会让图形沿 Y 轴左右镜像X 坐标全部取反u_scale (1, -1)会上下翻转两者都为负则相当于绕原点旋转 180°。从数学上看scaledPosition a_position * u_scale中负分量自然地把对应轴坐标取反因此翻转是乘法的直接结果无需任何额外代码。这也解释了为何示例滑杆范围允许取到 -5——你可以亲手拖动scaleX滑杆跨过 0 点观察 F 在某一瞬间被压成一条线然后翻转到镜像位置的过程。从手写缩放走向矩阵数学本教程把缩放写死在着色器里但正如系列所强调的缩放、旋转、平移三种变换互相依赖顺序每次调整顺序都要改写着色器非常不便。下一篇二维矩阵数学将展示如何用 3×3 矩阵把三种变换统一起来——届时缩放矩阵的构建正是本篇思路的推广。在矩阵体系中缩放被表示为一个对角矩阵仓库中的矩阵库 webgl/resources/m3.js 提供了现成实现scaling函数见该文件 L259-L273function scaling(sx, sy, dst) { dst dst || new MatType(9); dst[0] sx; dst[1] 0; dst[2] 0; dst[3] 0; dst[4] sy; dst[5] 0; dst[6] 0; dst[7] 0; dst[8] 1; return dst; }其中sx、sy与本篇的u_scale.x、u_scale.y一一对应配套的scale(m, sx, sy)函数则是「矩阵 × 缩放矩阵」的快捷方式同文件 L284-L286。在矩阵方案下顶点着色器退化为一行gl_Position vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);缩放、旋转、平移全部由 JavaScript 侧按需组合矩阵实现且调整变换顺序不再需要改写任何 GLSL 代码。附注为什么示例图形用字母 F教程的底栏补充了一个经典设计决策为什么所有示例都用字母 F原文webgl/lessons/ja/webgl-2d-scale.md 底栏给出的理由是F 的朝向在任何方向下都能一眼辨认这是调试变换效果的关键。用爱心 ❤ 或三角形 △水平翻转后无法分辨它们具有对称性用圆形 ○ 更糟旋转与否几乎看不出来用四色矩形理论上可行但必须记住哪个角是什么颜色徒增记忆负担F 的左竖、上横、中横结构使其在缩放、旋转、镜像后依然可辨识方向方便即时验证变换是否正确。配套的方向示意图为 webgl/resources/f-orientation.svg。教程作者表示任何「方向可辨识」的图形都适用只是自第一次见到有人用 F 做纹理示例后便一直沿用至今。小结二维缩放的本质极其简单——a_position * u_scale一个乘法即可完成但它承载了两个重要观念一是「顶点只上传一次、变换交给着色器 uniform」的 GPU 友好架构二是变换顺序对结果的显著影响。前者在本系列中一以贯之后者则直接引出了下一站用矩阵数学统一平移、旋转与缩放三种变换为进入三维世界做好准备。若想继续深入可以继续阅读二维矩阵数学或回顾平移与旋转两篇建立完整认知。赞分享图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载相关推荐WebGL 二维缩放Scale实战在顶点着色器中使用 u_scale 实现像素级缩放变换WebGL 二维缩放Scale实战在顶点着色器中使用 u_scale 实现像素级缩放变换 本篇是 webgl fundamentals 系列「WebGL图形学webgl-fundamentals 实战WebGL 2D 矩阵Matrix完全指南——从平移旋转缩放到投影矩阵webgl fundamentals 实战WebGL 2D 矩阵Matrix完全指南——从平移旋转缩放到投影矩阵 本文是 webgl fundamenta图形学如何把QuickJS嵌入到你的C项目中C API嵌入开发8步完整教程如何把QuickJS嵌入到你的C项目中C API嵌入开发8步完整教程 QuickJS 是一个小巧、可嵌入的 JavaScript 引擎本教程带你用 C AP上一篇nodebestpractices 通用 Docker 最佳实践从 COPY/ADD 取舍到镜像完整性校验的六条守则下一篇如何通过APK安装器在Windows上原生运行安卓应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

COMSOL多物理场耦合实现三维材料损伤模拟

COMSOL多物理场耦合实现三维材料损伤模拟

1. 项目背景与核心价值在工程仿真领域,材料损伤模拟一直是极具挑战性的课题。传统有限元软件往往只能实现简单的线性损伤模型,而COMSOL Multiphysics凭借其强大的多物理场耦合能力和用户自定义功能,为复杂损伤行为的精确模拟提供了可能。这个…

📅 2026/10/13 8:17:32
AGI时代开发者生存指南:从编码产能到判断力价值重构

AGI时代开发者生存指南:从编码产能到判断力价值重构

“人口寒冬”和“AGI 黎明”,这两个词放在一起,本身就是一种很强烈的时代碰撞。我最近和一些团队负责人、独立开发者、行业前辈聊天,大家讨论的焦点几乎都在同一个地方:一边是招人越来越难,团队年龄结构悄悄变化&#…

📅 2026/10/13 8:12:32
goInception:MySQL 审核与回滚工具实战

goInception:MySQL 审核与回滚工具实战

简介:goInception 是一款集 SQL 审核、执行、备份与回滚语句生成于一体的 MySQL 运维工具,面向数据库管理员、后端开发与运维工程师,尤其适合需要规范 SQL 上线流程、降低误操作风险的团队。它通过对 SQL 进行语法解析,按自定义规…

📅 2026/10/13 8:12:32
MORE NEWS

更多资讯

📰

中柏EZpad 7S/HB10驱动包安装与避坑指南

简介:这份驱动包面向中柏EZpad 7S与HB10平板电脑用户,尤其是需要重装系统或修复硬件异常的个人与教育场景使用者。资源针对Windows 10/11平台整理,重点解决触摸屏失灵、声卡无声、显卡与网络设备无法识别等常见兼容性问题,让设备恢…

📰

OPC客户端C#完整源码实战:从连接失败到稳定自愈的工程化拆解

简介:这份资源是一套用C#编写的OPC客户端完整源码,面向工业自动化方向的.NET开发者、工控软件学习者以及需要对接OPC服务器的工程师,帮助理解如何在C#环境中实现与OPC服务器的数据通信。压缩包共3个文件,以xml配置文件和rels关系文…

📰

基于哈希表的轻量级键值数据库:持久化与并发设计实践

1. 项目概述与核心动机马上要进入正题之前,我先聊聊为什么会做“基于哈希表结构的轻量级数据库”这个项目。如果你翻过一些开源代码,会发现市面上的轻量级存储方案其实不少——有纯文件存储的,有基于B树实现索引的,也有直接把整个…

📰

DETR端到端目标检测:论文笔记与源码逐行拆解

1. 从“论文笔记源码”这个组合说起:为什么DETR值得逐行拆第一次看到“论文笔记源码”这种组合,我下意识会把它归类成两种东西之一:要么是学生交作业式的论文摘要,要么是跑通官方脚本后截几张图就完事的“复现记录”。但DETR这个题…

📰

SpringBoot+Vue+MySQL物流管理系统毕设全解析:从数据库设计到部署

“【毕业设计】SpringBootVueMySQL 物流信息管理系统平台源码数据库论文部署文档”——这个标题我在各种资源站、毕设辅导群、高校论坛里见过不下几百次,但说实话,真正把系统本地跑起来、把论文逻辑讲清楚、把源码改出自己风格的人,可能不到三…

📰

考研择校与夏令营:信息战如何决定你的上岸概率

1. 为什么择校是考研路上第一个分水岭每年三四月份,总有一批学弟学妹来问我同一个问题:“学长,我想考某所学校的某个专业,你觉得我行不行?”说实话,这个问题我没法直接回答,因为“行不行”取决于…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬