我有一个OpenGL可视化器(使用OpenTK)。它应该以PLY格式打开一个网格文件,并显示它。用户可以围绕对象移动、旋转或缩放它。最后,用户应该能够将网格保存到PLY文件中,从他/她在保存之前看到的完全相同的视图。
我已经将网格对象抽象成一个类。下面是类(sort of)的样子:
public class Mesh {
Vector3d vertices[];
int[] triangleIndices;
Matrix4d translation = Matrix4d.Identity();
Matrix4d rotation = Matrix4d.Identity();
Matrix4d scale = Matrix4d.Identity();
int vboHandle;
int faceHandle;
public void Draw() {
GL.PushMatrix();
/**
* Apply transformations
*/
// Do VBO drawing stuff
// ...
GL.PopMatrix();
}
}
我正在做的是将PLY文件加载为VBO。当要绘制网格时,我首先应用平移,应用旋转(通过找到对象的质心,平移坐标系统,旋转并向后平移),然后(以与旋转相同的方式)应用缩放。
这工作得很好,可视化器正确绘制网格。但是,当我想保存时,我不知道如何对所有顶点应用相同的转换集并编写PLY文件。
我试着这样做:(非常模仿我上面的Draw
方法):
for(int i = 0 ; i <this.vertices.Length; i++)
{
// Apply the transformations
Vector3d transformed = Vector3d.Transform(this.vertices[i], this.translation);
// Handle rotation
Matrix4d centerTrans = Matrix4d.CreateTranslation(Center);
transformed = Vector3d.Transform(transformed, centerTrans);
transformed = Vector3d.Transform(transformed, this.rotation);
centerTrans = Matrix4d.CreateTranslation(-Center);
transformed = Vector3d.Transform(transformed, centerTrans);
// Handle scale
centerTrans = Matrix4d.CreateTranslation(Center);
transformed = Vector3d.Transform(transformed, centerTrans);
transformed = Vector3d.Transform(transformed, this.scale);
centerTrans = Matrix4d.CreateTranslation(-Center);
transformed = Vector3d.Transform(transformed, centerTrans);
result[i] = transformed;
}
但问题是输出有点偏离(特别是当对象被旋转时)。
我的主要问题是如何解决这个问题,以及解决这个问题的正确方法是什么。
p。我知道我的方法可能是过时的,幼稚的,错误的(考虑到OpenGL的最佳实践),但我正在创建这个可视化工具来解决一个完全不相关的更大的问题。所以在某种程度上,我是在为自己的目的而黑东西。对于解决这个问题的好方法,任何建议都是非常感谢的。
PS2。多次Matrix4d
变换的原因是我总是希望旋转应用到对象的中心,我不知道有任何其他方法可以达到相同的效果。
需要注意的一点是转换的顺序。当你使用旧的OpenGL矩阵堆栈指定转换时,它们以反向顺序应用于顶点。
例如,如果你有这样的序列:
glTranslatef(...);
glRotatef(...);
这意味着首先旋转顶点,然后平移。
虽然这种行为乍一看似乎违反直觉,但实际上比相反的方式要有用得多。例如,如果使用视图矩阵,则在帧的开始处指定它。然后在绘制对象时指定模型转换。但是视图转换需要在模型转换之后应用,即使它是首先指定的。
类似地,如果你有一个对象的层次结构,你经常想要指定适用于所有对象的"全局"转换,然后当你在层次结构中绘制对象时指定"局部"转换。但是,局部转换需要在全局转换之前应用。
基于此,如果你在自己的代码中一个接一个地应用转换,你将不得不以与你为OpenGL渲染指定它们时使用的顺序相反的顺序应用它们。
选择
有几种可能的替代方法:
你可以得到当前的变换矩阵,然后把它应用到你的顶点:
GLfloat mat[16]; glGetFloatv(GL_MODELVIEW_MATRIX, mat);
只有在渲染帧时使用才有效。如果您希望在呈现之外应用转换,那么您的方法更可行。此外,它让你更深入地使用遗留矩阵堆栈,这意味着你有更多的工作,如果你曾经想携带你的代码到OpenGL核心配置文件,或将其移植到OpenGL ES。
正如@Ike在上面的评论中建议的那样,您可以更改代码以首先组合矩阵,然后将组合矩阵应用于您的顶点。如果你有很多使用相同转换序列的顶点,这将更有效。
OpenGL有一个叫做"转换反馈"的功能,它允许你从图形管道中得到转换后的坐标。我不认为它最适合你的用例,但无论如何你可能想要阅读它。