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

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

这个问题与此直接相关: LibGDX 3D增加性能

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

下图是:

在此处输入图像描述

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

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 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(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通道似乎工作,奇怪的脸问题似乎已经消失。 然而,当我将更改应用到我的真实游戏中时,我注意到有时面粉会在游戏地形中占据上风。 我更新了测试,通过在立方体中间添加大平面来说明问题。 制作了一个video: https : //www.youtube.com/watch?v = LQhSMJfuyZY 。

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

我看到几个问题:

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

  2. 你没有剔除背面。

因此,当您创建新材质时,不要只使用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{ 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 

你会这样排序:

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