我正在制作3D游戏.
游戏需要大约100个立方体才能工作,所有立方体都是动态的.
我真的不知道这样的游戏需要多少性能,但我正在使用带有Mali-400 MP2 GPU,1 GB RAM,1.5 GHz双核的平板电脑进行测试.我知道在一个网格中渲染所有立方体,但是我不能分别移动所有这些立方体.
这个设置给了我一个非常摇摆的fps.跳跃在20到50之间,大多在30以下.(在模拟器10-15中)
当游戏开始时,我构建了一个ModelInstances的arraylist,他们都使用相同的模型.
model = new ModelBuilder().createBox(1f, 1f, 1f, new Material(ColorAttribute.createDiffuse(Color.GREEN)), Usage.Position | Usage.Normal); // width,height,length = 5, creating a total of 125 cubes for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { for (int z = 0; z < length; z++) { if (this.map[x][y][z] > 0) { this.modelInstances.add(instance = new ModelInstance(model)); instance.transform.translate(x, -(y * 1.5f), -z); } } } }
渲染:
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); mb.begin(camera3D); mb.render(this.modelInstances); mb.end();
相机初始化:
camera3D = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); camera3D.position.set(0f, 8f, 5f); camera3D.lookAt(0, 0, 0); camera3D.near = 1f; camera3D.far = 300f; camera3D.update();
我该怎么做才能提高性能?
对于像这样的游戏,平板电脑是否太弱了?或问题是代码?
编辑:
用webGL进行了一些测试,同样的平板电脑,使用铬,渲染125立方体:稳定40-50 fps
您可以将所有多维数据集批量处理为单个Model和ModelInstance,如下所示:
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), new Material(ColorAttribute.createDiffuse(Color.GREEN))); for (int i=0; i<numCubes; i++){ mpb.box(1, 1, 1); } Model model = mb.end(); mBatchedCubesModelInstance = new ModelInstance(model);
但棘手的部分是能够将每个立方体移动到不同的位置,并能够独立操作它们.
这是一个Cube类,可以从上面的模型中操作各个多维数据集.我认为理论上它应该适用于您创建的每个立方体使用24个唯一顶点的网格,因此您可以添加纹理坐标.
这也依赖于position,然后normal始终是mesh的前两个Usage属性,所以希望在libGDX的Mesh类中成立.
这基本上通过跟踪它在基础网格中的索引号(它是多维数据集编号)来工作,因此它可以选择顶点数组中需要更新的顶点.顶点需要每帧复制到网格中.
public class 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(); 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}; 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 setIndex(int index){ this.index = index; transformDirty = true; colorDirty = true; } /** * Call this after moving and/or rotating. */ public void update(float[] meshVertices){ 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 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; } }
要使用它,首先获取网格参考并创建立方体:
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 )); } } }
然后在你的render
方法中:
mBatchedCubes.get(0).rotate(1, 1, 1, 180*delta); //example manipulation of a single cube for (Cube cube : mBatchedCubes){ //must update any changed cubes. cube.update(mBatchedCubesVertices); } mBatchedCubesMesh.setVertices(mBatchedCubesVertices); //apply changes to mesh ... modelBatch.begin(camera); modelBatch.render(mBatchedCubesModelInstance); modelBatch.end();
现在CPU顶点操作不如着色器顶点操作效率高,因此如果您在每个帧周围移动所有多维数据集,这可能会成为CPU绑定.如果你没有旋转它们,可能有助于为旋转创建一个单独的"脏"变量,并且只在必要时在更新方法中旋转.
编辑:从这个问题更新
如果您想要具有透明度,那么立方体必须是可排序的,因此可以从远到远对它们进行排序以进行绘制.它们的index
值必须更新为新的顺序,因为它们是按顺序排列到网格中的.这是一个支持排序的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) { //This is a simple sort based on center point distance to camera. A more //sophisticated sorting method might be required if the cubes are not all the same //size (such as calculating which of the 8 vertices is closest to the camera //and using that instead of the center point). 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++); }