AGSL 与 GLSL 之间的差异

AGSL 和 GLSL 在语法上非常相似,只需进行少量更改即可将许多 GLSL 片段着色器效果引入 Android。AGSL 将其 GLSL 功能集固定在 GLSL ES 1.0(OpenGL ES 2.0 使用的着色语言),以实现最大的设备覆盖范围。

GLSL 片段着色器控制光栅化器和混合硬件之间 GPU 的全部行为。该着色器完成计算颜色的所有工作,并且它生成的颜色正是传递到流水线混合阶段的颜色。当您在 AGSL 中编写着色器时,您正在对 Android 图形流水线的一个阶段进行编程。许多语言差异都源于此。

着色器执行

与 GLSL 着色器类似,AGSL 着色器在 main 函数中开始执行。与 GLSL 不同,该函数将“本地”坐标中的着色器位置作为参数。这类似于 gl_FragCoord,但这些坐标可能在调用您的着色器之前已经过转换,而不是帧缓冲区坐标。然后,您的着色器以中精度或高精度返回像素颜色,格式为 vec4(类似于 GLSL 中的 out vec4 colorgl_FragColor)。

mediump vec4 main(in vec2 fragCoord)

坐标空间

GLSL vs AGSL coordinate spaces

使用 GLSL 绘制的着色器 与 使用 AGSL 绘制的几乎相同的着色器

AGSL 和 GLSL 默认使用不同的坐标空间。在 GLSL 中,片段坐标 (fragCoord) 相对于左下角。AGSL 匹配 Canvas 的屏幕坐标系,这意味着 Y 轴从左上角开始。如果需要,您可以通过将分辨率作为 uniform 传入,并使用 resolution.y - fragCoord.y 作为 Y 轴值来在这两个空间之间进行转换。或者,您可以将局部变换矩阵应用于您的着色器。

// AGSL to GLSL coordinate space transformation matrix
val localMatrix = Matrix()
localMatrix.postScale(1.0f, -1.0f)
localMatrix.postTranslate(0.0f, viewHeight)
gridShader.setLocalMatrix(localMatrix)

精度和类型

支持 GLSL 兼容的精度修饰符,但 AGSL 引入了 halfshort 类型,它们也表示中等精度。

向量类型可以声明为命名格式 <base type><columns>。您可以使用 float2 代替 vec2,使用 bool4 代替 bvec4。矩阵类型可以声明为命名格式 <base type><columns>x<rows>,因此使用 float3x3 代替 mat3。AGSL 还允许使用 GLSL 风格的 matvec 声明,这些类型会映射到其浮点等效类型。

预处理器

AGSL 不支持 GLSL 风格的预处理器指令。将 #define 语句转换为 const 变量。AGSL 的编译器支持对 const 变量进行常量折叠和分支消除,因此这些操作会很高效。

颜色空间

Android 应用是色彩管理的。Canvas 的颜色空间决定了绘制的工作颜色空间。源内容(如着色器,包括 BitmapShader)也有颜色空间。

对于某些效果,例如物理精确的光照,数学计算应在线性颜色空间中进行。为了帮助实现这一点,AGSL 提供了这些内置函数

half3 toLinearSrgb(half3 color)
half3 fromLinearSrgb(half3 color)

这些函数在工作颜色空间和 Android 的 LINEAR_EXTENDED_SRGB 颜色空间之间进行颜色转换。该空间使用 sRGB 颜色基色(色域)和线性传输函数。它使用扩展范围值(低于 0.0 和高于 1.0)表示超出 sRGB 色域的值。

Uniform

由于 AGSL 不知道 uniform 是否包含颜色,因此它不会自动对其进行颜色转换。您可以使用 layout(color) 标记 half4/float4/vec4,这样 Android 就能知道该 uniform 将用作颜色,从而允许 Android 将 uniform 值转换为工作颜色空间。

在 AGSL 中,按如下方式声明 uniform

layout(color) uniform half4 iColor;  // Input color
uniform float2 iResolution;          // Viewport resolution (pixels)

在 Android 代码中,您可以按如下方式设置 uniform

shader.setColorUniform("iColor", Color.GREEN)
shader.setFloatUniform("iResolution", canvas.width.toFloat(), canvas.height.toFloat())