` 实现二维与三维旋转变换)
three.js TSL 节点详解用RotateNode/rotate()实现二维与三维旋转变换【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js导读RotateNode是 three.js 节点材质Node Material / TSL体系中负责“对位置类节点施加旋转变换”的工具节点它同时支持 2D输入为vec2旋转量为单个float角度与 3D输入为vec3/vec4旋转量为欧拉角分量两种计算路径。通过其 TSL 工厂函数rotate()你可以在不编写 GLSL 的前提下把旋转直接嵌入material.positionNode、UV 坐标计算等节点表达式中实现粒子自旋、Sprite 旋转、实例化对象旋转、贴图 UV 旋转等常见效果。读完本文你将掌握RotateNode的构造方式、公开属性/方法、内部矩阵计算原理、缓存与序列化机制以及在 three.js 官方示例与源码中的真实用法。一、RotateNode 在节点体系中的定位从源码文档注释可知RotateNode的继承链为EventDispatcher → Node → TempNode → RotateNode对应源码 src/nodes/utils/RotateNode.js 中import TempNode from ../core/TempNode.js; /** * Applies a rotation to the given position node. * * augments TempNode */ class RotateNode extends TempNode { ... }它继承自TempNodesrc/nodes/core/TempNode.js而TempNode本身具备一个关键能力通过缓存管理当同一节点在着色器中被引用多次时会自动创建临时变量避免重复计算相同表达式参见TempNode.build()中基于usageCount的判定逻辑。这保证了旋转矩阵构建这类计算不会被冗余展开。其作用正如类注释所描述——对给定的 position 节点施加一次旋转。它接收“待旋转的坐标”与“旋转量”两个节点作为输入输出仍是节点因此可以继续参与 TSL 表达式链运算.add()、.mul()、.mod()等也可以整体赋值给material.positionNode这样的材质钩子。说明虽然命名与“位置”强相关但实际使用中旋转对象不限于顶点位置UV 坐标、局部偏移量等一切可旋转的量都可作为第一个参数传入详见下文“真实应用”。二、构造函数与 TSL 工厂函数rotate()2.1 构造函数签名官方文档docs/pages/RotateNode.html给出的签名如下new RotateNode( positionNode : Node, rotationNode : Node )源码中的实际实现src/nodes/utils/RotateNode.js还带有第三个可选参数constructor( positionNode, rotationNode, order XYZ ) { super(); this.positionNode positionNode; this.rotationNode rotationNode; /** * The Euler rotation order. * * private * type {string} * default XYZ */ this._order order; }参数含义参数类型说明positionNodeNode待旋转的坐标节点。它可以是vec2二维也可以是vec3/vec4三维。该节点的类型直接决定最终节点类型见下文generateNodeTyperotationNodeNode施加到positionNode上的旋转量。当positionNode为 2D 数据时它表示一个float旋转角弧度当positionNode为 3D 数据时它表示一个欧拉角各分量x/y/z为对应轴向旋转角单位弧度order可选string欧拉旋转顺序默认XYZ仅用于 3D 旋转路径决定三个轴向旋转矩阵的复合顺序2.2 TSL 快捷工厂函数rotate绝大多数情况下不需要手动new RotateNode(...)而是使用文件底部导出的 TSL 工厂函数src/nodes/utils/RotateNode.jsexport const rotate /*__PURE__*/ nodeProxy( RotateNode ).setParameterLength( 2, 3 );nodeProxy来自 src/nodes/tsl/TSLBase.js它会自动把普通调用包装成节点构造调用.setParameterLength(2, 3)表示该函数既可传 2 个参数positionNode, rotationNode顺序取默认XYZ也可传 3 个参数positionNode, rotationNode, order。rotate已由 src/Three.TSL.js 统一导出因此可直接从three/tsl引入import { rotate } from three/tsl;2.3 最小使用示例TSL 风格的最小用法将旋转后位置赋值给材质的位置钩子const rotated rotate( positionLocal, angleNode ); // 2D/3D 自动适配 material.positionNode rotated;三、公开属性与 Euler 顺序访问器3.1 属性属性类型说明.positionNodeNode待旋转的 position 节点构造函数第一参数.rotationNodeNode旋转量节点。2D 时为单个浮点角度3D 时为欧拉角._order私有stringEuler 旋转顺序默认XYZpositionNode与rotationNode均为公有可写属性赋值后会在下一次构建setup时生效。3.2 setOrder / getOrder3D 旋转顺序通过一对链式友好的访问器维护src/nodes/utils/RotateNode.jssetOrder( value ) { this._order value; return this; // 支持链式调用 } getOrder() { return this._order; }用法示例// 方式一构造函数第三参 const r1 rotate( positionLocal, euler, ZYX ); // 方式二显式 setOrder工厂函数返回的节点可继续链式配置 const r2 rotate( positionLocal, euler ).setOrder( ZYX );对于 2D 输入_order会被忽略因为 2D 只有单一旋转角不存在轴向顺序问题。setOrder返回this因此可插入任意 TSL 链式表达式中使用。四、核心原理2D 与 3D 两条计算路径RotateNode根据输入节点类型自动分流其核心逻辑位于setup()src/nodes/utils/RotateNode.js配合generateNodeType()src/nodes/utils/RotateNode.js决定节点类型generateNodeType( builder ) { return this.positionNode.getNodeType( builder ); }即节点的最终类型由positionNode的类型决定覆盖了TempNode.generateNodeType并由此在setup()中做分支判断。4.1 二维路径nodeType vec2if ( nodeType vec2 ) { const cosAngle rotationNode.cos(); const sinAngle rotationNode.sin(); const rotationMatrix mat2( cosAngle, sinAngle, sinAngle.negate(), cosAngle ); return rotationMatrix.mul( positionNode ); }当输入是vec2例如 UV 坐标、Sprite 的二维局部偏移时对作为float的rotationNode调用.cos()/.sin()得到该旋转角对应的三角函数节点用cos与sin组成一个 2×2 旋转矩阵mat2用该矩阵与positionNode做矩阵乘得到旋转后的二维坐标。由于矩阵中的正、余弦项都由节点表示因此角度本身可以是随时间动态变化的节点表达式例如time、实例化缓冲属性等实现“每一帧角度都在变”的动画。4.2 三维路径欧拉角与矩阵链当输入为vec3/vec4或其他非vec2类型时进入 3D 分支此时rotationNode被当作一个欧拉角节点使用其.x/.y/.z分别表示绕 X/Y/Z 轴的旋转量弧度const rotation rotationNode; const order this._order; const rotationXMatrix mat4( vec4( 1.0, 0.0, 0.0, 0.0 ), vec4( 0.0, cos( rotation.x ), sin( rotation.x ), 0.0 ), vec4( 0.0, sin( rotation.x ).negate(), cos( rotation.x ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); const rotationYMatrix mat4( vec4( cos( rotation.y ), 0.0, sin( rotation.y ).negate(), 0.0 ), vec4( 0.0, 1.0, 0.0, 0.0 ), vec4( sin( rotation.y ), 0.0, cos( rotation.y ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); const rotationZMatrix mat4( vec4( cos( rotation.z ), sin( rotation.z ), 0.0, 0.0 ), vec4( sin( rotation.z ).negate(), cos( rotation.z ), 0.0, 0.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); const matrixMap { X: rotationXMatrix, Y: rotationYMatrix, Z: rotationZMatrix }; const matrixChain matrixMap[ order.charAt( 0 ) ] .mul( matrixMap[ order.charAt( 1 ) ] ) .mul( matrixMap[ order.charAt( 2 ) ] ); return matrixChain.mul( vec4( positionNode, 1.0 ) ).xyz;该分支的关键实现要点三个基础 4×4 旋转矩阵分别针对 X、Y、Z 轴构建使用cos/sin填充对应元素负正弦项决定旋转方向顺序可配置将三个矩阵放入matrixMap按_order字符串的每个字符order.charAt(0/1/2)取出对应轴矩阵依序连乘得到matrixChain。默认顺序XYZ即执行X.mul(Y).mul(Z)升维与降维先将 3D 坐标补成齐次形式vec4( positionNode, 1.0 )参与矩阵乘最后取.xyz还原为三维向量输出。可以看出旋转矩阵的复合与展开全部发生在节点构建期实际生成的着色器代码里只会出现最终的矩阵乘表达式配合TempNode的缓存机制不会产生多余开销。五、缓存键与序列化保证相同 Euler 顺序的节点可复用5.1 customCacheKey把顺序纳入缓存键RotateNode重写了customCacheKey()src/nodes/utils/RotateNode.jscustomCacheKey() { return hashString( this._order ); }从源码推断three.js 节点系统在缓存/复用临时节点时会计算自定义缓存键。此处将 Euler 旋转顺序字符串hashString化为缓存键的一部分是为了避免“顺序不同但其他结构相同的两个旋转节点”在节点缓存中被错误地混用确保缓存命中与计算结果一致。5.2 serialize / deserialize跨会话保留顺序serialize( data ) { super.serialize( data ); data.order this._order; } deserialize( data ) { super.deserialize( data ); this._order data.order; }节点序列化如用于 TSL 编辑器/图的持久化时会把_order一并写入data.order反序列化时再恢复。这样即使重建节点对象欧拉顺序也不会丢失。六、源码与官方示例中的真实应用rotate在 three.js 的源码与示例中被广泛使用是理解其 API 语义最直接的佐证。6.1rotateUV绕任意中心点旋转 UVsrc/nodes/utils/UVUtils.js 中基于rotate封装了rotateUV/** * Rotates the given uv coordinates around a center point */ export const rotateUV /*__PURE__*/ Fn( ( [ uv, rotation, center vec2( 0.5 ) ] ) { return rotate( uv.sub( center ), rotation ).add( center ); } );实现思路即经典的“平移→旋转→平移回原中心”三步先把 UV 平移到以center为原点默认vec2(0.5)即纹理中心调用rotate()旋转再平移回原位置。此处uv是vec2、rotation是float走的正是上述 2D 矩阵路径。rotateUV同样在 src/Three.TSL.js 中导出。6.2 Sprite / Points 节点材质的二维旋转Sprite 节点材质在 src/materials/nodes/SpriteNodeMaterial.js 中alignedPositionSprite 的二维对齐位置与旋转量rotation一起传入const rotatedPosition rotate( alignedPosition, rotation ); return vec4( mvPosition.xy.add( rotatedPosition ), mvPosition.zw );这正是 2D 输入路径旋转一个vec2偏移后叠加到裁剪空间位置。Points 节点材质在 src/materials/nodes/PointsNodeMaterial.js 中同样有offset rotate( offset, rotation )的调用用于点精灵的旋转。6.3 WebGPU 示例实例化粒子的 3D 自旋在官方示例 examples/webgpu_layers.html 中TSL 片段把“每个实例的欧拉角”作为旋转量传入实现逐实例旋转动画import { ... , rotate, ... } from three/tsl; // ... const localTime instancedBufferAttribute( timeAttribute ).add( time.mul( 0.02 ) ); const modTime mod( localTime, 1.0 ); const rotatedPosition rotate( positionLocal, instanceRotation.mul( modTime.mul( 20 ) ) ); material.positionNode rotatedPosition.add( instancePosition ).add( instanceDirection.mul( modTime.mul( 50 ) ) );这里positionLocal为局部位置、instanceRotation是每个实例的欧拉角缓冲属性vec3随时间增长并取模后作为旋转量属于完整的 3D 欧拉角旋转路径。旋转结果再叠加实例位置与方向偏移形成层叠飞行的粒子效果。6.4 WebGPU 示例对坐标网格做 45° 旋转另一个官方示例 examples/webgpu_tsl_halftone.html 将rotate用在屏幕坐标的 UV 化处理上let gridUv screenCoordinate.xy.div( screenSize.yy ).mul( count ); gridUv rotate( gridUv, Math.PI * 0.25 ).mod( 1 );对二维gridUv旋转Math.PI * 0.2545°后再取模得到斜向的网点halftone排列——这证明rotate不止能作用于“位置”也适合对任意二维坐标/UV 做变换。七、使用要点与注意事项综合源码实现与上述用法可总结如下实践要点输入类型决定旋转语义positionNode为vec2时rotationNode传float弧度为vec3/vec4时rotationNode传包含x/y/z分量的欧拉角节点。类型判断由generateNodeType→setup自动完成无需手动切换 API。旋转角度的单位是弧度如示例中直接使用Math.PI * 0.25表示 45°动态角度用time、modTime等节点表达式驱动即可形成旋转动画。动态旋转优于预计算由于矩阵内是cos/sin节点角度可以在每一帧变化适合与instancedBufferAttribute、uniform、time组合实现逐实例、逐帧的旋转。3D 路径注意 Euler 顺序默认XYZ若几何体出现异常翻转或需要匹配既有约定可通过构造函数第三参或.setOrder(ZYX)等调整。customCacheKey已把顺序纳入缓存键同一节点实例变更顺序后也能得到正确结果。结果仍是节点可继续参与链式运算rotate(...)返回的是节点对象可以继续.add()、.mul()、.mod()也可以赋值给material.positionNode、与context等机制组合rotateUV就是链式封装的典型。绕任意中心旋转采用“先平移后旋转”仅对位置绕原点旋转时直接使用rotate(position, angle)需要绕非原点中心如纹理中心旋转时参考rotateUV的“减中心→旋转→加中心”模式。八、延伸阅读节点文档docs/pages/RotateNode.html本文依据的原始 API 文档含继承链、构造、属性、方法说明源码实现src/nodes/utils/RotateNode.js完整类定义、setup两条旋转路径、rotate工厂导出基类src/nodes/core/TempNode.js临时节点缓存机制、src/nodes/core/Node.js相关工具src/nodes/utils/UVUtils.jsrotateUV/spherizeUV、src/nodes/tsl/TSLBase.js应用实例examples/webgpu_layers.html、examples/webgpu_tsl_halftone.html、src/materials/nodes/SpriteNodeMaterial.js、src/materials/nodes/PointsNodeMaterial.js【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考