我用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);
答案 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或其他东西)