Menggunakan AGSL di aplikasi Android

Halaman ini membahas dasar-dasar AGSL dan berbagai cara untuk menggunakan AGSL di Android .

Shader AGSL sederhana

Kode shader Anda dipanggil untuk setiap piksel yang digambar, dan menampilkan warna piksel yang harus digunakan untuk menggambar. Shader yang sangat sederhana adalah yang selalu menampilkan satu warna; contoh ini menggunakan warna merah. Shader ditentukan di dalam String.

Kotlin

private const val COLOR_SHADER_SRC =
   """half4 main(float2 fragCoord) {
      return half4(1,0,0,1);
   }"""

Java

private static final String COLOR_SHADER_SRC =
   "half4 main(float2 fragCoord) {\n" +
      "return half4(1,0,0,1);\n" +
   "}";

Langkah berikutnya adalah membuat RuntimeShader yang diinisialisasi dengan string shader. Kode ini juga mengompilasi shader.

Kotlin

val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)

Java

RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

RuntimeShader dapat digunakan di mana pun shader Android standar dapat digunakan. Sebagai seorang sebagai contoh, Anda dapat menggunakannya untuk menggambar ke View kustom menggunakan Canvas.

Kotlin

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      canvas.drawPaint(paint) // fill the Canvas with the shader
   }
}

Java

Paint paint = new Paint();
paint.setShader(fixedColorShader);
public void onDrawForeground(@Nullable Canvas canvas) {
   if (canvas != null) {
      canvas.drawPaint(paint); // fill the Canvas with the shader
   }
}

Tindakan ini akan menggambar View merah. Anda dapat menggunakan uniform untuk meneruskan parameter warna ke shader yang akan digambar. Pertama, tambahkan warna uniform ke shader:

Kotlin

private const val COLOR_SHADER_SRC =
"""layout(color) uniform half4 iColor;
   half4 main(float2 fragCoord) {
      return iColor;
   }"""

Java

private static final String COLOR_SHADER_SRC =
   "layout(color) uniform half4 iColor;\n"+
      "half4 main(float2 fragCoord) {\n" +
      "return iColor;\n" +
   "}";

Lalu, panggil setColorUniform dari View kustom Anda untuk meneruskan warna yang diinginkan ke dalam shader AGSL.

Kotlin

fixedColorShader.setColorUniform("iColor", Color.GREEN )

Java

fixedColorShader.setColorUniform("iColor", Color.GREEN );

Sekarang, Anda mendapatkan View hijau; warna View dikontrol menggunakan dari kode di View kustom Anda, bukan disematkan dalam shader.

Sebagai gantinya, Anda dapat membuat efek gradien warna. Anda harus mengubahnya terlebih dahulu shader untuk menerima resolusi View sebagai input:

Kotlin

private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
   half4 main(float2 fragCoord) {
      float2 scaled = fragCoord/iResolution.xy;
      return half4(scaled, 0, 1);
   }"""

Java

private static final String COLOR_SHADER_SRC =
   "uniform float2 iResolution;\n" +
      "half4 main(float2 fragCoord) {\n" +
      "float2 scaled = fragCoord/iResolution.xy;\n" +
      "return half4(scaled, 0, 1);\n" +
   "}";

Menggambar gradien

Shader ini melakukan sesuatu yang sedikit rumit. Untuk setiap piksel, kode ini membuat float2 vektor yang berisi koordinat x dan y dibagi dengan resolusi, yang akan menciptakan nilai antara nol dan satu. Kemudian menggunakan vektor yang diskalakan itu untuk membuat komponen merah dan hijau dari warna yang dikembalikan.

Anda meneruskan resolusi View ke dalam shader AGSL uniform dengan memanggil setFloatUniform.

Kotlin

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      fixedColorShader.setFloatUniform("iResolution", width.toFloat(), height.toFloat())
      canvas.drawPaint(paint)
   }
}

Java

Paint paint = new Paint();
paint.setShader(fixedColorShader);
public void onDrawForeground(@Nullable Canvas canvas) {
   if (canvas != null) {
      fixedColorShader.setFloatUniform("iResolution", (float)getWidth(), (float()getHeight()));
      canvas.drawPaint(paint);
   }
}
Gradien merah dan Hijau
Gradien merah dan hijau

Menganimasikan shader

Anda dapat menggunakan teknik serupa untuk menganimasikan shader dengan memodifikasinya untuk menerima uniform iTime dan iDuration. Shader akan menggunakan nilai-nilai ini untuk membuat gelombang segitiga untuk warna, yang menyebabkan warna tersebut berputar bolak-balik melintasi nilai gradiennya.

Kotlin

private const val DURATION = 4000f
private const val COLOR_SHADER_SRC = """
   uniform float2 iResolution;
   uniform float iTime;
   uniform float iDuration;
   half4 main(in float2 fragCoord) {
      float2 scaled = abs(1.0-mod(fragCoord/iResolution.xy+iTime/(iDuration/2.0),2.0));
      return half4(scaled, 0, 1.0);
   }
"""

Java

private static final float DURATION = 4000f;
private static final String COLOR_SHADER_SRC =
   "uniform float2 iResolution;\n"+
   "uniform float iTime;\n"+
   "uniform float iDuration;\n"+
   "half4 main(in float2 fragCoord) {\n"+
      "float2 scaled = abs(1.0-mod(fragCoord/iResolution.xy+iTime/(iDuration/2.0),2.0));\n"+
      "return half4(scaled, 0, 1.0);\n"+
   "}";

Dari kode sumber tampilan kustom, ValueAnimator memperbarui Seragam iTime.

Kotlin

// declare the ValueAnimator
private val shaderAnimator = ValueAnimator.ofFloat(0f, DURATION)

// use it to animate the time uniform
shaderAnimator.duration = DURATION.toLong()
shaderAnimator.repeatCount = ValueAnimator.INFINITE
shaderAnimator.repeatMode = ValueAnimator.RESTART
shaderAnimator.interpolator = LinearInterpolator()

animatedShader.setFloatUniform("iDuration", DURATION )
shaderAnimator.addUpdateListener { animation ->
    animatedShader.setFloatUniform("iTime", animation.animatedValue as Float )
}
shaderAnimator.start()

Java

// declare the ValueAnimator
private final ValueAnimator shaderAnimator = ValueAnimator.ofFloat(0f, DURATION);

// use it to animate the time uniform
shaderAnimator.setDuration((long)DURATION);
shaderAnimator.setRepeatCount(ValueAnimator.INFINITE);
shaderAnimator.setRepeatMode(ValueAnimator.RESTART);
shaderAnimator.setInterpolator(new LinearInterpolator());

animatedShader.setFloatUniform("iDuration", DURATION );
shaderAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   public final void onAnimationUpdate(ValueAnimator animation) {
      animatedShader.setFloatUniform("iTime", (float)animation.getAnimatedValue());
   }
});
Gradien animasi Merah dan Hijau
Gradien animasi Merah dan Hijau

Melukis objek kompleks

Anda tidak perlu menggambar shader untuk mengisi latar belakang; bisa jadi digunakan di tempat mana pun yang menerima Objek Paint, seperti drawText.

Kotlin

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint)

Java

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint);
Teks gradien animasi merah dan Hijau
Teks gradien animasi Merah dan Hijau

Transformasi Shading dan Canvas

Anda dapat menerapkan transformasi Canvas tambahan pada teks berarsir, seperti kunci. Di ValueAnimator, Anda dapat memperbarui matriks untuk rotasi 3D menggunakan fitur bawaan android.graphics.Camera.

Kotlin

// in the ValueAnimator
camera.rotate(0.0f, animation.animatedValue as Float / DURATION * 360f, 0.0f)

Java

// in the ValueAnimator
camera.rotate(0.0f, (Float)animation.getAnimatedValue() / DURATION * 360f, 0.0f);

Karena Anda ingin memutar teks dari sumbu tengah, bukan dari sudut, dapatkan batas teks, lalu gunakan preTranslate dan postTranslate untuk mengubah untuk menerjemahkan teks sehingga 0,0 adalah pusat rotasi tanpa mengubah posisi teks yang digambar di layar.

Kotlin

linearColorPaint.getTextBounds(ANIMATED_TEXT, 0, ANIMATED_TEXT.length, bounds)
camera.getMatrix(rotationMatrix)
val centerX = (bounds.width().toFloat())/2
val centerY = (bounds.height().toFloat())/2
rotationMatrix.preTranslate(-centerX, -centerY)
rotationMatrix.postTranslate(centerX, centerY)
canvas.save()
canvas.concat(rotationMatrix)
canvas.drawText(ANIMATED_TEXT, 0f, 0f + bounds.height(), paint)
canvas.restore()

Java

linearColorPaint.getTextBounds(ANIMATED_TEXT, 0, ANIMATED_TEXT.length(), bounds);
camera.getMatrix(rotationMatrix);
float centerX = (float)bounds.width()/2.0f;
float centerY = (float)bounds.height()/2.0f;
rotationMatrix.preTranslate(-centerX, -centerY);
rotationMatrix.postTranslate(centerX, centerY);
canvas.save();
canvas.concat(rotationMatrix);
canvas.drawText(ANIMATED_TEXT, 0f, 0f + bounds.height(), paint);
canvas.restore();
Teks gradien animasi berputar merah dan Hijau
Teks gradasi animasi berwarna Merah dan Hijau

Menggunakan RuntimeShader dengan Jetpack Compose

Penggunaan RuntimeShader akan jauh lebih mudah jika Anda merender UI menggunakan Jetpack Compose. Dimulai dengan shader gradien yang sama dari before:

private const val COLOR_SHADER_SRC =
    """uniform float2 iResolution;
   half4 main(float2 fragCoord) {
   float2 scaled = fragCoord/iResolution.xy;
   return half4(scaled, 0, 1);
}"""

Anda dapat menerapkan shader tersebut ke ShaderBrush Anda lalu gunakan ShaderBrush sebagai parameter untuk perintah menggambar dalam Cakupan gambar Canvas.

// created as top level constants
val colorShader = RuntimeShader(COLOR_SHADER_SRC)
val shaderBrush = ShaderBrush(colorShader)

Canvas(
   modifier = Modifier.fillMaxSize()
) {
   colorShader.setFloatUniform("iResolution",
   size.width, size.height)
   drawCircle(brush = shaderBrush)
}
Lingkaran gradien AGSL Compose
Lingkaran gradien merah dan hijau

Menggunakan RuntimeShader dengan RenderEffect

Anda dapat menggunakan RenderEffect untuk menerapkan RuntimeShader kepada View induk dan semua tampilan turunan. Tindakan ini lebih mahal daripada menggambar View kustom. tapi itu memungkinkan Anda untuk dengan mudah menciptakan efek yang menggabungkan apa yang awalnya digambar menggunakan createRuntimeShaderEffect

Kotlin

view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))

Java

view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));

Parameter kedua adalah nama uniform shader yang dapat Anda eval dengan koordinat (seperti yang diteruskan dalam fragCoord) untuk mendapatkan warna asli dari RenderNode (Tampilan dan turunannya tampilan), memungkinkan Anda untuk melakukan segala macam efek.

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Tombol petak digabungkan
Tombol gabungan petak AGSL

Efek petak tercampur di atas tombol, tetapi di bawah tombol tindakan mengambang (karena berada dalam hierarki View yang berbeda).