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 color
或 gl_FragColor
)。
mediump vec4 main(in vec2 fragCoord)
坐标空间
使用 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 引入了 half
和 short
类型,它们也表示中等精度。
矢量类型可以声明为名为 <base type><columns>。您可以使用 float2
(而非 vec2
)和 bool4
(而非 bvec4
)。矩阵类型可以声明为名为 <base type><columns>x<rows>,因此是 float3x3
,而不是 mat3
。AGSL 还允许为 mat
和 vec
进行 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())