Halaman ini membahas dasar-dasar AGSL dan berbagai cara untuk menggunakan AGSL di aplikasi Android.
Shader AGSL sederhana
Kode shader Anda dipanggil untuk setiap piksel yang digambar, dan menampilkan warna yang harus digunakan untuk menggambar piksel. Shader yang sangat sederhana adalah shader 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 objek RuntimeShader
yang diinisialisasi dengan string shader Anda. Tindakan ini juga akan mengompilasi shader.
Kotlin
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
Java
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
RuntimeShader
dapat digunakan di mana pun yang dapat dilakukan shader Android standar. Misalnya, Anda dapat menggunakannya untuk menggambar ke dalam 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
berwarna 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" + "}";
Kemudian, panggil setColorUniform
dari View
kustom untuk meneruskan warna yang diinginkan
ke shader AGSL.
Kotlin
fixedColorShader.setColorUniform("iColor", Color.GREEN )
Java
fixedColorShader.setColorUniform("iColor", Color.GREEN );
Sekarang, Anda mendapatkan View
hijau; warna View
dikontrol menggunakan
parameter dari kode dalam View
kustom, bukan disematkan dalam
shader.
Sebagai gantinya, Anda dapat membuat efek gradien warna. Pertama-tama, Anda harus mengubah
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 mewah. Untuk setiap piksel, kode ini membuat vektor float2
yang berisi koordinat x dan y yang dibagi dengan resolusi, yang
akan menghasilkan nilai antara nol dan satu. Kemudian vektor yang diskalakan tersebut digunakan untuk
membuat komponen merah dan hijau dari warna yang ditampilkan.
Anda meneruskan resolusi View
ke uniform
shader AGSL 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); } }
Menganimasikan shader
Anda dapat menggunakan teknik serupa untuk menganimasikan shader dengan mengubahnya untuk menerima seragam iTime
dan iDuration
. Shader akan menggunakan nilai ini untuk membuat
gelombang segitiga untuk warna, yang menyebabkannya berganti-ganti nilai gradien.
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
akan mengupdate
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()); } });
Melukis objek kompleks
Anda tidak perlu menggambar shader untuk mengisi latar belakang. shader dapat
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);
Transformasi Shading dan Canvas
Anda dapat menerapkan transformasi Canvas
tambahan pada teks berarsir, misalnya
rotasi. Di ValueAnimator
, Anda dapat memperbarui matriks untuk rotasi 3D
menggunakan class
android.graphics.Camera
bawaan.
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 matriks guna menerjemahkan teks sehingga 0,0 menjadi 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();
Menggunakan RuntimeShader dengan Jetpack Compose
Penggunaan RuntimeShader
akan jauh lebih mudah jika Anda merender UI menggunakan
Jetpack Compose. Memulai dengan shader gradien yang sama dari
sebelumnya:
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
. Kemudian,
gunakan ShaderBrush
sebagai parameter untuk perintah gambar 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)
}
Menggunakan RuntimeShader dengan RenderEffect
Anda dapat menggunakan
RenderEffect
untuk menerapkan
RuntimeShader
ke View
dan semua tampilan turunan. Ini lebih mahal daripada menggambar View
kustom. tetapi
memungkinkan Anda dengan mudah membuat 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 seragam shader yang dapat Anda eval
dengan
parameter koordinat (seperti yang diteruskan dalam fragCoord) untuk mendapatkan warna asli
RenderNode
(Tampilan dan tampilan
turunannya), yang memungkinkan Anda melakukan segala jenis efek.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Efek petak tercampur di atas tombol, tetapi di bawah tombol tindakan mengambang
(karena berada dalam hierarki View
yang berbeda).