画面に最大 300 個のキューブをレンダリングするゲームを開発しようとしています。キューブごとに新しい modelInstance を作成するときの modelBatch のパフォーマンスはひどいものです。私の知る限り、すべてのキューブを 1 回の描画呼び出しにバッチ処理する 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<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);
}
}
私のゲームでは立方体は動かないので、変換も必要ありません。フレームごとに色(アルファチャンネルを含む)を設定するだけです。バッチ処理されたメッシュは libgdx シャドウ マッピングでも動作するはずです (適切にバッチ処理された modelInstance がある場合に自動的に動作することを願っています)。
私のコードの何が問題なのか、一部の顔が表示されない理由を教えてください。質問が多いかもしれないことは承知していますので、2 日後にこの質問に報奨金 (50 ポイント) を付けます。
編集: Tenfour04 からの回答の後、状況は大幅に改善されました。アルファチャンネルは動作しているようで、変な顔の問題はなくなったようです。しかし、実際のゲームに変更を適用すると、小麦粉がゲームの地形の上に描画されることがあることに気付きました。立方体の真ん中に大きな平面を追加して、問題を説明するためにテストを更新しました。ビデオを作成しました: https://www.youtube.com/watch?v=LQhSMJfuyZY .
また、カスタム シェーダーを使用していないことも明確にしたいと思います。追加の openGl 呼び出しなしで ModelBatch.begin() および .end() メソッドのみを使用します。