【问题标题】:Save an OpenGL mesh from the current viewing point从当前视点保存 OpenGL 网格
【发布时间】:2015-05-24 00:21:06
【问题描述】:

我有一个 OpenGL 可视化工具(使用 OpenTK)。它应该以 PLY 格式打开一个网格文件,并显示它。用户能够在对象周围移动、旋转或缩放它。最后,用户应该能够从他/她在保存之前看到的完全相同的视图将网格保存到 PLY 文件中。

我已将网格对象抽象为一个类。这是类(有点)的样子:

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;
}

但问题是输出有点偏离(尤其是在对对象应用旋转时)。

我的主要问题是如何解决这个问题以及解决这个问题的正确方法是什么。

PS。我知道我的方法可能是过时的、幼稚的和错误的整个网格渲染事情(并考虑到 OpenGL 最佳实践),但我正在创建这个可视化工具来解决一个完全不相关且更大的问题。所以在某种程度上,我是为了我的目的而破解东西。非常感谢任何有关解决此问题的好方法的建议)。

PS2。多次Matrix4d 变换的原因是我总是希望将旋转应用于对象的中心,而我不知道任何其他方法可以达到相同的效果。

【问题讨论】:

  • 您可能需要颠倒转换的顺序。请记住,当您在 OpenGL 中指定变换时,它们会按照您指定它们的相反顺序应用到顶点。因此,如果您自己将它们一个一个应用到向量,则与指定它们时使用的顺序相反用于 OpenGL。
  • 你感兴趣的是unprojection从世界坐标到屏幕坐标(包括屏幕深度)。已经有一个名为 gluUnproject 的函数可以精确地执行此操作,但如果您有兴趣滚动自己的投影/非投影函数,那么这就是您要搜索的内容。
  • @RetoKoradi 哦,这解决了我的问题!谢谢!介意将其发布为答案,以便我接受吗?另外,你能简要解释一下这种行为吗?我没有在任何地方读到任何关于它的东西......
  • @Ike 我知道 gluUnproject 但这似乎不适合我的目的。请注意,我已经知道我的对象所在的位置,我不想将其屏幕坐标转换为世界坐标。
  • 我现在明白了——是的,除了翻转的转换顺序之外,如果不是将同一个顶点转换 7 次,而是通过以下方式连接转换,您可能会简化一些事情并节省一些计算时间将循环外的所有矩阵相乘成一个单一的、包罗万象的变换矩阵。然后你的内部循环逻辑只包括通过这个矩阵转换每个顶点,如果有些东西变得时髦,你可以在循环外摆弄矩阵逻辑(比如颠倒乘法的顺序)。

标签: opengl rendering mesh opentk


【解决方案1】:

需要注意的一个方面是转换的顺序。当您使用旧版 OpenGL 矩阵堆栈指定变换时,它们将按指定顺序的逆序应用到顶点。

例如,如果你有这个序列:

glTranslatef(...);
glRotatef(...);

这意味着顶点首先旋转,然后平移。

虽然这种行为一开始可能看起来违反直觉,但实际上它比相反的方式有用得多。例如,如果您使用视图矩阵,则在帧的开头指定它。然后在绘制对象时指定模型转换。但是视图转换需要模型转换之后应用,即使它是先指定的。

同样,如果您有对象层次结构,您通常希望指定适用于所有对象的“全局”转换,然后在层次结构中绘制对象时指定“局部”转换。但同样,需要在全局转换之前应用局部转换。

基于此,如果您在自己的代码中一一应用转换,则必须按照与为 OpenGL 渲染指定它们时使用的顺序相反的顺序应用它们。

替代方案

您目前使用的有几种可能的替代方案:

  • 您可以获得当前的变换矩阵,然后将其应用于您的顶点:

    GLfloat mat[16];
    glGetFloatv(GL_MODELVIEW_MATRIX, mat);
    

    只有在渲染帧时使用它才能正常工作。如果您想在渲染之外应用转换,您的方法更可行。此外,它还可以让您更深入地使用传统矩阵堆栈,这意味着如果您想将代码带入 OpenGL 核心配置文件或将其移植到 OpenGL ES,您需要做更多的工作。

  • 正如@Ike 在上面的评论中所建议的,您可以将代码更改为首先组合矩阵,然后将组合矩阵应用于您的顶点。如果您有大量使用相同变换序列的顶点,这会更有效。

  • OpenGL 有一个称为“变换反馈”的功能,可让您从图形管道中获取变换后的坐标。我不认为它非常适合您的用例,但无论如何您可能都想阅读它。

【讨论】:

  • 感谢您的详细回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-10
  • 1970-01-01
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多