首页 文章

将多个.OBJ-index-buffers映射/折叠到OpenGL的1索引缓冲区

提问于
浏览
9

我正在尝试加载.obj文件并在 glDrawElements 的帮助下绘制它 .

现在, glDrawArrays 一切都很完美,但它当然是效率低下的 .

我现在遇到的问题是,.obj文件使用多个索引缓冲区(对于每个属性),而OpenGL只能使用一个 . 所以我需要相应地映射它们 .

那里有很多伪算法,我甚至找到了一个C实现 . 我确实知道很多C,但奇怪的是我没有帮助我在Scala中实现 .

让我们来看看:

private def parseObj(path: String): Model =
{
    val objSource: List[String] = Source.fromFile(path).getLines.toList

    val positions: List[Vector3] = objSource.filter(_.startsWith("v ")).map(_.split(" ")).map(v => new Vector3(v(1).toFloat,v(2).toFloat,v(3).toFloat))//, 1.0f))
    val normals: List[Vector4] = objSource.filter(_.startsWith("vn ")).map(_.split(" ")).map(v => new Vector4(v(1)toFloat,v(2).toFloat, v(3).toFloat, 0.0f))
    val textureCoordinates: List[Vector2] = objSource.filter(_.startsWith("vt ")).map(_.split(" ")).map(v => new Vector2(v(1).toFloat, 1-v(2).toFloat)) // TODO 1-y because of blender
    val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))

    val vertices: List[Vertex] =  for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))

    val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
    println(f.mkString("\n"))

    val indices: List[Int] = faces.map(f => f._1-1) // Wrong!

    new Model(vertices.toArray, indices.toArray)
}

val indices: List[Int] 是我的第一个天真的方法,当然是错的 . 但是让我们从顶部开始:

我加载文件并完成它 . (我假设您知道.obj文件是如何组成的)

我读了顶点,纹理坐标和法线 . 然后我来到了脸上 .

现在,我的示例中的每个面都有3个值 v_x, t_y, n_z ,用于定义 vertexAtIndexX, textureCoordAtIndexY, normalAtIndexZ . 因此,每个定义一个顶点,而其中三个(或文件中的一行)定义一个面/多边形/三角形 .

val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1))) 我实际上尝试创建顶点(一个案例类,目前只保存位置和纹理坐标,现在忽略法线)

真正的问题是这一行:

val indices: List[Int] = faces.map(f => f._1-1) // Wrong!

为了得到真正的指数,我基本上需要这样做 instead

val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))val indices: List[Int] = faces.map(f => f._1-1) // Wrong!

伪代码:

Iterate over all faces
    Iterate over all vertices in a face
       Check if we already have that combination of(position, texturecoordinate, normal) in our newVertices

       if(true)
          indices.put(indexOfCurrentVertex)
       else
          create a new Vertex from the face
          store the new vertex in the vertex list
          indices.put(indexOfNewVertex)

然而,我完全陷入困境 . 我尝试了不同的东西,但无法想出一个真正有用的漂亮而干净的解决方案 .

像:

val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))

并且尝试 f.distinct 不起作用,因为没有什么可以区分,所有条目都是唯一的,如果我查看文件完全有意义,但这就是伪代码告诉我检查的内容 .

当然那么我需要相应地填写指数(最好是单线并具有很多功能美)

但我应该试着找到重复的东西,所以...我有点困惑 . 我猜我把所有的参考都混合了不同的“顶点”和“位置” .

所以,我认为是错误的,还是算法/思考正确,我只需要在漂亮,干净(实际工作)的Scala代码中实现它?

拜托,赐教!

As per comments, I made a little update:

var index: Int = 0
val map: mutable.HashMap[(Int, Int, Int), Int] = new mutable.HashMap[(Int, Int, Int), Int].empty

val combinedIndices: ListBuffer[Int] = new ListBuffer[Int]

for(face <- faces)
{
    val vID: Int = face._1-1
    val nID: Int = face._2-1
    val tID: Int = face._3-1

    var combinedIndex: Int = -1

    if(map.contains((vID, nID, tID)))
    {
        println("We have a duplicate, wow!")
        combinedIndex = map.get((vID, nID, tID)).get
    }
    else
    {
        combinedIndex = index
        map.put((vID, nID, tID), combinedIndex)
        index += 1
    }

    combinedIndices += combinedIndex
}

面孔仍然是:

val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))

Fun fact 我显然还是不明白,因为这样我从来没有复制过!

意味着 combinedIndices 在最后只是拥有如下自然数:

ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)

1 回答

  • 1

    这是javascript(抱歉不是scala)但是它被评论并且应该很容易转换 .

    // bow-tie
    var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv -1 1 0\nv -1 -1 0\n" +
        "vt 0 .5\nvt 1 1\nvt 1 0\n" +
        "vn 0 0 1\n" +
        "f 1/1/1 2/2/1 3/3/1\nf 1/1/1 4/2/1 5/3/1";
    // output indices should be [0, 1, 2, 0, 3, 4]
    // parse the file
    var lines = objString.split("\n");
    var data = lines.map(function(line) { return line.split(" "); });
    var v = [];
    var t = [];
    var n = [];
    var f = [];
    var indexMap = new Map(); // HashMap<face:string, index:integer>
    var nextIndex = 0;
    var vertices = [];
    var indices = [];
    // fill vertex, texture and normal arrays
    data.filter(function(d) { return d[0] == "v"; }).forEach(function(d) { v.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
    data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d) { t.push([parseFloat(d[1]), parseFloat(d[2])]); });
    data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d) { n.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
    //
    console.log("V", v.toString());
    console.log("T", t.toString());
    console.log("N", n.toString());
    // create vertices and indices arrays by parsing faces
    data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
        var f1 = d[1].split("/").map(function(d) { return parseInt(d)-1; });
        var f2 = d[2].split("/").map(function(d) { return parseInt(d)-1; });
        var f3 = d[3].split("/").map(function(d) { return parseInt(d)-1; });
        // 1
        if(indexMap.has(d[1].toString())) {
            indices.push(indexMap.get(d[1].toString()));
        } else {
            vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
            indexMap.set(d[1].toString(), nextIndex);
            indices.push(nextIndex++);
        }
        // 2
        if(indexMap.has(d[2].toString())) {
            indices.push(indexMap.get(d[2].toString()));
        } else {
            vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
            indexMap.set(d[2].toString(), nextIndex);
            indices.push(nextIndex++);
        }
        // 3
        if(indexMap.has(d[3].toString())) {
            indices.push(indexMap.get(d[3].toString()));
        } else {
            vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
            indexMap.set(d[3].toString(), nextIndex);
            indices.push(nextIndex++);
        }
    });
    //
    console.log("Vertices", vertices.toString());
    console.log("Indices", indices.toString());
    

    输出

    V 0,0,0,1,1,0,1,-1,0,-1,1,0,-1,-1,0
    T 0,0.5,1,1,1,0
    N 0,0,1
    Vertices 0,0,0,0,0.5,0,0,1,1,1,0,1,1,0,0,1,1,-1,0,1,0,0,0,1,-1,1,0,1,1,0,0,1,-1,-1,0,1,0,0,0,1
    Indices 0,1,2,0,3,4
    

    JSFiddle http://jsfiddle.net/8q7jLvsq/2

    我唯一要做的就是〜使用字符串hat表示一个face的一部分作为我的indexMap的键(例如:“25/32/5”) .

    EDIT JSFiddle http://jsfiddle.net/8q7jLvsq/2/此版本结合了顶点,纹理和法线的重复值 . 这将优化OBJ文件,这些文件重复相同的值,使每个面都独一无二 .

    // bow-tie
    var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv 0 0 0\nv -1 1 0\nv -1 -1 0\n" +
        "vt 0 .5\nvt 1 1\nvt 1 0\nvt 0 .5\nvt 1 1\nvt 1 0\n" +
        "vn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\n" +
        "f 1/1/1 2/2/2 3/3/3\nf 4/4/4 5/5/5 6/6/6";
    // output indices should be [0, 1, 2, 0, 3, 4]
    // parse the file
    var lines = objString.split("\n");
    var data = lines.map(function(line) { return line.split(" "); });
    var v = [];
    var t = [];
    var n = [];
    var f = [];
    var vIndexMap = new Map(); // map to earliest index in the list
    var vtIndexMap = new Map();
    var vnIndexMap = new Map();
    var indexMap = new Map(); // HashMap<face:string, index:integer>
    var nextIndex = 0;
    var vertices = [];
    var indices = [];
    // fill vertex, texture and normal arrays
    data.filter(function(d) { return d[0] == "v"; }).forEach(function(d, i) { 
        v[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
        var key = [d[1], d[2], d[3]].toString();
        if(!vIndexMap.has(key)) {
            vIndexMap.set(key, i);
        }
    });
    data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d, i) { 
        t[i] = [parseFloat(d[1]), parseFloat(d[2])]; 
        var key = [d[1], d[2]].toString();
        if(!vtIndexMap.has(key)) {
            vtIndexMap.set(key, i);
        }
    });
    data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d, i) { 
        n[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
        var key = [d[1], d[2], d[3]].toString();
        if(!vnIndexMap.has(key)) {
            vnIndexMap.set(key, i);
        }
    });
    //
    console.log("V", v.toString());
    console.log("T", t.toString());
    console.log("N", n.toString());
    // create vertices and indices arrays by parsing faces
    data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
        var f1 = d[1].split("/").map(function(d, i) {
            var index = parseInt(d)-1;
            if(i == 0) index = vIndexMap.get(v[index].toString());
            else if(i == 1) index = vtIndexMap.get(t[index].toString());
            else if(i == 2) index = vnIndexMap.get(n[index].toString());
            return index;
        });
        var f2 = d[2].split("/").map(function(d, i) { 
            var index = parseInt(d)-1;
            if(i == 0) index = vIndexMap.get(v[index].toString());
            else if(i == 1) index = vtIndexMap.get(t[index].toString());
            else if(i == 2) index = vnIndexMap.get(n[index].toString());
            return index;
        });
        var f3 = d[3].split("/").map(function(d, i) { 
            var index = parseInt(d)-1;
            if(i == 0) index = vIndexMap.get(v[index].toString());
            else if(i == 1) index = vtIndexMap.get(t[index].toString());
            else if(i == 2) index = vnIndexMap.get(n[index].toString());
            return index; 
        });
        // 1
        if(indexMap.has(f1.toString())) {
            indices.push(indexMap.get(f1.toString()));
        } else {
            vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
            indexMap.set(f1.toString(), nextIndex);
            indices.push(nextIndex++);
        }
        // 2
        if(indexMap.has(f2.toString())) {
            indices.push(indexMap.get(f2.toString()));
        } else {
            vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
            indexMap.set(f2.toString(), nextIndex);
            indices.push(nextIndex++);
        }
        // 3
        if(indexMap.has(f3.toString())) {
            indices.push(indexMap.get(f3.toString()));
        } else {
            vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
            indexMap.set(f3.toString(), nextIndex);
            indices.push(nextIndex++);
        }
    });
    //
    console.log("Vertices", vertices.toString());
    console.log("Indices", indices.toString());
    

相关问题