На этой странице описаны основы 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, разделенные на разрешение, что создает значение от нуля до единицы. Затем он использует этот масштабированный вектор для построения красного и зеленого компонентов возвращаемого цвета.
Вы передаете разрешение View
в uniform
шейдера AGSL, вызывая setFloatUniform
.
Котлин
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
Использовать RuntimeShader
еще проще, если вы визуализируете свой пользовательский интерфейс с помощью Jetpack Compose . Начнем с того же шейдера градиента, что и раньше:
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"));
Второй параметр — это имя формы шейдера, которую вы можете eval
с помощью параметра координат (например, переданного в fragCoord), чтобы получить исходный цвет RenderNode
(представление и его дочерние представления), что позволяет вам выполнять все виды операций. эффекты.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
Эффект сетки, смешанный с кнопкой, но под кнопкой плавающего действия (поскольку она находится в другой иерархии View
).
Контент и образцы кода на этой странице предоставлены по лицензиям. Java и OpenJDK – это зарегистрированные товарные знаки корпорации Oracle и ее аффилированных лиц.
Последнее обновление: 2024-11-13 UTC.