Sử dụng AGSL trong ứng dụng Android của bạn

Trang này trình bày các kiến thức cơ bản về AGSL và các cách sử dụng AGSL trong Android .

Chương trình đổ bóng AGSL đơn giản

Mã chương trình đổ bóng được gọi cho mỗi pixel đã vẽ và trả về màu mà pixel thường được vẽ. Chương trình đổ bóng cực kỳ đơn giản là chương trình luôn trả về một màu sắc duy nhất; ví dụ này sử dụng màu đỏ. Chương trình đổ bóng được xác định bên trong 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" +
   "}";

Bước tiếp theo là tạo RuntimeShader được khởi tạo bằng chuỗi chương trình đổ bóng. Thao tác này cũng biên dịch chương trình đổ bóng.

Kotlin

val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)

Java

RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

Bạn có thể sử dụng RuntimeShader ở mọi nơi chương trình đổ bóng Android tiêu chuẩn có thể. Là một Ví dụ: bạn có thể dùng đối tượng này để vẽ vào View tuỳ chỉnh bằng cách sử dụng 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
   }
}

Thao tác này sẽ vẽ một View màu đỏ. Bạn có thể sử dụng uniform để truyền tham số màu vào chương trình đổ bóng cần vẽ. Trước tiên, hãy thêm màu uniform vào chương trình đổ bóng:

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

Sau đó, hãy gọi setColorUniform từ View tuỳ chỉnh để truyền màu mong muốn vào chương trình đổ bóng AGSL.

Kotlin

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

Java

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

Bây giờ, bạn sẽ thấy một View màu xanh lục; màu View được kiểm soát bằng cách sử dụng từ mã trong View tuỳ chỉnh của bạn thay vì được nhúng trong chương trình đổ bóng.

Thay vào đó, bạn có thể tạo hiệu ứng chuyển màu. Trước tiên, bạn cần thay đổi chương trình đổ bóng để chấp nhận độ phân giải View làm dữ liệu đầu vào:

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

Vẽ dải chuyển màu

Chương trình đổ bóng này thực hiện tác vụ hơi lạ mắt. Đối với mỗi pixel, thao tác này sẽ tạo một float2 vectơ chứa toạ độ x và y chia cho độ phân giải, tức là sẽ tạo ra một giá trị trong khoảng từ 0 đến 1. Sau đó, phương pháp này sử dụng vectơ được điều chỉnh theo tỷ lệ đó để tạo các thành phần màu đỏ và màu xanh lục của màu trả về.

Bạn truyền độ phân giải của View vào chương trình đổ bóng AGSL uniform bằng cách gọi 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);
   }
}
Chuyển màu đỏ và xanh lục
Độ dốc màu đỏ và xanh lục

Tạo ảnh động cho chương trình đổ bóng

Bạn có thể sử dụng kỹ thuật tương tự để tạo ảnh động cho chương trình đổ bóng bằng cách sửa đổi chương trình này để nhận được các lớp đồng nhất iTimeiDuration. Chương trình đổ bóng sẽ sử dụng các giá trị này để tạo sóng tam giác cho các màu, khiến chúng luân chuyển qua lại giữa các giá trị độ dốc.

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

Từ mã nguồn của chế độ xem tuỳ chỉnh, ValueAnimator cập nhật Đồng nhất 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());
   }
});
Độ dốc có hiệu ứng động màu đỏ và xanh lục
Độ dốc ảnh động màu đỏ và xanh lục

Vẽ các đối tượng phức tạp

Bạn không cần phải vẽ chương trình đổ bóng để tô nền; có thể được sử dụng ở bất kỳ nơi nào chấp nhận Đối tượng Paint, chẳng hạn như 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);
Văn bản chuyển màu động màu đỏ và xanh lục
Văn bản chuyển màu có hiệu ứng động màu đỏ và xanh lục

Hiệu ứng đổ bóng và biến đổi Canvas

Bạn có thể áp dụng thêm các phép biến đổi Canvas trên văn bản được tô bóng, chẳng hạn như xoay. Trong ValueAnimator, bạn có thể cập nhật ma trận để xoay 3D bằng cách sử dụng các tính năng Lớp 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);

Vì bạn muốn xoay văn bản từ trục giữa thay vì từ góc, lấy ranh giới văn bản rồi dùng preTranslatepostTranslate để thay đổi ma trận để dịch văn bản sao cho 0,0 là tâm xoay mà không thay đổi vị trí vẽ văn bản trên màn hình.

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();
Văn bản chuyển màu động có màu đỏ và màu xanh lục xoay
Văn bản chuyển màu động có màu đỏ và màu xanh lục xoay vòng

Sử dụng RuntimeShader với Jetpack Compose

Việc sử dụng RuntimeShader thậm chí còn dễ dàng hơn nếu bạn kết xuất giao diện người dùng bằng Jetpack Compose. Bắt đầu với cùng một chương trình đổ bóng chuyển màu của trước:

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

Bạn có thể áp dụng chương trình đổ bóng đó cho ShaderBrush. Bạn sau đó sử dụng ShaderBrush làm tham số cho các lệnh vẽ trong Phạm vi vẽ của 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)
}
Vòng tròn chuyển màu trong AGSL Compose
Vòng tròn chuyển màu đỏ và xanh lục

Sử dụng RuntimeShader với RenderEffect

Bạn có thể sử dụng RenderEffect để áp dụng một RuntimeShader đến cha mẹ View tất cả chế độ xem của trẻ. Việc này sẽ tốn kém hơn so với việc vẽ một View tuỳ chỉnh. nhưng công cụ này giúp bạn dễ dàng tạo hiệu ứng kết hợp những gì sẽ có ban đầu được vẽ bằng createRuntimeShaderEffect.

Kotlin

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

Java

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

Tham số thứ hai là tên của đồng nhất chương trình đổ bóng mà bạn có thể eval bằng tham số toạ độ (chẳng hạn như thông số được truyền vào fragCoord) để lấy màu gốc của RenderNode (Chế độ xem và chế độ xem con chế độ xem), cho phép bạn thực hiện tất cả các loại hiệu ứng.

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Nút kết hợp lưới
Nút AGSL pha trộn trên nút

Hiệu ứng lưới kết hợp trên một nút, nhưng bên dưới một nút hành động nổi (vì nó thuộc một hệ phân cấp View khác).