AGSL 与 GLSL 之间的区别

AGSL 和 GLSL 的语法非常相似,只需极少的更改即可将许多 GLSL fragment 着色器效果引入 Android。AGSL 将其 GLSL 功能集修复为 GLSL ES 1.0(OpenGL ES 2.0 使用的着色语言),以尽可能扩大设备覆盖范围。

GLSL fragment 着色器可以控制光栅器和混合硬件之间 GPU 的整个行为。该着色器会执行所有计算颜色的工作,它生成的颜色正是馈送给流水线混合阶段的颜色。使用 AGSL 编写着色器时,您正在对 Android 图形管道的一个阶段进行编程。很多语言差异都源于这种语言。

着色器执行

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

mediump vec4 main(in vec2 fragCoord)

坐标空间

GLSL 与 AGSL 坐标空间

使用 GLSL 绘制的着色器与使用 AGSL 绘制近乎完全相同的着色器

默认情况下,AGSL 和 GLSL 使用不同的坐标空间。在 GLSL 中,fragment 坐标 (fragCoord) 相对于左下角而言。AGSL 与画布的屏幕坐标系匹配,这意味着 Y 轴从左上角开始。如果需要,您可以在这两个空间之间进行转换,方法是传入统一分辨率并使用 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 还允许为 matvec 进行 GLSL 样式的声明,这些类型会映射到其等效的浮点数。

预处理器

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

颜色空间

Android 应用可通过颜色进行管理。画布的颜色空间决定了绘制的有效颜色空间。源内容(如着色器,包括 BitmapShader)也具有颜色空间。

对于某些效果(例如物理准确光照),应在线性颜色空间中进行计算。为帮助完成此任务,AGSL 提供了以下固有函数:

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

这些颜色会在工作颜色空间和 Android 的 LINEAR_EXTENDED_SRGB 颜色空间之间转换颜色。该空间使用 sRGB 原色(色域)和线性传递函数。它使用扩展范围值(低于 0.0 且高于 1.0)表示 sRGB 色域以外的值。

制服

由于 AGSL 不知道 uniform 是否包含颜色,因此它不会自动对其应用颜色转换。您可以为 half4/float4/vec4 添加 layout(color) 标签,以便让 Android 知道将制服颜色用作颜色,从而让 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())