בדף הזה נסביר את העקרונות הבסיסיים של AGSL ודרכים שונות לשימוש ב-AGSL ב-Android. אפליקציה.
תוכנת הצללה פשוטה של AGSL
המערכת מפעילה את קוד ההצללה לכל פיקסל שצויר ומחזיר את הצבע של הפיקסל
צריך לצייר עם. כלי ההצללה (shader) פשוט מאוד הוא כזה שתמיד מחזיר
צבע אחד; בדוגמה הזו נעשה שימוש באדום. תוכנת ההצללה מוגדרת בתוך 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" + "}";
השלב הבא הוא ליצור RuntimeShader
שאותחל במחרוזת של תוכנת ההצללה (shader). הפעולה הזו גם מבצעת הידור של תוכנת ההצללה.
Kotlin
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
Java
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
אפשר להשתמש ב-RuntimeShader
בכל מקום שבו ניתן להשתמש בכלי ההצללה הרגיל של Android. בתור
לדוגמה, אפשר להשתמש בו כדי לשרטט לתוך View
בהתאמה אישית באמצעות
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 } }
תיקח View
אדום. אפשר להשתמש ב-uniform
כדי להעביר פרמטר של צבע אל
את תוכנת ההצללה שיש לצייר. קודם כול, מוסיפים את הצבע uniform
לכלי ההצללה:
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" + "}";
לאחר מכן, קוראים לפונקציה setColorUniform
ממכשיר View
בהתאמה אישית כדי להעביר את הצבע הרצוי
לתוכנת ההצללה (shader) של AGSL.
Kotlin
fixedColorShader.setColorUniform("iColor", Color.GREEN )
Java
fixedColorShader.setColorUniform("iColor", Color.GREEN );
עכשיו מקבלים View
ירוק; הצבע View
נשלט באמצעות
מקוד ב-View
המותאם אישית במקום להיות מוטמע
שמבוסס על תוכנת ההצללה.
אפשר במקום זאת ליצור אפקט של הדרגתי של צבע. קודם צריך לשנות
תוכנת ההצללה כדי לקבל את הרזולוציה View
כקלט:
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" + "}";
שרטוט של שיפוע
תוכנת ההצללה הזו פועלת בצורה קצת מתוחכמת. לכל פיקסל נוצרת float2
שמכיל את הקואורדינטות של x ו-y חלקי הרזולוציה,
תיצור ערך בין אפס לאחד. לאחר מכן היא משתמשת בווקטור המותאם אישית כדי
לבנות את הרכיבים האדומים והירוקים של הצבע המוחזר.
מעבירים את הרזולוציה של View
ל-shader של AGSL uniform
על ידי קריאה
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); } }
אנימציה של תוכנת ההצללה
אפשר להשתמש בשיטה דומה כדי להוסיף אנימציה לכלי ההצללה על ידי שינוי שלו לקבלת מדים של iTime
ו-iDuration
. תוכנת ההצללה תשתמש בערכים האלו כדי ליצור
גל בצורת משולש להצגת הצבעים, שגורם להם לנוע קדימה ואחורה בין הערכים ההדרגתיים שלהם.
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"+ "}";
מקוד המקור של התצוגה המותאמת אישית,
ValueAnimator
מעדכנת את
מדי 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()); } });
ציור אובייקטים מורכבים
אין צורך לצייר את תוכנת ההצללה כדי למלא את הרקע. יכול להיות
בשימוש בכל מקום שמקבל
Paint
, כמו
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);
טרנספורמציות של הצללה ולוח הציור
אפשר להחיל טרנספורמציות Canvas
נוספות על הטקסט המוצלל, כמו
בסבב. ב-ValueAnimator
אפשר לעדכן מטריצה לסיבובים בתלת-ממד
באמצעות מנוע החיפוש המובנה
כיתה 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);
מאחר שברצונך לסובב את הטקסט מהציר המרכזי ולא מהפינה,
מקבלים את גבולות הטקסט ואז משתמשים ב-preTranslate
וב-postTranslate
כדי לשנות את גבולות הטקסט
במטריצה לתרגום הטקסט כך ש-0,0 הוא מרכז הסיבוב ללא
שינוי המיקום שבו הטקסט מצויר על המסך.
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();
שימוש ב-RuntimeShader עם Jetpack Compose
אפילו קל יותר להשתמש ב-RuntimeShader
אם מתבצע רינדור של ממשק המשתמש באמצעות
Jetpack Compose. מתחיל עם אותו צבעי הצללה הדרגתיים מ-
before:
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
half4 main(float2 fragCoord) {
float2 scaled = fragCoord/iResolution.xy;
return half4(scaled, 0, 1);
}"""
אפשר להחיל את תוכנת ההצללה הזאת
ShaderBrush
שלך
להשתמש ב-ShaderBrush
כפרמטר בפקודות השרטוט
היקף השרטוט של 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)
}
שימוש ב-RuntimeShader עם Render לחשבונות
אפשר להשתמש
RenderEffect
כדי להחיל
RuntimeShader
להורה View
וגם כל הצפיות לילדים. פעולה זו יקרת יותר משרטוט View
בהתאמה אישית. אבל
היא מאפשרת ליצור בקלות אפקט שמשלב את מה
במקור הוא שורטט באמצעות
createRuntimeShaderEffect
Kotlin
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
Java
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));
הפרמטר השני הוא השם של מדית הצללה שאפשר eval
באמצעות
coordinate (למשל, העברה ב-fragCoord) כדי לקבל את הצבע המקורי
של
RenderNode
(התצוגה המפורטת והצאצא שלה
), וכך לבצע כל מיני סוגים של אפקטים.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
אפקט רשת משולב מעל לחצן, אבל מתחת ללחצן פעולה צף
(כי הוא בהיררכיה שונה של View
).