Draworder OpenGL - большие объекты над меньшими
В моей игре OpenGL ES для Android я пытаюсь рисовать фигуры в последовательном порядке в соответствии с их z-значением (то есть положением в z-плоскости). Это значит сначала нарисовать самые маленькие фигуры (текстуры), а затем нарисовать больше. Я сохранил свойства фигур в массиве, и при рисовании и обновлении я использую итератор. Допустим, это массив List с его z-значениями (просто пример)
ArrayList arraylistBricks
obj (0) ... z = -20
obj (1) ... z = -37
obj (2) ... z = -31
obj (3) -...z = -20
Я сортирую этот arrayList, используя Collections.sort(..) для каждого кадра, и после сортировки список выглядит следующим образом
obj (0) ... z = -37
obj (1) ... z = -31
obj (2) ... z = -20
obj (3) -...z = -20
То есть самые маленькие фигуры находятся первыми в списке и должны быть нарисованы первыми. Я вижу, что массив действительно отсортирован, но почему-то ничего не происходит. Я хочу, чтобы меньшие фигуры были позади больших. Есть ли у GPU какой-то алгоритм порядка рисования? Я только что мигрировал из skia в opengl и был удивлен этим.
Что мне здесь не хватает? Как я мог решить проблему?
private void drawShape() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboDataListLevelSprites.get(iName).getBuff_id_vertices());
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, 0);
for (int i = 0; i < BrickProperties.get_buff_id_uvs().length; i++) {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, BrickProperties.get_buff_id_uvs()[i]);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, 0);
for (Iterator<BrickProperties> shapeIterator = arrayListBricks.iterator(); shapeIterator.hasNext(); ) {
BrickProperties bp = shapeIterator.next();
int buffIndexVal = bp.get_status_diff();
if (buffIndexVal == i) {
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, bp.getTranslateData()[0], bp.getTranslateData()[1], bp.getTranslateData()[2]);
if (bp.get_status() == 0) {
Matrix.rotateM(mModelMatrix, 0, bp.getAngleInDegrees(), 0, 1, 0);
}
render();
}
}
}
}
метод рендеринга
private void render() {
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
обновление (фрагмент)
public void update() {
Collections.sort(arrayListBricks, new Comparator<BrickProperties>() {
@Override
public int compare(BrickProperties bp2, BrickProperties bp1)
{
int result = Float.compare(bp1.getTranslateData()[2], bp2.getTranslateData()[2]);
return result;
}
});
Collections.reverse(arrayListBricks);
....
2 ответа
OpenGL ES 2 имеет встроенное тестирование глубины. Вы можете включить его, используя glEnable(GL_DEPTH_TEST)
,
Не забывайте очищать буфер каждый кадр (или проверять буфер кадров, если вы используете OpenGL ES 3).
Это значит сначала нарисовать самые маленькие фигуры (текстуры), а затем нарисовать больше.
Не делайте этого так - вы потеряете все преимущества раннего теста для уничтожения окклюдированных фрагментов до того, как они будут затенены.
Визуализируйте непрозрачные фрагменты в порядке рендеринга спереди к спине (тест глубины включен, запись глубины включена), затем просвечивайте прозрачность поверх в порядке рендеринга спереди назад (тест глубины включен, запись глубины отключена).