基于Java的Minecraft自定义天空渲染:四元数旋转向量匹配异常问题排查
看起来你已经在数学逻辑上做了充分验证(Mathematica动画是个很棒的调试手段!),但问题出在向量方向的匹配和渲染逻辑的坐标系对齐上。我帮你拆解几个核心问题和修正方案:
1. 自转向量的初始方向搞反了
你的代码里,观察者的自转向量初始是Vector3f.YN(Y负方向,指向地面),但注释里说这是“指向地面正上方”——这里明显矛盾!Minecraft中观察者的头顶方向是Vector3f.YP(Y正方向),地面才是YN。
你Mathematica里的黑色向量是观察者的自转方向(头顶),所以必须把初始rotVec改成YP:
Vector3f rotVec = copyVector(Vector3f.YP); // 替换原来的YN
这会直接修正四元数计算的基准方向,避免后续所有旋转都基于错误的初始向量。
2. 目标向量搞反了:太阳在行星的反方向
你的posVec计算的是行星相对于太阳的位置(从太阳指向行星的向量),但我们需要的是太阳相对于行星的位置(从行星指向太阳的向量)——也就是-posVec!
这解释了为什么你的Mathematica动画里蓝色向量指向其他行星,但太阳渲染时位置不对:你现在的四元数是把自转向量转到“行星朝向太阳的反方向”,自然太阳会出现在相反的位置。
修正四元数计算部分,把posVec替换为它的反方向:
// 新增一个取反向量的工具方法 public static Vector3f negateVector(Vector3f vec) { return new Vector3f(-vec.x(), -vec.y(), -vec.z()); } // 替换原来的cross和dot计算 Vector3f sunDirection = negateVector(posVec); Vector3f cross = crossVectors(rotVec, sunDirection); float dot = dotVectors(rotVec, sunDirection);
3. 验证四元数旋转的应用逻辑
Minecraft的matrixStack.mulPose(quat)是将后续的渲染对象绕当前坐标系旋转。你的目标是把初始在Y正上方的太阳,旋转到sunDirection指向的天空位置——修正了前两点后,这个旋转逻辑就和你的数学验证对齐了。
快速测试方案
先简化参数,排除变量干扰:
- 设置
orbitalTilt=0,axialTilt=0 - 设置
year为极大值(比如overworldYear = overworldSolarDay * 10000),让行星公转暂时静止 - 设置
siderealDay=24000,和原版同步
这时候太阳应该和原版一样,从东升起、西落下,正午在头顶。如果这个场景正常,再逐步加入轨道倾角和自转轴倾角,验证每个参数的影响。
4. 额外注意:四元数的边界情况处理
你的代码里处理了dot < -0.999999F的情况(向量完全相反),但当前的默认旋转是绕X轴转180度,这可能在某些坐标系下不符合预期。可以改成基于垂直于rotVec的任意轴旋转,比如:
if (dot < -0.999999F) { // 找一个和rotVec垂直的向量作为旋转轴 Vector3f axis = rotVec.x() != 0 || rotVec.y() != 0 ? new Vector3f(-rotVec.y(), rotVec.x(), 0) : new Vector3f(0, -rotVec.z(), rotVec.y()); axis = normalizeVector(axis); quat = rotation(axis, 180.0F, useDegrees); }
这会避免在某些极端情况下出现异常旋转。
修正后的核心代码片段
调整后的centralSunQuaternion方法关键部分:
// 计算太阳相对于行星的方向 Vector3f sunDirection = negateVector(posVec); sunDirection = normalizeVector(sunDirection); // 计算从自转向量到太阳方向的四元数 Vector3f cross = crossVectors(rotVec, sunDirection); float dot = dotVectors(rotVec, sunDirection); float r = dot + 1.0F; Quaternion quat = Quaternion.ONE; if (dot < -0.999999F) { Vector3f axis = rotVec.x() != 0 || rotVec.y() != 0 ? new Vector3f(-rotVec.y(), rotVec.x(), 0) : new Vector3f(0, -rotVec.z(), rotVec.y()); axis = normalizeVector(axis); quat = rotation(axis, 180.0F, useDegrees); } else if (dot < 0.999999F) { quat = new Quaternion(cross.x(), cross.y(), cross.z(), r); } quat = normalizeQuaternion(quat); return quat;
按照这些步骤修正后,你的四元数系统应该能和渲染逻辑正确对齐,太阳会出现在天空中的预期位置。
内容的提问来源于stack exchange,提问作者Floydman




