第 9 章练习:二维旋转与旋转中心
对应教程:09 二维旋转矩阵与绕任意点旋转
起始示例:旋转箭头,修改 09-rotation.js 和需要时的 09-rotation.html。
练习 1:预测四个方向
**目标:**不运行代码,分别计算 rotate2D(θ) * vec2(1.0, 0.0) 在 θ = 0°、90°、180°、-90° 时的结果。再判断示例片元中若把 rotate2D(-uAngle) 改成 rotate2D(uAngle),角度滑块设为 90° 时箭头视觉上指向哪里。
**完成标准:**能写出四个向量,说明前向旋转和反向查询的符号区别。
练习 2:让箭头绕尾部转
**目标:**在示例中新增“箭头尾部为中心”模式,旋转时尾部保持固定。保留原有可拖动中心模式。
**要求:**箭头本地形状仍由 arrowMask 定义;在新模式中决定旋转中心的位置,不用 JavaScript 改动网格的 rotation。可以增加一个下拉框切换模式。
**完成标准:**切到尾部模式后,角度为 0° 和 90° 时,箭头尾部位于同一屏幕位置;蓝点准确落在尾部。
<details><summary>提示</summary>
当前图案的本地尾部 x 约为 -0.35。想让它固定在指定的世界二维坐标 anchor,可在查询图案前额外处理本地坐标的偏移。
</details>
练习 3:旋转重复图案中的每个单元
**目标:**借用第 6 章 fract 的重复方法,在平面上画 4 × 3 个小箭头,每个箭头都绕各自单元中心旋转,而不是所有箭头绕平面中心旋转。
**要求:**先计算每个单元内部的局部 UV,再做比例一致的二维旋转;相邻单元的箭头方向相同。保留现有单个箭头模式用于对照。
**完成标准:**角度设为 90° 时,每个箭头都在原单元内转向上方;整个阵列没有绕大平面公转,也没有被拉成长椭圆。
<a id=“reference-answers”></a>
第 9 章练习参考解答
对应练习:二维旋转与旋转中心。
练习 1
| θ | 结果 |
|---|---|
0° |
(1, 0) |
90° |
(0, 1) |
180° |
(-1, 0) |
-90° |
(0, -1) |
小数计算时允许极小的浮点误差。示例现在用 rotate2D(-uAngle) 反查,所以滑块 90° 让箭头视觉上指向上方。若改成 rotate2D(uAngle),视觉上指向下方。
练习 2
一种做法是在片元着色器中定义尾部锚点 anchor,让箭头本地尾部 (-0.35, 0.0) 正好对应它:
vec2 anchor = uPivot;
vec2 tailLocal = vec2( -0.35, 0.0 );
vec2 localPoint = tailLocal + rotate2D( -uAngle ) * ( point - anchor );
float arrow = arrowMask( localPoint );
float pivotDot = 1.0 - smoothstep( 0.018, 0.023, length( point - anchor ) );
在原有代码的 localPoint 和 pivotDot 处,用下拉控件控制是否采用这组计算。角度为零时 localPoint = tailLocal + point - anchor,当 point == anchor 时查询本地尾部。任意角度下,point == anchor 仍对应 tailLocal,因此尾部固定。
也可以把本地图案整体平移,使尾部先位于本地原点,再沿用原本的反向查询。关键是锚点与本地尾部之间的映射不能随角度改变。
练习 3
以下代码替换原来的 point / localPoint / arrow 计算,作为新模式的核心:
vec2 grid = vec2( 4.0, 3.0 );
vec2 cellUv = fract( vUv * grid );
vec2 cellSize = vec2( uAspect / grid.x, 1.0 / grid.y );
vec2 cellPoint = ( cellUv - 0.5 ) * cellSize;
vec2 localPoint = rotate2D( -uAngle ) * cellPoint;
float arrow = arrowMask( localPoint * 2.2 );
cellSize 把各单元的 UV 宽高换成相同的平面高度单位;如果省略它,单元在屏幕上可能不是正方形。2.2 把原箭头缩到单元内,可以按实际留白调整。这里 fract 在边界处跳变,每个单元都有自己的原点,所以每个箭头都绕自身单元中心转。为避免旧的蓝点和十字线干扰观察,可在新模式中不绘制它们;模式切换由 HTML <select> 和对应 uniform int 控制。