目录
一、节点功能概述
二、端口详解
控制选项
三、技术原理解析
3.1 数学基础:罗德里格斯旋转公式
3.2 旋转矩阵构造
3.3 生成代码解析
1. 弧度模式(Radians)
2. 度模式(Degrees)
3.4 旋转方向:右手定则
四、应用场景与实战案例
4.1 角色骨骼旋转(动画驱动)
场景:实现角色手臂绕肱骨(上臂骨)旋转,模拟弯曲动作
4.2 相机环绕效果(第三人称视角)
场景:让相机绕目标物体(如角色)的 Y 轴旋转,实现环绕观察
4.3 粒子运动轨迹(螺旋线)
场景:让粒子绕某一轴做螺旋运动,模拟星云、涡流效果
4.4 地形变形(绕轴扭曲)
场景:让地形沿某一倾斜轴旋转扭曲,模拟地震或魔法效果
五、使用技巧与注意事项
5.1 旋转轴的归一化
5.2 角度单位的选择
5.3 旋转方向的控制
5.4 性能考量
5.5 与其他旋转节点的对比
六、总结与拓展应用
绕轴旋转节点(Rotate About Axis Node)是 Unity Shader Graph 中用于实现三维向量绕指定轴旋转的核心工具。它接收一个三维向量(In)、一个旋转轴(Axis)和一个旋转角度(Rotation),返回旋转后的向量(Out)。旋转角度的单位可通过参数(Unit)切换为弧度(Radians)或度(Degrees),满足不同场景的使用习惯。
该节点的核心特性在于:
端口名称 | 方向 | 类型 | 描述 |
---|---|---|---|
In | 输入 | Vector 3 | 待旋转的三维向量(如顶点位置、方向向量) |
Axis | 输入 | Vector 3 | 旋转轴向量(任意方向,会被自动归一化) |
Rotation | 输入 | Float | 旋转角度(单位由 Unit 参数指定) |
Out | 输出 | Vector 3 | 旋转后的三维向量 |
控制名称 | 类型 | 可选值 | 描述 |
---|---|---|---|
Unit | 下拉菜单 | Radians / Degrees | 旋转角度的单位(弧度 / 度) |
绕轴旋转的核心是通过旋转矩阵实现向量变换,节点基于罗德里格斯旋转公式构建矩阵,公式如下:其中:
根据罗德里格斯公式,节点生成的旋转矩阵展开为:其中k_x, k_y, k_z是旋转轴
的分量(已归一化)。
节点根据角度单位(Unit)生成两种代码,核心差异在于是否需要将角度从度转换为弧度:
hlsl
void Unity_RotateAboutAxis_Radians_float(float3 In, float3 Axis, float Rotation, out float3 Out)
{
float s = sin(Rotation); // 正弦值(旋转角度已为弧度)
float c = cos(Rotation); // 余弦值
float one_minus_c = 1.0 - c; // 1 - cosθ
Axis = normalize(Axis); // 旋转轴归一化
// 构造旋转矩阵(基于罗德里格斯公式)
float3x3 rot_mat =
{ one_minus_c * Axis.x * Axis.x + c, one_minus_c * Axis.x * Axis.y - Axis.z * s, one_minus_c * Axis.z * Axis.x + Axis.y * s,
one_minus_c * Axis.x * Axis.y + Axis.z * s, one_minus_c * Axis.y * Axis.y + c, one_minus_c * Axis.y * Axis.z - Axis.x * s,
one_minus_c * Axis.z * Axis.x - Axis.y * s, one_minus_c * Axis.y * Axis.z + Axis.x * s, one_minus_c * Axis.z * Axis.z + c
};
Out = mul(rot_mat, In); // 向量与旋转矩阵相乘,得到旋转后向量
}
hlsl
void Unity_RotateAboutAxis_Degrees_float(float3 In, float3 Axis, float Rotation, out float3 Out)
{
Rotation = radians(Rotation); // 先将度转换为弧度
float s = sin(Rotation);
float c = cos(Rotation);
float one_minus_c = 1.0 - c;
Axis = normalize(Axis);
float3x3 rot_mat = // 后续矩阵构造与弧度模式完全一致
{ one_minus_c * Axis.x * Axis.x + c, one_minus_c * Axis.x * Axis.y - Axis.z * s, one_minus_c * Axis.z * Axis.x + Axis.y * s,
one_minus_c * Axis.x * Axis.y + Axis.z * s, one_minus_c * Axis.y * Axis.y + c, one_minus_c * Axis.y * Axis.z - Axis.x * s,
one_minus_c * Axis.z * Axis.x - Axis.y * s, one_minus_c * Axis.y * Axis.z + Axis.x * s, one_minus_c * Axis.z * Axis.z + c
};
Out = mul(rot_mat, In);
}
旋转方向遵循右手定则:右手拇指指向旋转轴(Axis)的正方向,四指弯曲的方向即为旋转方向(逆时针为正角度)。
hlsl
// 肱骨方向(旋转轴,从肩部到肘部的向量)
float3 humerusAxis = normalize(_ElbowPos - _ShoulderPos);
// 动画参数:旋转角度(度,0-90度对应弯曲程度)
float bendAngle = _BendParameter * 90.0;
// 手臂顶点的初始位置(相对于肩部)
float3 armVertex = IN.vertex.xyz - _ShoulderPos;
// 绕肱骨轴旋转顶点
float3 rotatedVertex = RotateAboutAxisNode.Out; // In=armVertex, Axis=humerusAxis, Rotation=bendAngle, Unit=Degrees
// 旋转后位置 = 肩部位置 + 旋转后的顶点相对位置
o.vertex.xyz = _ShoulderPos + rotatedVertex;
hlsl
// 目标物体位置(旋转中心)
float3 targetPos = _TargetWorldPos;
// 相机到目标的初始方向(半径固定)
float3 cameraOffset = _CameraDistance * float3(1, 0, 0); // 初始在目标右侧
// 旋转轴:世界Y轴(垂直向上)
float3 rotateAxis = float3(0, 1, 0);
// 旋转角度:随鼠标X轴输入变化(度/帧)
float rotateAngle = _MouseXInput * _Sensitivity;
// 旋转相机偏移量
float3 rotatedOffset = RotateAboutAxisNode.Out; // In=cameraOffset, Axis=rotateAxis, Rotation=rotateAngle, Unit=Degrees
// 相机新位置 = 目标位置 + 旋转后的偏移
float3 newCameraPos = targetPos + rotatedOffset;
// 更新相机视图矩阵(简化示例)
_WorldSpaceCameraPos = newCameraPos;
hlsl
// 粒子初始方向(沿Z轴)
float3 particleDir = float3(0, 0, 1);
// 旋转轴:沿Z轴(螺旋中心轴)
float3 spiralAxis = float3(0, 0, 1);
// 旋转角度:随粒子生命周期递增(每移动1单位距离旋转180度)
float travelDistance = length(IN.vertex.xyz - _EmitterPos);
float spiralAngle = travelDistance * 180.0; // 度
// 旋转粒子方向
float3 rotatedDir = RotateAboutAxisNode.Out; // In=particleDir, Axis=spiralAxis, Rotation=spiralAngle, Unit=Degrees
// 粒子新位置 = 发射位置 + 旋转方向 * 距离
o.vertex.xyz = _EmitterPos + rotatedDir * travelDistance;
hlsl
// 扭曲轴:沿XZ平面的对角线(如(1,0,1)方向)
float3 twistAxis = normalize(float3(1, 0, 1));
// 扭曲强度:距离扭曲中心越远,旋转角度越大(弧度)
float distanceToCenter = length(IN.worldPos.xz - _TwistCenter.xz);
float twistAngle = distanceToCenter * _TwistStrength; // 弧度
// 地形顶点的初始位置
float3 terrainVertex = IN.worldPos;
// 绕扭曲轴旋转顶点
float3 twistedVertex = RotateAboutAxisNode.Out; // In=terrainVertex, Axis=twistAxis, Rotation=twistAngle, Unit=Radians
// 应用扭曲变形
o.vertex.xyz = twistedVertex;
虽然节点内部会对旋转轴(Axis)进行归一化(Axis = normalize(Axis)
),但建议在输入前手动归一化,避免因轴向量模长异常导致的计算误差(尤其是动态生成的轴向量):
hlsl
float3 safeAxis = normalize(dynamicAxis); // 提前归一化,更可靠
_Time.y * 2π
直接对应一圈)。若需反向旋转(顺时针),可对角度取负:
hlsl
float reverseAngle = -originalAngle; // 顺时针旋转
旋转矩阵的计算包含多次乘法、三角函数(sin/cos)和矩阵乘法,属于中高开销操作。在片段着色器中高频使用时(如每像素旋转),建议:
节点 | 核心逻辑 | 适用场景 |
---|---|---|
Rotate About Axis | 绕任意轴旋转 | 复杂 3D 旋转(如骨骼、相机) |
Rotate Around | 绕指定点 + 轴旋转(含平移) | 环绕目标旋转(如卫星运动) |
Transform | 基于模型 / 世界矩阵的整体旋转 | 模型空间到世界空间的变换 |
绕轴旋转节点通过罗德里格斯旋转公式,为三维向量绕任意轴的旋转提供了精准工具,其核心价值在于:
拓展方向: