无法在libgdx中批处理多维数据集

时间:2014-07-13 15:36:48

标签: java api 3d libgdx batching

我正在尝试开发一款游戏,我在屏幕上渲染多达300个立方体。在为每个多维数据集创建新的modelInstance时,modelBatch的性能非常糟糕。据我所知,没有3D批处理将所有立方体批量打包到一个绘图调用。所以我拼命地尝试以某种方式批量处理它们。

此问题与此问题直接相关:LibGDX 3D increase perfomance

发布的答案成功批量处理所有立方体,但是当添加环境以获得某些照明时,看起来立方体缺少侧面或其他东西是错误的。

下图:

enter image description here

这是我的立方体类(几乎从上面的答案复制)

public class Cube {

  int index;
  int vertexFloatSize;
  int posOffset;
  int norOffset;
  boolean hasColor;
  int colOffset;
  private Vector3 position = new Vector3();
  private Matrix4 rotationTransform = new Matrix4().idt();

  private Color color = new Color();
  public float halfWidth, halfHeight, halfDepth;
  private boolean transformDirty = false;
  private boolean colorDirty = false;

  static final Vector3 CORNER000 = new Vector3();
  static final Vector3 CORNER010 = new Vector3();
  static final Vector3 CORNER100 = new Vector3();
  static final Vector3 CORNER110 = new Vector3();
  static final Vector3 CORNER001 = new Vector3();
  static final Vector3 CORNER011 = new Vector3();
  static final Vector3 CORNER101 = new Vector3();
  static final Vector3 CORNER111 = new Vector3();

  static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
  static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
  static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
  static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
  static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
  static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
  static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};

  static final Vector3 NORMAL0 = new Vector3();
  static final Vector3 NORMAL1 = new Vector3();
  static final Vector3 NORMAL2 = new Vector3();
  static final Vector3 NORMAL3 = new Vector3();
  static final Vector3 NORMAL4 = new Vector3();
  static final Vector3 NORMAL5 = new Vector3();
  static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};

  float[] meshVertices;

  public Cube(float x, float y, float z, float width, float height, float depth, int index, 
        VertexAttributes vertexAttributes, float[] meshVertices){

      position.set(x,y,z);
      this.halfWidth = width/2;
      this.halfHeight = height/2;
      this.halfDepth = depth/2;
      this.index = index;
      this.meshVertices = meshVertices;

      NORMAL0.set(0,0,-1);
      NORMAL1.set(0,0,1);
      NORMAL2.set(-1,0,0);
      NORMAL3.set(1,0,0);
      NORMAL4.set(0,-1,0);
      NORMAL5.set(0,1,0);

      vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
      posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
      norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;

      VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
      hasColor = colorAttribute!=null;
      if (hasColor){
          colOffset = colorAttribute.offset/4;
          this.setColor(Color.WHITE);
      }
      transformDirty = true;

  }

  public void setDimensions(float x, float y , float z){

      this.halfWidth = x/2;
      this.halfHeight = y/2;
      this.halfDepth = z/2;

  }

  public void setIndex(int index){

      this.index = index;
      transformDirty = true;
      colorDirty = true;

  }

  /**
   * Call this after moving and/or rotating.
   */
  public void update(){

      if (colorDirty && hasColor){
          for (int faceIndex= 0; faceIndex<6; faceIndex++){
              int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
              for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                  int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
                  meshVertices[vertexIndex] = color.r;
                  meshVertices[++vertexIndex] = color.g;
                  meshVertices[++vertexIndex] = color.b;
                  meshVertices[++vertexIndex] = color.a;
              }
          }
          colorDirty = false;
      }


      if (!transformDirty){
          return;
      }

      transformDirty = false;

      CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
      CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
      CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
    CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);

      NORMAL0.set(0,0,-1).rot(rotationTransform);
      NORMAL1.set(0,0,1).rot(rotationTransform);
      NORMAL2.set(-1,0,0).rot(rotationTransform);
      NORMAL3.set(1,0,0).rot(rotationTransform);
      NORMAL4.set(0,-1,0).rot(rotationTransform);
      NORMAL5.set(0,1,0).rot(rotationTransform);

      for (int faceIndex= 0; faceIndex<6; faceIndex++){
          int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
          for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
              int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
              meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
              meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
              meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;

              vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
              meshVertices[vertexIndex] = NORMALS[faceIndex].x;
              meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
              meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
          }
      }
  }

  public Cube setColor(Color color){

      if (hasColor){
          this.color.set(color);
          colorDirty = true;
      }
      return this;
  }

  public void setAlpha(float alpha) {

       if (hasColor){

           this.color.set(this.color.r, this.color.g, this.color.b, alpha);
           colorDirty = true;

       }
  }

  public Cube translate(float x, float y, float z){
      position.add(x,y,z);
      transformDirty = true;
      return this;
  }

  public Cube setPosition(float x, float y, float z){
      position.set(x,y,z);
      transformDirty = true;
      return this;
  }

public Cube setPosition(Vector3 position1) {

       position.set(position1);
       transformDirty = true;
       return this;

  }

  public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
      int len = attributes.size();
      for (int i = 0; i < len; i++)
          if (attributes.get(i).usage == usage) return attributes.get(i);

      return null;
  }



  public Vector3 getPosition() {

      return this.position;
  }

}

这是我为测试多维数据集而创建的测试用例。

public class TestCase {

    ModelInstance mBatchedCubesModelInstance;
    Mesh mBatchedCubesMesh;
    float[] mBatchedCubesVertices;
    Array<Cube> mBatchedCubes;

    TestCase(){

            int width = 5;
            int height = 5;
            int length = 5;
            int numCubes = width*height*length;

            ModelBuilder mb = new ModelBuilder();
            mb.begin();
            MeshPartBuilder mpb = mb.part("cubes", GL20.GL_TRIANGLES, (Usage.Position | Usage.Normal | Usage.Color), new Material());
            for (int i=0; i<numCubes; i++){
                mpb.box(1, 1, 1);
            }
            Model model = mb.end();
            mBatchedCubesModelInstance = new ModelInstance(model);

            mBatchedCubesMesh = model.meshes.get(0);
            VertexAttributes vertexAttributes = mBatchedCubesMesh.getVertexAttributes();
            int vertexFloatSize = vertexAttributes .vertexSize / 4; //4 bytes per float
            mBatchedCubesVertices = new float[numCubes * 24 * vertexFloatSize]; //24 unique vertices per cube
            mBatchedCubesMesh.getVertices(mBatchedCubesVertices);

            mBatchedCubes = new Array<Cube>(numCubes);
            int cubeNum = 0;
            for (int x = 0; x < width; x++) {
                    for (int y = 0; y < height; y++) {
                            for (int z = 0; z < length; z++) {
                                    mBatchedCubes.add(new Cube((x-(width/2f))*1.5f, -((y-(height/2f)) * 1.5f), -(z-(length/2f))*1.5f, 1,1,1, cubeNum++, vertexAttributes, mBatchedCubesVertices ).setColor(Colors.getMixedColor()));
                            }
                    }
            }

    }

    public void render(ModelBatch batch, Environment environment){

            for (Cube cube : mBatchedCubes){ //must update any changed cubes.
                cube.update();
            }
            mBatchedCubesMesh.setVertices(mBatchedCubesVertices); //apply changes to mesh

            if(environment!=null) batch.render(mBatchedCubesModelInstance,environment);
            else batch.render(this.mBatchedCubesModelInstance);

    }

}

立方体甚至不会在我的游戏中移动,所以我甚至不需要转换。我只需要每帧设置颜色(包括alpha通道)。批处理网格也应该与libgdx阴影贴图一起使用(我希望当你有一个正确批处理的modelInstance时它会自动运行)。

有人能告诉我我的代码有什么问题以及为什么有些面孔不会出现。我知道我可能会问得太多,所以我会在两天后对这个问题(50分)给予赏金。

编辑:在Tenfour04的回答之后,事情变得更好了。 Alpha通道似乎工作,奇怪的脸问题似乎已经消失。然而,当我将更改应用到我的真实游戏中时,我注意到有时面粉会在游戏地形中显示出来。我更新了测试,通过在立方体中间添加大平面来说明问题。制作视频:https://www.youtube.com/watch?v=LQhSMJfuyZY

我还想澄清一点,我没有使用任何自定义着色器。仅使用ModelBatch.begin()和.end()方法,无需额外的openGl调用。

1 个答案:

答案 0 :(得分:1)

我发现了几个问题:

  1. 您已关闭混合和/或着色器未设置Alpha值

  2. 你没有剔除背面。

  3. 因此,当您创建新材料时,请使用:

    ,而不是仅使用new Material()
    new Material(
        IntAttribute.createCullFace(GL20.GL_FRONT),//For some reason, libgdx ModelBuilder makes boxes with faces wound in reverse, so cull FRONT
        new BlendingAttribute(1f), //opaque since multiplied by vertex color
        new DepthTestAttribute(false), //don't want depth mask or rear cubes might not show through
        ColorAttribute.createDiffuse(Color.WHITE) //white since multiplied by vertex color
        );
    

    您还需要按距离相机的距离对多维数据集进行排序,以使其alpha值正确分层。这是一个支持排序的更新Cube类。它必须能够独立于顶点数组跟踪颜色,以防其索引发生变化:

    public class Cube implements Comparable<Cube>{
    
        private int index;
        int vertexFloatSize;
        int posOffset;
        int norOffset;
        boolean hasColor;
        int colOffset;
        private Vector3 position = new Vector3();
        private Matrix4 rotationTransform = new Matrix4().idt();
        public float halfWidth, halfHeight, halfDepth;
        private boolean transformDirty = false;
        private boolean colorDirty = false;
        private Color color = new Color();
        float camDistSquared;
    
        static final Vector3 CORNER000 = new Vector3();
        static final Vector3 CORNER010 = new Vector3();
        static final Vector3 CORNER100 = new Vector3();
        static final Vector3 CORNER110 = new Vector3();
        static final Vector3 CORNER001 = new Vector3();
        static final Vector3 CORNER011 = new Vector3();
        static final Vector3 CORNER101 = new Vector3();
        static final Vector3 CORNER111 = new Vector3();
    
        static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
        static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
        static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
        static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
        static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
        static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
        static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};
    
        static final Vector3 NORMAL0 = new Vector3();
        static final Vector3 NORMAL1 = new Vector3();
        static final Vector3 NORMAL2 = new Vector3();
        static final Vector3 NORMAL3 = new Vector3();
        static final Vector3 NORMAL4 = new Vector3();
        static final Vector3 NORMAL5 = new Vector3();
        static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};
    
        public Cube(float x, float y, float z, float width, float height, float depth, int index, 
            VertexAttributes vertexAttributes, float[] meshVertices){
        position.set(x,y,z);
        this.halfWidth = width/2;
        this.halfHeight = height/2;
        this.halfDepth = depth/2;
        this.index = index;
    
    
        vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
        posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
        norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;
    
        VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
        hasColor = colorAttribute!=null;
        if (hasColor){
            colOffset = colorAttribute.offset/4;
            this.setColor(Color.WHITE, meshVertices);
        }
        transformDirty = true;
        }
    
        public void updateCameraDistance(Camera cam){
        camDistSquared = cam.position.dst2(position);
        }
    
        /**
         * Call this after moving and/or rotating.
         */
        public void update(float[] meshVertices){
    
        if (transformDirty){
            transformDirty = false;
    
            CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
    
            NORMAL0.set(0,0,-1).rot(rotationTransform);
            NORMAL1.set(0,0,1).rot(rotationTransform);
            NORMAL2.set(-1,0,0).rot(rotationTransform);
            NORMAL3.set(1,0,0).rot(rotationTransform);
            NORMAL4.set(0,-1,0).rot(rotationTransform);
            NORMAL5.set(0,1,0).rot(rotationTransform);
    
            for (int faceIndex= 0; faceIndex<6; faceIndex++){
            int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
            for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
                meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
                meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
                meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;
    
                vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
                meshVertices[vertexIndex] = NORMALS[faceIndex].x;
                meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
                meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
            }
            }
        }
    
        if (colorDirty){
            colorDirty = false;
    
            for (int faceIndex= 0; faceIndex<6; faceIndex++){
            int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
            for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
                meshVertices[vertexIndex] = color.r;
                meshVertices[++vertexIndex] = color.g;
                meshVertices[++vertexIndex] = color.b;
                meshVertices[++vertexIndex] = color.a;
            }
            }
        }
        }
    
        public Cube setColor(Color color, float[] meshVertices){
        if (hasColor){
            this.color.set(color);
            colorDirty = true;
    
        }
        return this;
        }
    
        public void setIndex(int index){
        if (this.index != index){
            transformDirty = true;
            colorDirty = true;
            this.index = index;
        }
        }
    
        public Cube translate(float x, float y, float z){
        position.add(x,y,z);
        transformDirty = true;
        return this;
        }
    
        public Cube translateTo(float x, float y, float z){
        position.set(x,y,z);
        transformDirty = true;
        return this;
        }
    
        public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
        rotationTransform.rotate(axisX, axisY, axisZ, degrees);
        transformDirty = true;
        return this;
        }
    
        public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
        rotationTransform.idt();
        rotationTransform.rotate(axisX, axisY, axisZ, degrees);
        transformDirty = true;
        return this;
        }
    
        public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
        int len = attributes.size();
        for (int i = 0; i < len; i++)
            if (attributes.get(i).usage == usage) return attributes.get(i);
    
        return null;
        }
    
        @Override
        public int compareTo(Cube other) {
        //the cube has a lower index than a cube that is closer to the camera
        if (camDistSquared>other.camDistSquared)
            return -1;
        return camDistSquared<other.camDistSquared ? 1 : 0;
        }
    }
    

    你会这样排序:

    for (Cube cube : mBatchedCubes){
        cube.updateCameraDistance(camera);
    }
    mBatchedCubes.sort();
    int index = 0;
    for (Cube cube : mBatchedCubes){
        cube.setIndex(index++);
    }