Оптимизация системы частиц в OpenGL ES 3.0 - PullRequest
0 голосов
/ 13 марта 2020

Существует система частиц для взрыва, похожая на фейерверк:

enter image description here

Код вершинного шейдера:

#version 300 es
uniform float u_lastTimeExplosion; // time elapsed since the explosion
// explosion center (particle coordinates are set relative to this center
uniform vec3 u_centerPosition; 
uniform float u_sizeSprite;
// particle lifetime in seconds
layout(location = 0) in float a_lifeTime;
// initial position of the particle at the time of the explosion
layout(location = 1) in vec3 a_startPosition;
layout(location = 2) in vec3 a_endPosition; // final position of the particle
out float v_lifeTime; // remaining particle lifetime
void main()
{
    // calculate particle position (algorithm from the book of D.Ginsburg, B.Purnomo)
    gl_Position.xyz = a_startPosition + (u_lastTimeExplosion * a_endPosition);
    gl_Position.xyz += u_centerPosition;
    gl_Position.w = 1.0;
    // calculate the remaining particle lifetime
    v_lifeTime = 1.0 - (u_lastTimeExplosion / a_lifeTime);
    v_lifeTime = clamp(v_lifeTime, 0.0, 1.0);
    // calculate sprite size based on remaining life time
    gl_PointSize = pow(v_lifeTime, 5.0) * u_sizeSprite;
}

Код фрагментного шейдера:

#version 300 es
precision lowp float;
in float v_lifeTime;
uniform vec4 u_color;
out vec4 fragColor;
uniform sampler2D s_texture;
void main()
{
    vec4 texColor = texture(s_texture, gl_PointCoord);
    fragColor = u_color * texColor;
    // increase sprite transparency
    fragColor.a *= v_lifeTime;
}

Три вершинных буфера используются: для массива с временем жизни частицы; массив начальных координат частиц; массив конечных координат частиц:

lifeTimeAsFloatBuffer.position(0);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO[0]);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,
        FLOAT_SIZE * numberParticles, lifeTimeAsFloatBuffer,
        GLES30.GL_STATIC_DRAW);

startPositionAsFloatBuffer.position(0);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO[1]);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,
        FLOAT_SIZE * NUM_COORDINATES * numberParticles,
        startPositionAsFloatBuffer, GLES30.GL_STATIC_DRAW);

endPositionAsFloatBuffer.position(0);
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO[2]);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,
        FLOAT_SIZE * NUM_COORDINATES * numberParticles,
        endPositionAsFloatBuffer, GLES30.GL_STATIC_DRAW);

Генерация данных вершины:

private final float[] lifeTimeData; // life time of particles
private final float[] startPositionData; // start coordinates of particles
private final float[] endPositionData; // end coordinates of particles
...
public void createDataVertex() {
    float maxLifeTime = 3.0f; 
    for (int i = 0; i < numberParticles; i ++) {
        // life time of particle, random value 0-3 second
        lifeTimeData[i] = random.nextFloat() * maxLifeTime;
    }
    float[] xyz;
    for (int i = 0; i < numberParticles * NUM_COORDINATES; i += NUM_COORDINATES) {
        xyz = getPointForSphere(startRadius); // start position particle
        startPositionData[i] = xyz[0] * aspect;
        startPositionData[i + 1] = xyz[1];
        startPositionData[i + 2] = xyz[2];
        xyz = getPointForSphere(endRadius); // end position particle
        endPositionData[i] = xyz[0] * aspect;
        endPositionData[i + 1] = xyz[1];
        endPositionData[i + 2] = xyz[2];
        }
    lifeTimeAsFloatBuffer = floatBuffer(lifeTimeData);
    startPositionAsFloatBuffer = floatBuffer(startPositionData);
    endPositionAsFloatBuffer = floatBuffer(endPositionData);
}

Передача данных в шейдер:

public void onDrawFrame(GL10 glUnused) {
    ...
    GLES30.glEnableVertexAttribArray(startPositionLink);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO[1]);
    GLES30.glVertexAttribPointer(startPositionLink, NUM_COORDINATES, 
    GLES30.GL_FLOAT, false, FLOAT_SIZE * NUM_COORDINATES, 0);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    ...
}

Проблема: на некоторых физических устройствах наблюдается замедление рендеринга (уменьшение FPS). Даже если есть несколько сотен частиц. Просмотрено сообщение: Улучшение системы частиц OpenGL . Но здесь проблема, кажется, другая.

Вопрос: Есть ли способ дальнейшей оптимизации этой системы частиц? Любой ответ / комментарий будет очень ценным.

1 Ответ

0 голосов
/ 27 марта 2020

Как оказалось, на производительность сильно влияет размер спрайта. Если начальный размер спрайта составляет от 1 до 10, то все в порядке:

GLES20.glUniform1f(sizeSpriteLink, 10f);

Если размер спрайта увеличивается, замедляется рендеринг (уменьшение FPS). Не уверен, но кажется, что увеличение размера спрайта сильно влияет на потребление ресурсов памяти graphi c. Может быть, кто-то знает об этом?

По совету Коломбо добавлен mipmap для текстуры частиц. Желательно, чтобы размер спрайта не превышал размер текстуры.

GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D)

Также лучше использовать этот тип фильтрации для увеличения производительности:

// one value is taken from the nearest pyramid level
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
        GLES20.GL_NEAREST_MIPMAP_NEAREST);
...