如何旋转球体UV纹理且避免出现倾斜问题
问题分析
你的核心问题是:同时旋转pitch和yaw时球体UV纹理出现倾斜,但单独旋转其中一个参数时表现正常。这是因为当前实现通过旋转整个球体交点来计算UV,会改变纹理极点的朝向,导致纹理顶部不再对齐全局z轴(向上方向)。
你需要的效果是:无论球体/纹理正面朝向哪个方向,纹理顶部始终保持全局向上,这需要构建一个以全局向上为基准的自定义UV坐标系,而非直接旋转交点。
解决方案
正确思路是:基于目标正面方向和全局向上方向构建正交坐标系,将球体交点转换到该坐标系后再计算UV,确保纹理的v分量(纬度)始终沿全局向上的经线分布。
实现步骤
- 计算目标正面方向:根据pitch和yaw生成单位向量,代表纹理需要朝向的方向。
- 构建正交基向量:
- 右侧向量:由全局向上向量叉乘正面向量得到,保证水平方向正确性。
- 局部向上向量:由正面向量叉乘右侧向量得到,确保与全局向上对齐(避免倾斜)。
- 将交点转换到该正交坐标系下,用转换后的坐标计算UV。
修改后的代码
use nalgebra::{Point3, Vector3, Unit}; pub fn get_unit_sphere_uv(intersection_point: Point3) -> (f64, f64) { const PI: f64 = std::f64::consts::PI; const TAU: f64 = std::f64::consts::TAU; let pitch = PI / 4.0; // 45度俯仰 let yaw = PI / 4.0; // 45度偏航 let roll = 0.0; // 保持滚转为0 // 1. 计算目标正面方向(原默认正面为-z方向,根据pitch和yaw旋转) let forward = Vector3::new( yaw.sin() * pitch.cos(), -pitch.sin(), -yaw.cos() * pitch.cos(), ) .normalize(); // 2. 构建正交基:确保局部向上对齐全局向上 let global_up = Vector3::z_axis(); let right = Unit::new_normalize(global_up.cross(&forward)); let up = Unit::new_normalize(forward.cross(&right)); // 3. 将交点转换到自定义坐标系中 let point_vec = intersection_point - Point3::origin(); let x = right.dot(&point_vec); let y = up.dot(&point_vec); let z = -forward.dot(&point_vec); // 对应原逻辑中的-z,保持theta计算一致 // 4. 用转换后的坐标计算UV let theta = z.acos(); let phi = y.atan2(x) + PI; let u = phi / TAU; let v = theta / PI; (u, v) } // 原角度计算函数可保留,也可直接整合到上述逻辑中 // fn get_unit_sphere_angles(intersection_point: Point3) -> (f64, f64) { // let theta = (-intersection_point.z).acos(); // let phi = f64::atan2(intersection_point.y, intersection_point.x) + PI; // (theta, phi) // }
关键说明
- 放弃直接旋转交点的方式,通过正交基构建自定义坐标系,确保纹理顶部始终绑定全局z轴。
- 正面方向
forward由pitch和yaw推导,符合相机朝向逻辑(yaw绕z轴转,pitch绕y轴转)。 - 正交基通过叉乘保证向量两两垂直,
up向量始终与全局向上对齐,从根源避免纹理倾斜。
测试验证
当pitch和yaw同时设为45度时,纹理顶部会保持全局向上,不会出现之前的向左倾斜问题;单独调整pitch或yaw时,纹理会正常响应方向变化且顶部不变。
内容的提问来源于stack exchange,提问作者thabnir
相关产品推荐
相关产品推荐

