این صفحه اصول AGSL و روشهای مختلف استفاده از AGSL در برنامه Android را پوشش میدهد.
سایهزن 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 شما میتوان در هر جایی که از سایهزن استاندارد Android استفاده میشود استفاده کرد. بهعنوان
مثال، میتوانید از آن برای طراحی در 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 را به سایهزن AGSL uniform منتقل میکنید.
کاتلین
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
میتوانید تبدیلهای 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
اگر میانای کاربر را بااستفاده از 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)
}
استفاده از 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);
جلوه شبکهای که روی دکمه ترکیب شده است، اما زیر دکمه کنش شناور قرار دارد
(چون در سلسلهمراتب View متفاوتی قرار دارد).