有些面是透明的,有些是不透明的

时间:2009-02-19 14:10:50

标签: opengl transparency

我用OpenGL创建了一个regular dodecahedron。我想使面部透明(如维基百科上的图像),但这并不总是有效。在对OpenGL文档进行一些挖掘后,似乎我“需要sort the transparent faces from back to front”。嗯。我该怎么做?

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

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

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

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

旋转视图的代码:

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

4 个答案:

答案 0 :(得分:3)

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

执行此操作的一种方法是为每个透明面计算距离相机的代表距离(例如,其中心距相机中心的距离),然后在此代表距离上对透明面列表进行排序

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

(我应该补充说,“按照脸部中心的距离进行分类”的技巧有点幼稚,并且在面部很大或靠近相机的情况下导致错误的结果。但它很简单,会让你开始;以后会有足够的时间来担心更复杂的Z排序方法。)


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

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

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

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

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

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

答案 1 :(得分: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();
        }

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

这句话说明了一切 - 你需要对面部进行排序。

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

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

请注意,排序部分可能很棘手,因为函数“is-obsucred-by”是您要比较面部的函数(因为您需要先绘制模糊的面)不是一个排序,例如。可能存在周期(面部A模糊B&amp;面部B模糊A)。在这种情况下,您可能会拆分其中一个面以打破循环。

编辑:

通过获取传递给glVertex3f()的坐标获得顶点的z坐标,通过附加1使其成为4D(同质坐标),使用模型视图矩阵对其进行变换,然后使用投影矩阵对其进行变换,然后做透视师。详细信息请参见第2章“坐标转换”一节中的OpenGL规范。

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