Opengl es 为什么雪碧配料器更快?

Opengl es 为什么雪碧配料器更快?,opengl-es,Opengl Es,我现在正在读(马里奥·泽克纳) 在阅读OpenGL ES 1.0的2D游戏时,作者介绍了SpriteBatcher的概念,它为每个精灵提供坐标和角度。SpriteBatcher然后计算sprite矩形的最终坐标,并将其放入单个大缓冲区 在渲染方法中,SpriteBatcher为所有精灵设置一次状态(纹理、混合、顶点缓冲区、纹理坐标缓冲区)。所有精灵使用相同的纹理,但不使用相同的纹理坐标 这种行为的优点是: 渲染管道不会暂停,因为渲染所有精灵时没有状态更改 OpenGL调用更少。(=更少的JNI

我现在正在读(马里奥·泽克纳)

在阅读OpenGL ES 1.0的2D游戏时,作者介绍了SpriteBatcher的概念,它为每个精灵提供坐标和角度。SpriteBatcher然后计算sprite矩形的最终坐标,并将其放入单个大缓冲区

在渲染方法中,SpriteBatcher为所有精灵设置一次状态(纹理、混合、顶点缓冲区、纹理坐标缓冲区)。所有精灵使用相同的纹理,但不使用相同的纹理坐标

这种行为的优点是:

  • 渲染管道不会暂停,因为渲染所有精灵时没有状态更改
  • OpenGL调用更少。(=更少的JNI开销)
但我看到了一个主要的缺点:

  • 对于旋转,CPU必须计算正弦和余弦,并对每个精灵执行16次乘法。据我所知,计算正弦和余弦是非常昂贵和缓慢的
但是SpriteBatcher方法要比使用大量的glRotate/glTranslate逐个渲染精灵快得多

最后,我的问题是:

  • 为什么速度更快?OpenGL状态更改真的那么昂贵吗
  • GPU针对矢量乘法和旋转进行了优化,而CPU则没有。为什么这不重要
  • 有人会在带有专用GFX卡的桌面上使用SpriteBatcher吗
  • SpriteBatcher是否存在效率低下的问题

我不知道SpriteBatcher的情况,但看看您提供的信息,我的想法如下:

  • 它更快,因为它使用更少的状态更改,更重要的是,使用更少的draw调用。移动平台对每帧的抽奖呼叫数有特别严格的限制
  • 这并不重要,因为他们可能正在使用CPU进行旋转。一、 就个人而言,没有理由不使用GPU,这将大大加快速度并消除带宽负载
  • 考虑到第1点,我想这仍然是一个很好的优化
  • 我可以想到两种极端情况:精灵太少,或者合成纹理(包含所有旋转的精灵)变得太大(移动设备的大小限制较低)
但我看到了一个主要的缺点:

  • 对于旋转,CPU必须计算正弦和余弦,并对每个精灵执行16次乘法。据我所知,计算正弦和余弦是非常昂贵和缓慢的
实际上,sin和cos非常快,在现代体系结构上,如果管道以前没有停止,它们需要1个时钟周期来执行。但是,如果每个精灵都单独旋转,并且使用普通的平截头体透视投影,则此代码的作者不知道他的线性代数

如果你回忆起modelview矩阵将线性局部/世界坐标映射到眼睛空间,那么整个任务可以简化很多。旋转位于左上角的3×3子矩阵中,该列构成局部基向量。通过取这个子矩阵的逆矩阵,你就得到了精确的向量,你需要这些向量作为精灵的基础,将平面映射到眼睛空间。如果仅应用旋转(和缩放,可能),左上角3×3的倒数就是转置;因此,通过使用左上角的3×3行作为精灵基础,您可以在不进行任何三角运算的情况下获得该效果:

/* populates the currently bound VBO with sprite geometry */
void populate_sprites_VBO(std::vector<vec3> sprite_positions)
{
    GLfloat mv[16];
    GLfloat sprite_left[3];
    GLfloat sprite_up[3];

    glGetMatrixf(GL_MODELVIEW_MATRIX, mv);

    for(int i=0; i<3; i++) {
        sprite_left[i] = mv[i*4];
        sprite_up[i]   = mv[i*4 + 4];
    }

    std::vector<GLfloat> sprite_geom;
    for(std::vector<vec3>::iterator sprite=sprite_positions.begin(), end=sprite_positions.end();
        sprite != end;
        sprite++ ){
           sprite_geom.append(sprite->x + (-sprite_left[0] - sprite_up[0])*sprite->scale);
           sprite_geom.append(sprite->y + (-sprite_left[1] - sprite_up[1])*sprite->scale);
           sprite_geom.append(sprite->z + (-sprite_left[2] - sprite_up[2])*sprite->scale);

           sprite_geom.append(sprite->x + ( sprite_left[0] - sprite_up[0])*sprite->scale);
           sprite_geom.append(sprite->y + ( sprite_left[1] - sprite_up[1])*sprite->scale);
           sprite_geom.append(sprite->z + ( sprite_left[2] - sprite_up[2])*sprite->scale);


           sprite_geom.append(sprite->x + ( sprite_left[0] + sprite_up[0])*sprite->scale);
           sprite_geom.append(sprite->y + ( sprite_left[1] + sprite_up[1])*sprite->scale);
           sprite_geom.append(sprite->z + ( sprite_left[2] + sprite_up[2])*sprite->scale);


           sprite_geom.append(sprite->x + (-sprite_left[0] + sprite_up[0])*sprite->scale);
           sprite_geom.append(sprite->y + (-sprite_left[1] + sprite_up[1])*sprite->scale);
           sprite_geom.append(sprite->z + (-sprite_left[2] + sprite_up[2])*sprite->scale);
    }
    glBufferData(GL_ARRAY_BUFFER, 
                 sprite_positions.size() * sizeof(sprite_positions[0]), &sprite_positions[0], 
                 GL_DRAW_STREAM);
}    
/*使用精灵几何体填充当前绑定的VBO*/
void填充精灵VBO(标准::矢量精灵位置)
{
glmv[16];
GLfloat sprite_左[3];
GLfloat sprite_up[3];
GLGETMARIxF(GLU模型视图矩阵,mv);
对于(int i=0;ix+(-sprite_left[0]-sprite_up[0])*sprite->scale);
精灵几何附加(精灵->y+(-sprite\u left[1]-sprite\u up[1])*精灵->缩放);
精灵几何附加(精灵->z+(-sprite\u left[2]-sprite\u up[2])*精灵->缩放);
精灵几何附加(精灵->x+(精灵左[0]-精灵向上[0])*精灵->缩放);
精灵几何附加(精灵->y+(精灵左[1]-精灵向上[1])*精灵->缩放);
精灵几何附加(精灵->z+(精灵左[2]-精灵向上[2])*精灵->缩放);
精灵几何附加(精灵->x+(精灵左[0]+精灵上[0])*精灵->比例);
精灵几何附加(精灵->y+(精灵左[1]+精灵向上[1])*精灵->缩放);
精灵几何附加(精灵->z+(精灵左[2]+精灵向上[2])*精灵->缩放);
精灵几何附加(精灵->x+(-sprite\u left[0]+sprite\u up[0])*精灵->缩放);
精灵几何附加(精灵->y+(-sprite\u left[1]+sprite\u up[1])*精灵->缩放);
精灵几何附加(精灵->z+(-sprite\u left[2]+sprite\u up[2])*精灵->缩放);
}
glBufferData(GL_数组_缓冲区,
sprite_positions.size()*sizeof(sprite_positions[0]),&sprite_positions[0],
GL(绘制流);
}    
如果着色器可用,则可以使用几何体着色器或顶点着色器,而不是在每个帧上重建精灵数据。几何体着色器将采用位置、比例、纹理等向量并发射四边形。使用顶点着色器可以发送大量[-1,1]四边形,其中每个顶点都会携带它所属精灵的中心位置作为附加的vec3属性


最后,我的问题是:

  • 为什么速度更快?OpenGL状态更改真的那么昂贵吗
有些状态更改非常昂贵,您将尽可能避免这些更改。切换纹理非常昂贵,而切换着色器稍微昂贵

  • GPU针对矢量乘法和旋转进行了优化,而CPU则没有。为什么这不重要
这不是GPU和CPU之间的区别。GPU与CPU不同之处在于,它对CPU执行相同的操作序列