2012-10-19 44 views
5

我试图找到一个解决办法,让我用一个不同的属性绕Z轴的点精灵(即统一不会做)。具有不同旋转的OpenGL ES 2.0点精灵 - 在着色器中计算矩阵?

在我的应用我有每帧数百/被吸引数千点的精灵,然后将其保存在维也纳组织(很能切实最终被> 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例如)。
    • 缺点:
      • 大规模增加了我的点精灵数据的大小(从16到80字节/点4x4矩阵;到52个字节/点的3x3矩阵......我相信这是可能的使用3x3旋转矩阵?)。这可能会导致我的应用程序更快崩溃3-5次!
      • 推很多更多的计算到CPU(数百/每秒数千帧矩阵计算的)。


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

    • 优点:
      • 保持指向子画面数据大小较小(从16到20字节/点)。
      • 推动繁重矩阵数学运用到GPU。
    • 缺点:
      • 需要编写自定义GLSL函数来创建旋转矩阵。不是一个大问题,但我的数学矩阵是生疏了,所以这可能是容易出错,尤其是当我试图找出3x3矩阵解决方案...
      • 鉴于这一定会发生数百/千顶点,这是否会严重影响性能(尽管由GPU处理)?


  • 我可以实际与用于角属性(255个不同的角度将是足够的)1个字节对应。有没有什么方法可以使用某种查找方式,以便我不需要不必要地重新计算相同的旋转矩阵?我首先想到了在顶点着色器中存储常量,但我不想开始在我的着色器中放置分支语句。

任何想法,以一个好办法?

回答

2

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

  • 保持点精灵数据大小很小。
  • 旋转计算由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; 

    //... 
} 

由于没有明显的性能打这个解决方案是理想的,因为没有必要建立纹理映射/查找并消耗额外的内存,并使代码的其余部分保持简洁。

我不能说每个顶点计算矩阵都没有缺点(例如电池寿命缩短),性能可能会在不同场景中出现问题,但这对我所需要的是有好处的。

1

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

在一个不同的音符,有纹理的计算性能损失片段着色器(间接纹理查找)内的坐标。这对您的案例可能并不重要,但值得记住。

+0

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

1

这是你乘预旋转矩阵:

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); 
1

FWIW,这是3×3预先计算矩阵我得到了一致斯图尔特代码:

v_rotationMatrix = MAT3(COS,-sin, 0.0, sin,cos,0.0, (1.0-cos-sin)* 0.5,(1.0 + sin-cos)* 0.5,1.0)。

请注意,glsl矩阵是列主格式。