【问题标题】:OpenGL ES 2.0 point sprites with distinct rotations - calculate matrix in shader?具有不同旋转的OpenGL ES 2.0点精灵-在着色器中计算矩阵?
【发布时间】:2012-10-10 09:20:57
【问题描述】:

我正在尝试找到一种解决方案,该解决方案允许我使用不同的属性围绕 z 轴旋转点精灵(即,uniform 不会这样做)。

在我的应用程序中,我每帧绘制了成百上千个点精灵,然后将它们存储在 VBO 中(很可能最终会超过 1,000,000)。因此,我正在寻找内存使用和性能之间的最佳折衷方案。

当前的顶点和片段着色器如下所示:

// VERTEX SHADER
attribute vec4 a_position;
attribute vec4 a_color;
attribute float a_size;
uniform mat4 u_mvpMatrix;
varying vec4 v_color;

void main()
{
    v_color = a_color;
    gl_Position = u_mvpMatrix * a_position;
    gl_PointSize = a_size;
}


// FRAGMENT SHADER
precision mediump float;
uniform sampler2D s_texture;
varying vec4 v_color;

void main()
{
    vec4 textureColor = texture2D(s_texture, gl_PointCoord);
    gl_FragColor = v_color * textureColor;
}

我目前可以想象以下几种可能性:

  • 为我的点精灵数据添加一个mat4 rotMatrix 属性。将此传递给片段着色器并旋转每个片段:

    vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy
    gl_FragColor = v_color * texture2D(s_texture, texCoord);
    
    • 优点:
      • 保持着色器简单。
      • 在着色器之外计算矩阵的简单代码(例如使用GLKit)。
    • 缺点:
      • 大幅增加了我的点精灵数据的大小(对于 4x4 矩阵从 16 字节/点增加到 80 字节/点;对于 3x3 矩阵从 52 字节/点...我相信可以使用 3x3 旋转矩阵?)。这可能会导致我的应用提前 3-5 次崩溃!
      • 将更多计算推送到 CPU 上(每帧数十/千次矩阵计算)。


  • 在我的点精灵数据中添加float angle属性,然后在顶点着色器中计算旋转矩阵。如上所述将旋转矩阵传递给片段着色器。

    • 优点:
      • 使点精灵数据大小保持较小(从 16 到 20 字节/点)。
      • 将繁重的矩阵数学推送到 GPU。
    • 缺点:
      • 需要编写自定义 GLSL 函数来创建旋转矩阵。不是一个大问题,但我的矩阵数学很生疏,所以这可能很容易出错,尤其是当我试图找出 3x3 矩阵解决方案时......
      • 鉴于这必须发生在成百上千个顶点上,这是否会严重拖累性能(尽管由 GPU 处理)?


  • 我可以实际处理 1 个字节的角度属性(255 个不同的角度就足够了)。有什么办法可以使用某种查找,这样我就不需要不必要地重新计算相同的旋转矩阵?在顶点着色器中存储常量是我的第一个想法,但我不想开始在我的着色器中放置分支语句。

有什么好的方法吗?

【问题讨论】:

    标签: ios matrix opengl-es-2.0 shader point-sprites


    【解决方案1】:

    我最终采用的解决方案是问题的第二个:计算顶点着色器中的旋转矩阵。这样做有以下优点:

    • 保持小精灵数据大小。
    • 旋转计算由 GPU 执行。

    我猜测的缺点似乎并不适用。即使在第一代 iPad 上运行,我也没有注意到性能下降。 GLSL 中的矩阵计算有些麻烦,但效果很好。为了其他尝试做同样事情的人的利益,这里是顶点着色器的相关部分:

    //...
    attribute float a_angle;
    varying mat4 v_rotationMatrix;
    
    void main()
    {
        //...
    
        float cos = cos(a_angle);
        float sin = sin(a_angle);
        mat4 transInMat = mat4(1.0, 0.0, 0.0, 0.0,
                               0.0, 1.0, 0.0, 0.0,
                               0.0, 0.0, 1.0, 0.0,
                               0.5, 0.5, 0.0, 1.0);
        mat4 rotMat = mat4(cos, -sin, 0.0, 0.0,
                           sin, cos, 0.0, 0.0,
                           0.0, 0.0, 1.0, 0.0,
                           0.0, 0.0, 0.0, 1.0);
        mat4 resultMat = transInMat * rotMat;
        resultMat[3][0] = resultMat[3][0] + resultMat[0][0] * -0.5 + resultMat[1][0] * -0.5;
        resultMat[3][1] = resultMat[3][1] + resultMat[0][1] * -0.5 + resultMat[1][1] * -0.5;
        resultMat[3][2] = resultMat[3][2] + resultMat[0][2] * -0.5 + resultMat[1][2] * -0.5;
        v_rotationMatrix = resultMat;
    
        //...
    }
    

    鉴于没有明显的性能影响,这个解决方案是理想的,因为不需要创建纹理贴图/查找并消耗额外的内存,并且它可以保持其余代码的干净和简单。

    我不能说为每个顶点计算矩阵没有缺点(例如,电池寿命缩短),并且在不同情况下性能可能是个问题,但它对我的需要有好处。

    【讨论】:

      【解决方案2】:

      您是否考虑过使用不同的预先计算和旋转的纹理(纹理图集)?如果只有几个角度就足以达到您想要达到的效果,这将是一个非常快速的解决方案。

      另一方面,在片段着色器中计算纹理坐标(间接纹理查找)会降低性能。这对您的情况可能并不重要,但值得牢记。

      【讨论】:

      • 嗨,我还没有实际尝试过,这听起来是一个合理的解决方案。自从发布问题以来,我已经解决了我的问题,但忘记发布答案(感谢您提醒我!)。我最终计算了顶点着色器中的旋转矩阵。即使在第一代 iPad 上,性能影响也可以忽略不计,它使我的数据结构保持较小。您的方法也可以工作,但一个缺点是它会增加我的纹理内存使用量(每个精灵需要超过 200 次旋转)。我喜欢顶点着色器解决方案,因为我不需要摆弄精灵,而且性能是可以接受的。
      【解决方案3】:

      这是你的预乘旋转矩阵:

      v_rotationMatrix = mat3(cos, sin, 0.0,
                              -sin, cos, 0.0,
                              (sin-cos+1.0)*0.5, (-sin-cos+1.0)*0.5, 1.0);
      

      【讨论】:

        【解决方案4】:

        FWIW,这是我得到的与 Stuart 的代码匹配的 3x3 预计算矩阵:

        v_rotationMatrix = mat3(cos, -sin, 0.0, 罪, cos, 0.0, (1.0-cos-sin)*0.5, (1.0+sin-cos)*0.5, 1.0);

        请注意, glsl 矩阵采用列优先格式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多