استخدام لغة 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. الذي تم إعداده باستخدام سلسلة التظليل. يؤدي هذا أيضًا إلى تجميع أداة التظليل.

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 إلى أداة تظليل 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)
}
دائرة تدرج الألوان AGSL Compose
دائرة متدرجة حمراء وأخضر

استخدام 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 هرمي مختلف).