استفاده از AGSL در برنامه اندروید

این صفحه اصول AGSL و روش های مختلف استفاده از AGSL را در برنامه اندروید شما پوشش می دهد.

یک سایه زن ساده AGSL

کد سایه زن شما برای هر پیکسل ترسیم شده فراخوانی می شود و رنگی را که پیکسل باید با آن رنگ آمیزی شود برمی گرداند. سایه زن بسیار ساده آن است که همیشه یک رنگ را برمی گرداند. این مثال از رنگ قرمز استفاده می کند. سایه زن در داخل یک String تعریف شده است.

کاتلین

private const val COLOR_SHADER_SRC =
   """half4 main(float2 fragCoord) {
      return half4(1,0,0,1);
   }"""

جاوا

private static final String COLOR_SHADER_SRC =
   "half4 main(float2 fragCoord) {\n" +
      "return half4(1,0,0,1);\n" +
   "}";

گام بعدی ایجاد یک شی RuntimeShader است که با رشته سایه زن شما مقداردهی اولیه شده است. این نیز سایه زن را کامپایل می کند.

کاتلین

val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)

جاوا

RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

RuntimeShader شما می تواند در هر جایی که یک سایه زن استاندارد اندروید می تواند استفاده شود. به عنوان مثال، می‌توانید از آن برای کشیدن به یک View سفارشی با استفاده از Canvas استفاده کنید.

کاتلین

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      canvas.drawPaint(paint) // fill the Canvas with the shader
   }
}

جاوا

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 را به سایه زن اضافه کنید:

کاتلین

private const val COLOR_SHADER_SRC =
"""layout(color) uniform half4 iColor;
   half4 main(float2 fragCoord) {
      return iColor;
   }"""

جاوا

private static final String COLOR_SHADER_SRC =
   "layout(color) uniform half4 iColor;\n"+
      "half4 main(float2 fragCoord) {\n" +
      "return iColor;\n" +
   "}";

سپس، setColorUniform از View سفارشی خود فراخوانی کنید تا رنگ مورد نظر را به سایه زن AGSL منتقل کنید.

کاتلین

fixedColorShader.setColorUniform("iColor", Color.GREEN )

جاوا

fixedColorShader.setColorUniform("iColor", Color.GREEN );

اکنون، یک View سبز رنگ دریافت می کنید. رنگ View با استفاده از پارامتری از کد در View سفارشی شما به جای تعبیه شدن در سایه زن کنترل می شود.

به جای آن می توانید یک افکت گرادیان رنگ ایجاد کنید. ابتدا باید سایه زن را تغییر دهید تا وضوح View را به عنوان ورودی بپذیرید:

کاتلین

private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
   half4 main(float2 fragCoord) {
      float2 scaled = fragCoord/iResolution.xy;
      return half4(scaled, 0, 1);
   }"""

جاوا

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 تقسیم بر وضوح است که مقداری بین صفر و یک ایجاد می کند. سپس از آن بردار مقیاس شده برای ساخت اجزای قرمز و سبز رنگ برگشتی استفاده می کند.

با فراخوانی setFloatUniform ، وضوح View به یک uniform AGSL منتقل می‌کنید.

کاتلین

val paint = Paint()
paint.shader = fixedColorShader
override fun onDrawForeground(canvas: Canvas?) {
   canvas?.let {
      fixedColorShader.setFloatUniform("iResolution", width.toFloat(), height.toFloat())
      canvas.drawPaint(paint)
   }
}

جاوا

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 استفاده کنید. سایه‌زن از این مقادیر برای ایجاد موج مثلثی برای رنگ‌ها استفاده می‌کند و باعث می‌شود آنها در مقادیر گرادیان خود به جلو و عقب بچرخند.

کاتلین

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);
   }
"""

جاوا

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 را به روز می کند.

کاتلین

// 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()

جاوا

// 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 .

کاتلین

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint)

جاوا

canvas.drawText(ANIMATED_TEXT, TEXT_MARGIN_DP, TEXT_MARGIN_DP + bounds.height(),
   paint);
متن گرادیان متحرک قرمز و سبز
متن گرادیان متحرک قرمز و سبز

تغییر سایه و بوم

می‌توانید تبدیل‌های Canvas اضافی را روی متن سایه‌دار خود اعمال کنید، مانند چرخش. در ValueAnimator ، می‌توانید یک ماتریس را برای چرخش‌های سه‌بعدی با استفاده از کلاس android.graphics.Camera داخلی به‌روزرسانی کنید.

کاتلین

// in the ValueAnimator
camera.rotate(0.0f, animation.animatedValue as Float / DURATION * 360f, 0.0f)

جاوا

// in the ValueAnimator
camera.rotate(0.0f, (Float)animation.getAnimatedValue() / DURATION * 360f, 0.0f);

از آنجایی که می خواهید متن را از محور مرکزی بچرخانید تا از گوشه، کران های متن را دریافت کنید و سپس از preTranslate و postTranslate برای تغییر ماتریس برای ترجمه متن استفاده کنید تا 0,0 مرکز چرخش بدون تغییر موقعیت باشد. متن روی صفحه کشیده می شود.

کاتلین

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()

جاوا

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

اگر UI خود را با استفاده از Jetpack Compose رندر می کنید، استفاده از RuntimeShader حتی ساده تر است. شروع با همان شیدر گرادیان قبلی:

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 ترسیم شده است، در خود جای دهد.

کاتلین

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

جاوا

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

پارامتر دوم نام یک فرم سایه زن است که می توانید با یک پارامتر مختصات (مانند پاس در fragCoord) آن eval تا رنگ اصلی RenderNode (نما و نماهای فرزند آن) را به دست آورید و به شما امکان می دهد انواع مختلفی را انجام دهید. اثرات

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
شبکه روی دکمه ترکیب شده است
شبکه AGSL روی دکمه ترکیب شده است

یک جلوه شبکه ای که روی یک دکمه ترکیب شده است، اما در زیر یک دکمه اکشن شناور (زیرا در یک سلسله مراتب View متفاوت است).