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); } }
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 iTime
và iDuration
. 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()); } });
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);
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 preTranslate
và postTranslate
để 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();
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)
}
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
và 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);
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).