You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Java的Minecraft自定义天空渲染:四元数旋转向量匹配异常问题排查

解决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=0axialTilt=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

火山引擎 最新活动