3

我用 OpenGL创建了一个常规的十二面体。我想让面孔透明(如维基百科上的图像),但这并不总是有效。在 OpenGL 文档中进行了一些挖掘之后,似乎我“需要从后到前对透明面进行排序”。嗯。我怎么做?

我的意思是我调用 glRotatef() 来旋转坐标系,但面的参考坐标保持不变;旋转效果应用在我的更新代码的“外部”。

如果我将变换应用于坐标,那么其他一切都将停止移动。

在这种情况下如何对人脸进行排序?

[编辑] 我知道为什么会这样。我不知道解决方案会是什么样子。有人可以指导我正确的 OpenGL 调用或一段示例代码吗?我知道坐标变换何时完成,并且我有面顶点的坐标。我知道如何计算人脸的中心坐标。我知道我需要按 Z 值对它们进行排序。如何通过当前视图矩阵(或任何旋转我的坐标系的东西)转换 Vector3f?

旋转视图的代码:

    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);
4

4 回答 4

3

当 OpenGL 文档说“对透明面进行排序”时,它的意思是“更改绘制它们的顺序”。您不转换面本身的几何形状,而是确保以正确的顺序绘制面:首先离相机最远,最后离相机最近,以便颜色在帧缓冲区中正确混合。

一种方法是为每个透明面计算距相机的代表距离(例如,其中心距相机中心的距离),然后根据该代表距离对透明面列表进行排序。

您需要这样做,因为 OpenGL 使用Z 缓冲技术。

(我要补充一点,“按人脸中心距离排序”的技术有点幼稚,在人脸大或靠近相机的情况下会导致错误的结果。但它很简单,会让你开始;以后会有很多时间来担心更复杂的 Z 排序方法。)


更新:Aaron,您澄清了帖子以表明您了解上述内容,但不知道如何为每个人脸计算合适的 Z 值。是对的吗?我通常会通过测量从相机到相关面部的距离来做到这一点。所以我想这意味着你不知道相机在哪里?

如果这是您遇到的问题的正确陈述,请参阅OpenGL FAQ 8.010

就OpenGL而言,没有相机。更具体地说,相机总是位于眼睛空间坐标(0., 0., 0.)。

更新:也许问题是你不知道如何通过模型视图矩阵转换一个点?如果这是问题所在,请参阅OpenGL FAQ 9.130

通过将点乘以 ModelView 矩阵,将点转换为眼睛坐标空间。然后简单地计算它与原点的距离。

用于glGetFloatv(GL_MODELVIEW_MATRIX, dst)将模型视图矩阵作为 16 个浮点数的列表。我认为您必须自己进行乘法运算:据我所知,OpenGL 没有为此提供 API。

于 2009-02-19T14:38:15.537 回答
1

作为参考,这里是代码(使用 lwjgl 2.0.1)。我通过使用浮点数组的数组来定义我的模型:

        float one = 1f * scale;

        // Cube of size 2*scale
        float[][] coords = new float[][] {
            {  one,  one,  one }, // 0
            { -one,  one,  one },
            {  one, -one,  one },
            { -one, -one,  one },
            {  one,  one, -one },
            { -one,  one, -one },
            {  one, -one, -one },
            { -one, -one, -one }, // 7
        };

面在 int 数组的数组中定义。内部数组中的项目是顶点的索引:

        int[][] faces = new int[][] {
            { 0, 2, 3, 1, },
            { 0, 4, 6, 2, },
            { 0, 1, 5, 4, },
            { 4, 5, 7, 6, },
            { 5, 1, 3, 7, },
            { 4, 5, 1, 0, },
        };

这些行加载模型/视图矩阵:

        Matrix4f matrix = new Matrix4f ();
        FloatBuffer params = FloatBuffer.allocate (16);
        GL11.glGetFloat (GL11.GL_MODELVIEW_MATRIX, params );
        matrix.load (params);

我将每张脸的一些信息存储在 Face 类中:

public static class Face
{
    public int id;
    public Vector3f center;

    @Override
    public String toString ()
    {
        return String.format ("%d %.2f", id, center.z);
    }
}

然后使用此比较器按 Z 深度对面进行排序:

public static final Comparator<Face> FACE_DEPTH_COMPARATOR = new Comparator<Face> ()
{
    @Override
    public int compare (Face o1, Face o2)
    {
        float d = o1.center.z - o2.center.z;
        return d < 0f ? -1 : (d == 0 ? 0 : 1);
    }

};

getCenter()返回面的中心:

    public static Vector3f getCenter (float[][] coords, int[] face)
    {
        Vector3f center = new Vector3f ();
        for (int vertice = 0; vertice < face.length; vertice ++)
        {
            float[] c = coords[face[vertice]];
            center.x += c[0];
            center.y += c[1];
            center.z += c[2];
        }
        float N = face.length;
        center.x /= N;
        center.y /= N;
        center.z /= N;
        return center;
    }

现在我需要设置人脸数组:

        Face[] faceArray = new Face[faces.length]; 
        Vector4f v = new Vector4f ();
        for (int f = 0; f < faces.length; f ++)
        {
            Face face = faceArray[f] = new Face ();
            face.id = f;
            face.center = getCenter (coords, faces[f]);
            v.x = face.center.x;
            v.y = face.center.y;
            v.z = face.center.z;
            v.w = 0f;
            Matrix4f.transform (matrix, v, v);
            face.center.x = v.x;
            face.center.y = v.y;
            face.center.z = v.z;
        }

在这个循环之后,我得到了转换后的中心向量,faceArray我可以按 Z 值对它们进行排序:

        Arrays.sort (faceArray, FACE_DEPTH_COMPARATOR);
        //System.out.println (Arrays.toString (faceArray));

渲染发生在另一个嵌套循环中:

        float[] faceColor = new float[] { .3f, .7f, .9f, .3f };
        for (Face f: faceArray)
        {
            int[] face = faces[f.id];
            glColor4fv(faceColor);

            GL11.glBegin(GL11.GL_TRIANGLE_FAN);
            for (int vertice = 0; vertice < face.length; vertice ++)
            {
                glVertex3fv (coords[face[vertice]]);
            }
            GL11.glEnd();
        }
于 2009-02-19T15:46:34.993 回答
0

您是否尝试过从后到前绘制与常规世界坐标相关的每张脸?通常,某些 OpenGL 文档中的措辞似乎很奇怪。我认为,如果您以正确的顺序绘制图形而不用担心旋转,它可能会在您添加旋转时自动工作。OpenGL 可能会在旋转矩阵时处理面的重新排序。

或者,您可以在绘制时获取当前矩阵(glGetMatrix())并根据哪些面将被旋转后/前重新排序您的绘图算法。

于 2009-02-19T14:37:03.933 回答
0

那句话说明了一切——你需要对面孔进行分类。

当绘制这样一个简单的对象时,您可以使用 z-buffer 先渲染背面,然后再渲染正面(通过使用不同的 z-buffer 比较函数渲染两次)。

但通常,您只想变换对象,然后对面进行排序。您只转换内存中对象的表示,然后通过排序确定绘制顺序,然后使用原始坐标按该顺序绘制,根据需要使用转换(需要与您所做的排序一致)。在实际应用程序中,您可能会隐式进行转换,例如。通过将场景存储为 BSP 或 Quad- 或 R- 或任何树,并简单地从各个方向遍历树。

请注意,排序部分可能很棘手,因为函数“is-obsucred-by”是您想要比较面孔的函数(因为您需要先绘制被遮挡的面孔)不是排序,例如。可以有循环(A面遮盖B && B面遮盖A)。在这种情况下,您可能会拆分其中一个面以中断循环。

编辑:

您可以通过获取传递给 的坐标来获得顶点的 z 坐标,glVertex3f()通过附加 1 使其成为 4D(齐次坐标),使用模型视图矩阵对其进行变换,然后使用投影矩阵对其进行变换,然后进行透视除法。详细信息在第 2 章坐标转换部分的 OpenGL 规范中。

但是,没有任何 API 可供您实际进行转换。OpenGL 让您做的唯一一件事就是绘制图元,并告诉渲染器如何绘制它们(例如,如何转换它们)。它不会让您轻松地转换坐标或其他任何东西(尽管 IIUC 有方法告诉 OpenGL 将转换后的坐标写入缓冲区,但这并不容易)。如果您想要一些库来帮助您操作实际对象、坐标等,请考虑使用某种场景库(OpenInventor 或其他东西)

于 2009-02-19T14:37:42.993 回答