OBJ 加载 - 奇怪的法线/uvs
OBJ loading - weird normals / uvs
所以,我正在尝试在 Java 中加载(Wavefront)OBJ 模型。目前它正确加载顶点位置,但纹理坐标混乱:
这是我在引擎中看到的:
这是我在搅拌机中看到的:
我当前的加载代码在这里:
private void loadOBJ(String filename)
{
ArrayList<Vector3f> verts = new ArrayList<Vector3f>();
ArrayList<Vector3f> norms = new ArrayList<Vector3f>();
ArrayList<Vector2f> uvs = new ArrayList<Vector2f>();
ArrayList<Integer> ints = new ArrayList<Integer>(); //Indices
ArrayList<Integer> nints = new ArrayList<Integer>(); //Normal indices
ArrayList<Integer> tints = new ArrayList<Integer>(); //Texture coord indices
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split(" ");
if(tokens[0].startsWith("vn"))
{
norms.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("vt"))
{
uvs.add(new Vector2f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2])));
}
else if(tokens[0].startsWith("v"))
{
verts.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("f"))
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[3].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[3].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[3].split("/")[2]) - 1);
if(tokens.length > 4) //For quads
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[4].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[4].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[4].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
}
}
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read file.");
e.printStackTrace();
System.exit(-1);
}
//Now convert the loaded data to internal format: VertexData[] that con tains positions, uvs, and normals, and int[] that has indices
vertices = new VertexData[verts.size()];
indices = new int[ints.size()];
for(int i = 0; i < ints.size(); i++)
{
indices[i] = ints.get(i);
int j = ints.get(i);
int k = tints.get(i);
int q = nints.get(i);
vertices[j] = new VertexData();
vertices[j].setXYZ(verts.get(j).x, verts.get(j).y, verts.get(j).z);
vertices[j].setST(uvs.get(k).x, uvs.get(k).y);
vertices[j].setNormal(norms.get(q).x, norms.get(q).y, norms.get(q).z);
}
}
正如您从图片中看到的,当前代码无法正确加载 uv,但我无法弄清楚哪里出了问题。帮忙?
代码看起来不错。
可能是导出问题。也许纹理坐标系统已恢复。
尝试翻转它:
u = u
v = 1 - v
您的代码没有正确生成适合 GL 的顶点数据。如您所知,在 obj 格式中,有一个单独的数组用于顶点位置、法线、纹理坐标等。人脸是通过独立索引到它们中形成的。在 GL 中,顶点是其所有属性的集合,您不能为每个属性使用单独的索引(至少不能直接使用;现代 GL 足够灵活,允许您在顶点着色器中实现该级别的间接访问,但那将是另一个故事)。
您的代码可能会尝试修复此问题,但解决方案是错误的。您只需使用 OBJ 文件中的顶点索引作为全局索引,并使用其他索引编写包含所有属性的顶点 不管怎样,如果相同的顶点索引可能与不同的法线组合一起使用或 texcoords - 你只需用最后一个这样的组合覆盖它。
协调方法将为每个发生的唯一(顶点、法线、纹理坐标)组合 创建一个单独的顶点。这样一来,顶点数当然会增加,索引值也不能直接复用于元素数组。
所以,我正在尝试在 Java 中加载(Wavefront)OBJ 模型。目前它正确加载顶点位置,但纹理坐标混乱:
这是我在引擎中看到的:
这是我在搅拌机中看到的:
我当前的加载代码在这里:
private void loadOBJ(String filename)
{
ArrayList<Vector3f> verts = new ArrayList<Vector3f>();
ArrayList<Vector3f> norms = new ArrayList<Vector3f>();
ArrayList<Vector2f> uvs = new ArrayList<Vector2f>();
ArrayList<Integer> ints = new ArrayList<Integer>(); //Indices
ArrayList<Integer> nints = new ArrayList<Integer>(); //Normal indices
ArrayList<Integer> tints = new ArrayList<Integer>(); //Texture coord indices
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split(" ");
if(tokens[0].startsWith("vn"))
{
norms.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("vt"))
{
uvs.add(new Vector2f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2])));
}
else if(tokens[0].startsWith("v"))
{
verts.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("f"))
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[3].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[3].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[3].split("/")[2]) - 1);
if(tokens.length > 4) //For quads
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[4].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[4].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[4].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
}
}
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read file.");
e.printStackTrace();
System.exit(-1);
}
//Now convert the loaded data to internal format: VertexData[] that con tains positions, uvs, and normals, and int[] that has indices
vertices = new VertexData[verts.size()];
indices = new int[ints.size()];
for(int i = 0; i < ints.size(); i++)
{
indices[i] = ints.get(i);
int j = ints.get(i);
int k = tints.get(i);
int q = nints.get(i);
vertices[j] = new VertexData();
vertices[j].setXYZ(verts.get(j).x, verts.get(j).y, verts.get(j).z);
vertices[j].setST(uvs.get(k).x, uvs.get(k).y);
vertices[j].setNormal(norms.get(q).x, norms.get(q).y, norms.get(q).z);
}
}
正如您从图片中看到的,当前代码无法正确加载 uv,但我无法弄清楚哪里出了问题。帮忙?
代码看起来不错。
可能是导出问题。也许纹理坐标系统已恢复。 尝试翻转它:
u = u
v = 1 - v
您的代码没有正确生成适合 GL 的顶点数据。如您所知,在 obj 格式中,有一个单独的数组用于顶点位置、法线、纹理坐标等。人脸是通过独立索引到它们中形成的。在 GL 中,顶点是其所有属性的集合,您不能为每个属性使用单独的索引(至少不能直接使用;现代 GL 足够灵活,允许您在顶点着色器中实现该级别的间接访问,但那将是另一个故事)。
您的代码可能会尝试修复此问题,但解决方案是错误的。您只需使用 OBJ 文件中的顶点索引作为全局索引,并使用其他索引编写包含所有属性的顶点 不管怎样,如果相同的顶点索引可能与不同的法线组合一起使用或 texcoords - 你只需用最后一个这样的组合覆盖它。
协调方法将为每个发生的唯一(顶点、法线、纹理坐标)组合 创建一个单独的顶点。这样一来,顶点数当然会增加,索引值也不能直接复用于元素数组。