استخدام لغة AGSL في تطبيق Android

تغطي هذه الصفحة أساسيات لغة AGSL وطرقًا مختلفة لاستخدام لغة AGSL في تطبيق Android الخاص بك.

أداة تظليل AGSL بسيطة

يتم استدعاء رمز التظليل لكل بكسل مرسوم، ويعرض اللون الذي يجب طلاء البكسل به. أداة التظليل البسيطة للغاية هي أداة تعرض دائمًا لونًا واحدًا؛ يستخدم هذا المثال اللون الأحمر. يتم تحديد أداة التظليل داخل 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 تم إعداده بسلسلة Shadr. يؤدي ذلك أيضًا إلى تجميع أداة التظليل.

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 المخصّص لتمرير اللون المطلوب إلى أداة تظليل 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 إلى uniform أداة تظليل لغة AGSL من خلال طلب 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. بدءًا من نفس أداة تظليل التدرج من قبل:

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)
}
دائرة متدرجة لإنشاء لغة AGSL
دائرة متدرجة حمراء وخضراء

استخدام RuntimeShader مع عرض RenderEffect

يمكنك استخدام RenderEffect لتطبيق RuntimeShader على أحد الوالدَين View وكل المشاهدات الفرعية. وهذه تكلفة أعلى من رسم View مخصّص. ولكنّه يتيح لك بسهولة إنشاء تأثير يتضمّن ما كان يمكن رسمه في الأصل باستخدام createRuntimeShaderEffect.

Kotlin

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

Java

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

والمَعلمة الثانية هي اسم عنصر تظليل يمكنك eval باستخدام مَعلمة إحداثية (مثل التي تم تمريرها في fragCoord) للحصول على اللون الأصلي للسمة RenderNode (طريقة العرض وطرق العرض الفرعية الخاصة بها)، ما يتيح لك تنفيذ جميع أنواع التأثيرات.

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
زر مدمَج على الشبكة
شبكة AGSL مدمجة فوق زر

تأثير شبكة مختلط على زر، ولكن أسفل زر إجراء عائم (لأنه في تسلسل View هرمي مختلف).