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); } }
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()); } });
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);
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();
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)
}
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);
Efek petak tercampur di atas tombol, tetapi di bawah tombol tindakan mengambang
(karena berada dalam hierarki View
yang berbeda).