【问题标题】:Some faces are transparent, other are opaque有些面是透明的,有些是不透明的
【发布时间】:2009-02-19 14:10:49
【问题描述】:

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

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

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

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

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

旋转视图的代码:

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

【问题讨论】:

  • 您的代码是否适用于不透明的面孔?我之所以这样问是因为我遇到了同样的问题,但面部不透明——即使我正确设置了法线,但面部渲染不正确。

标签: opengl transparency


【解决方案1】:

当 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。

【讨论】:

  • 这对我来说应该没关系,因为我没有重叠的面孔。
  • 在opengl中乘以矩阵试试glMultMatrixf(float*);
【解决方案2】:

作为参考,这里是代码(使用 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
        };

Faces 是在一个 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();
        }

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • lwjgl中没有调用glGetMatrix();是 GL_MODELVIEW_MATRIX 还是 GL_PROJECTION_MATRIX?
    【解决方案4】:

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

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

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

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

    编辑:

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

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

    【讨论】:

    • 如何获取人脸的 Z 坐标?
    猜你喜欢
    • 2017-01-11
    • 2014-05-27
    • 1970-01-01
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多