【问题标题】:Particle system optimization in OpenGL ES 3.0OpenGL ES 3.0 中的粒子系统优化
【发布时间】:2020-03-30 16:17:19
【问题描述】:

有一个爆炸的粒子系统,类似于烟花:

顶点着色器代码:

#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 降低)。哪怕有几百个粒子。看了一个帖子:Improve Particle system OpenGL。 但这里的问题似乎有所不同。

问题:有没有办法进一步优化这个粒子系统?任何答案/评论都会非常有价值。

【问题讨论】:

  • 那么,只有几百个粒子会导致减速?似乎令人惊讶,因为它相对微不足道。你能确认你是在一个draw call中发出它们吗?另外,您的纹理是否合理(即它要么很小,要么很大但有 mipmap)?
  • @Columbo 你好!谢谢!最有可能的是,所有这些都在一次绘制调用中 - 为问题添加了示例。纹理 - 黑色背景上的绿色圆圈,文件 *.png,4 KB。未使用 Mipmap。如果我需要使用mipmap,请写下你的答案,我会标记为正确答案。
  • @Columbo 也可以使用 VAO 吗?
  • @Columbo 减速通常发生在一次有几个爆炸时。
  • 纹理的分辨率比 png 的文件大小更重要。如果纹理很大(例如 1024x1024),Mipmapping 可能会有很大帮助,如果纹理很小(例如 32x32),则可能没有区别。无论如何我都会打开它。

标签: java android opengl-es-3.0


【解决方案1】:

事实证明,性能受精灵大小的影响很大。如果精灵的初始大小是从 1 到 10,那么一切都很好:

GLES20.glUniform1f(sizeSpriteLink, 10f);

如果精灵的大小增加,则渲染速度会变慢(FPS 降低)。不确定,但似乎增加精灵的大小会极大地影响图形内存资源的消耗。也许有人知道这件事?

根据 Columbo 的建议,为粒子纹理添加了 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);

【讨论】:

    猜你喜欢
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    相关资源
    最近更新 更多