使用UV纹理解析.obj文件

时间:2012-12-14 12:39:56

标签: android opengl-es textures

底线前线

在立方体的每一侧使用我的3D对象(例如立方体)1正确渲染三角形,而另一边则不正确。

概述

我正在尝试将在Blender中创建的3D对象导入我的Android OpenGL世界。

我编写了自己的自定义解析器来读取顶点,标记以及现在的纹理坐标。我可以很好地阅读所有数据并且没有纹理,形状是完美的。问题是,当我尝试读取纹理坐标时,在每个面上,其中一个三角形被拧得很紧,而其中一个三角形被正确渲染。

我认为这与如何在blender中生成纹理坐标有关。我注意到在文件f 1/1 2/2 3/3 4/4的部分中,它列出了“纹理的索引”,它们没有排列在列出纹理的顺序。这可能是问题吗?如果按照列出纹理索引的顺序构建纹理缓冲区会有帮助吗?

我的.obj文件

# Blender v2.63 (sub 0) OBJ File: ''
# www.blender.org
mtllib untitled.mtl
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.000000 0.334353
vt 0.332314 0.333333
vt 0.333333 0.665647
vt 0.001020 0.666667
vt 1.000000 0.001019
vt 0.998981 0.333333
vt 0.666667 0.332314
vt 0.667686 0.000000
vt 1.000000 0.665647
vt 0.667686 0.666667
vt 0.666667 0.334353
vt 0.334353 0.666667
vt 0.333333 0.334353
vt 0.665647 0.333333
vt 0.666667 0.665647
vt 0.333333 0.332314
vt 0.001020 0.333333
vt 0.000000 0.001020
vt 0.332314 0.000000
vt 0.333333 0.001019
vt 0.665647 0.000000
vt 0.334353 0.333333
usemtl Material_golden_fur_by_Dwaoviel.jpg
s off
f 1/1 2/2 3/3 4/4
f 5/5 8/6 7/7 6/8
f 1/6 5/9 6/10 2/11
f 2/12 6/13 7/14 3/15
f 3/16 7/17 8/18 4/19
f 5/20 1/21 4/7 8/22

我的解析器方法

public static Mesh createMesh(int resourceID)
{
    Mesh m = new Mesh();
    Scanner s;
    BufferedReader inputStream = null;

    ArrayList<Float> floats = new ArrayList<Float>();
    ArrayList<Short> indicies = new ArrayList<Short>();
    ArrayList<Float> textures = new ArrayList<Float>();
    ArrayList<Short> texturesindex = new ArrayList<Short>();
    try
    {
        inputStream = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(resourceID)));
        s = new Scanner(inputStream);
        String line = inputStream.readLine();
        line = inputStream.readLine();
        line = inputStream.readLine();
        line = inputStream.readLine();
        line = inputStream.readLine();
        while(line.charAt(0) == 'v'&&line.charAt(1)!='t')
        {
            s = new Scanner(line);
            s.next();
            floats.add(s.nextFloat());
            floats.add(s.nextFloat());
            floats.add(s.nextFloat());
            line = inputStream.readLine();
        }
        while(line.charAt(0)=='v' && line.charAt(1)=='t')
        {
            s = new Scanner(line);
            s.next(); //read in "vt"
            textures.add(s.nextFloat());
            textures.add(s.nextFloat());
            line = inputStream.readLine();
        }
        line = inputStream.readLine();
        line = inputStream.readLine();
        while(line != null && line.charAt(0) == 'f')
        {
            s = new Scanner(line);
            s.useDelimiter("[ /\n]");
            String xx = s.next();
            int a,b,c,d;
            int e,f,g,h;

            a = s.nextShort();

            e = s.nextShort();

            b = s.nextShort();

            f = s.nextShort();

            c = s.nextShort();

            g = s.nextShort();

            d = s.nextShort();

            h = s.nextShort();

            indicies.add((short)a);
            indicies.add((short)b);
            indicies.add((short)c);
            indicies.add((short)a);
            indicies.add((short)c);
            indicies.add((short)d);
            line = inputStream.readLine();
        }       
        float[] vertex = new float[floats.size()];
        for(int i=0;i<vertex.length;i++)
        {
            vertex[i] = floats.get(i).floatValue();
        }
        short[] ind = new short[indicies.size()];
        for(int i=0;i<ind.length;i++)
        {
            ind[i] = (short) (indicies.get(i).shortValue()-1); //minus one because its 1 based in that program
        }
        float[] texture = new float[floats.size()];
        for(int i=0;i<texture.length;i++)
        {
            texture[i] = textures.get(i).floatValue();
        }
        m.constructVertexBuffer(vertex);
        m.constructIndexBuffer(ind);
        m.constructTextureBuffer(texture);

    } 
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    } 
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return m;
}

注意,这个方法基本上读取与数组列表一样多的纹理uv坐标,然后填充一个float数组,然后调用构造Texture Buffer方法,该方法执行OpenGL所需的所有ByteBuffer / FloatBuffer。

我的施工方法

public void constructTextureBuffer(float[] texture)
{
    ByteBuffer vbb = ByteBuffer.allocateDirect(texture.length*4);
    vbb.order(ByteOrder.nativeOrder());
    textureBuffer = vbb.asFloatBuffer();
    textureBuffer.put(texture);
    textureBuffer.position(0);
}

有没有人对我可以尝试解决这个问题有任何想法?

2 个答案:

答案 0 :(得分:1)

我不确定我是否正确地读了你的代码,但我认为问题在于解析face命令('f'字符)。如果您查看OBJ specification内部或wiki上关于obj格式的文章,则face命令的格式为(在您的情况下)f v1/vt1 v2/vt2 v3/vt3 ...,这意味着首先是顶点坐标索引(进入您的先前加载的顶点坐标列表)'/'然后纹理坐标索引(进入先前加载的纹理坐标列表)。 'f'命令后一行上的所有顶点都会创建一个面(三角形,四边形等)。

如果纹理数组(列表)的索引与先前声明的顺序相同也无关紧要,它只是索引(0&lt; = index&lt; list of size)。

答案 1 :(得分:0)

有一些优秀的FOSS OpenGL引擎可以在野外使用。虽然我无法告诉您代码无法运行的确切原因但我知道Rajawali的OBJ解析器做得非常好,尤其是从Blender导出的模型。也许你可以在他们的代码中找到一些线索:

https://github.com/MasDennis/Rajawali/blob/master/src/rajawali/parser/ObjParser.java

编辑:我还应该补充说,即使在Rajawali,我通常也要从我的OBJ中删除两行,如下所示:     usemtl Material_golden_fur_by_Dwaoviel.jpg和     s off

也许你的解析器也处理不好?