我有一个圆形的形状对象,我希望它沿着它自己的轴像扇子一样旋转。
我可以使用我的变换矩阵改变任何方向的旋转,即dx, dy, dz
。
以下是代码:
Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
Matrix4f.translate(translation, matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(rx), new Vector3f(1,0,0), matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(ry), new Vector3f(0,1,0), matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(rz), new Vector3f(0,0,1), matrix, matrix);
Matrix4f.scale(new Vector3f(scale,scale,scale), matrix, matrix);
我的顶点代码:
vec4 worldPosition = transformationMatrix * vec4(position,1.0);
vec4 positionRelativeToCam = viewMatrix*worldPosition;
gl_Position = projectionMatrix *positionRelativeToCam;
Main Game Loop:
Object.increaseRotation(dxf,dyf,dzf);
但是,它不是沿着它自己的轴旋转。我在这里错过了什么? 我想要这样的东西。请帮忙
答案 0 :(得分:2)
你应该摆脱欧拉角。
对象/网格几何
您需要了解对象在其本地空间中的定位方式。例如,我们假设:
因此,在这种情况下,主旋转围绕轴z
。如果定义了网格,则旋转轴未与任何轴(x,y
或z
)对齐,或者中心点不是(0,0,0)
,则会导致问题。补救措施是更改网格几何体或创建一个特殊的常量变换矩阵M0
,它将所有顶点从网格 LCS (局部坐标系)转换为轴对齐和中心的另一个顶点旋转的轴在轴上也是零,也是旋转轴。
在后一种情况下,对象矩阵M
的任何操作都将如下所示:
M'=M.M0.operation.Inverse(M0)
或反向或反向(取决于矩阵/顶点乘法和行/列顺序约定)。如果您的网格已经居中并且轴对齐,那么请改为:
M'=M.operation
operation
是变化增量的变换矩阵(例如旋转矩阵)。 M
是来自#2 的对象当前转换矩阵,而M'
是应用operation
后的新版本。
对象转换矩阵
您需要为每个对象提供单个Transform矩阵。这将保持对象的位置和方向 LCS ,以便可以将其转换为世界/场景 GCS (全局坐标系)或其父对象 LCS 强>
围绕其局部旋转轴旋转对象
正如在Understanding 4x4 homogenous transform matrices中提到的标准OpenGL矩阵对流一样,你需要这样做:
M'=M*rotation_matrix
其中M
是当前对象变换矩阵,M'
是旋转后的新版本。这是你有所不同的事情。您正在使用欧拉角rx,ry,rz
而不是逐步累积旋转。你不能以任何理智和健全的方式用欧拉角做到这一点!即使许多现代游戏和应用程序仍在努力做到这一点(多年来都失败了)。
那么如何摆脱欧拉角:
每个对象必须具有持久性/全局/静态矩阵M
而不是每个渲染的本地实例,因此您需要初始化一次,而不是每帧清除它。
在您需要的动画更新应用操作
这样:
M*=rotation_around_z(angspeed*dt);
angspeed
位于您的粉丝速度的[rad/second]
或[deg/second]
,而dt
的时间已经过[seconds]
。例如,如果您在计时器中执行此操作,则dt
是计时器间隔。对于可变时间,您可以测量经过的时间(它取决于平台,我通常使用PerformanceTimers或RDTSC)。
您可以在自身之上堆叠更多操作(例如,您的风扇也可以绕y
轴转回和转发以覆盖更多区域。
对于对象直接控制(通过键盘,鼠标或操纵杆),只需添加以下内容:
if (keys.get( 38)) { redraw=true; M*=translate_z(-pos_speed*dt); }
if (keys.get( 40)) { redraw=true; M*=translate_z(+pos_speed*dt); }
if (keys.get( 37)) { redraw=true; M*=rotation_around_y(-turn_speed*dt); }
if (keys.get( 39)) { redraw=true; M*=rotation_around_y(+turn_speed*dt); }
keys
是键盘中每个键保持开/关状态的X,Y,Z
(所以我可以一次使用更多键)。此代码仅使用箭头控制对象。有关该主题的更多信息,请参阅相关的质量保证:
保持准确性
随着增量更改,由于浮点错误导致精度下降。因此,在矩阵类中添加一个计数器,计算它已被更改的次数(应用增量操作)以及是否有一些常量计数命中(例如128次操作)规范化矩阵。
为此,您需要确保矩阵的正交性。因此,eaxh轴向量Z
必须垂直于其他两个,其大小必须是单位。我是这样做的:
Z = Z/|Z|
轴,因为它通常是我的网格中的主轴(查看方向,旋转轴等)。所以只需制作此向量单元X = (+/-) Z x Y
Y = (+/-) Z x X
和X = X/|X|
,并将Y = Y/|Y|
和(+/-)
标准化。 void reper::orto(int test)
{
double x[3],y[3],z[3];
if ((cnt>=_reper_max_cnt)||(test)) // here cnt is the operations counter and test force normalization regardless of it
{
use_rep(); // you can ignore this
_rep=1; _inv=0; // you can ignore this
axisx_get(x);
axisy_get(y);
axisz_get(z);
vector_one(z,z);
vector_mul(x,y,z); // x is perpendicular to y,z
vector_one(x,x);
vector_mul(y,z,x); // y is perpendicular to z,x
vector_one(y,y);
axisx_set(x);
axisy_set(y);
axisz_set(z);
cnt=0;
}
}
是因为我不知道你的坐标系约定而且十字积可以产生与原始方向相反的向量,所以如果方向相反则改变乘法顺序或否定结果(这是在编码时完成的)不在运行时!)。这里的示例在 C ++ 中我的正交规范化是如何完成的:
axis?_get/set(a)
其中a
只是将vector_one(a,b)
作为轴/从您的矩阵获取/设置。 a = b/|b|
返回vector_mul(a,b,c)
和a = b x c
返回{{1}}