En esta página, se describen los conceptos básicos de AGSL y las diferentes formas de usar AGSL en tu dispositivo Android .
Un sombreador AGSL simple
Se llama al código del sombreador para cada píxel dibujado y se muestra el color del píxel
con el que se debe pintar. Un sombreador extremadamente simple es aquel que siempre devuelve
un solo color; este ejemplo usa rojo. El sombreador se define dentro de un 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" + "}";
El siguiente paso es crear un RuntimeShader
inicializado con la cadena del sombreador. Esto también compila el sombreador.
Kotlin
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
Java
RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);
RuntimeShader
se puede usar en cualquier lugar que pueda usar un sombreador estándar de Android. Como
Por ejemplo, puedes usarlo para dibujar en un View
personalizado con un
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 } }
Esto dibuja un View
rojo. Puedes usar un uniform
para pasar un parámetro de color a
el sombreador que se dibujará. Primero, agrega el color uniform
al sombreador:
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" + "}";
Luego, llama a setColorUniform
desde tu View
personalizado para pasar el color deseado.
al sombreador AGSL.
Kotlin
fixedColorShader.setColorUniform("iColor", Color.GREEN )
Java
fixedColorShader.setColorUniform("iColor", Color.GREEN );
Ahora, obtienes un elemento View
verde; el color View
se controla con un
del código de tu View
personalizado en lugar de estar incorporado en el
sombreador.
En su lugar, puedes crear un efecto de gradiente de color. Primero deberás cambiar
el sombreador para aceptar la resolución View
como entrada:
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" + "}";
Cómo dibujar el gradiente
Este sombreador hace algo un poco elaborado. Para cada píxel, crea un float2
.
vector que contiene las coordenadas x e y divididas por la resolución, que
creará un valor entre cero y uno. Luego, usa ese vector ajustado para
construir los componentes rojo y verde del color de retorno.
Para pasar la resolución de View
a un sombreador uniform
de AGSL, llama a
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); } }
Cómo animar el sombreador
Puedes usar una técnica similar para animar el sombreador modificándolo para recibir uniformes iTime
y iDuration
. El sombreador usará estos valores para crear un
onda triangular para los colores, lo que hace que alternan entre sus valores de gradiente.
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"+ "}";
Desde el código fuente de la vista personalizada, se crea
ValueAnimator
actualiza la
iTime
uniforme.
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()); } });
Pintar objetos complejos
No es necesario que dibujes el sombreador para llenar el fondo. puede ser
usarse en cualquier lugar que acepte una
Paint
, como
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);
Transformaciones de sombreado y lienzo
Puedes aplicar transformaciones Canvas
adicionales al texto sombreado, como el siguiente ejemplo:
y la rotación de claves. En ValueAnimator
, puedes actualizar una matriz para rotaciones 3D.
con la función
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);
Como quieres rotar el texto desde el eje central en lugar de hacerlo desde la esquina,
obtén los límites del texto y, luego, usa preTranslate
y postTranslate
para modificar el
para traducir el texto de modo que 0,0 sea el centro de la rotación sin
cambiar la posición en la que se dibuja el texto en la pantalla.
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();
Cómo usar RuntimeShader con Jetpack Compose
Es aún más fácil usar RuntimeShader
si renderizas tu IU con
Jetpack Compose Comenzando con el mismo sombreador de gradientes de
antes del:
private const val COLOR_SHADER_SRC =
"""uniform float2 iResolution;
half4 main(float2 fragCoord) {
float2 scaled = fragCoord/iResolution.xy;
return half4(scaled, 0, 1);
}"""
Puedes aplicar ese sombreador a un
ShaderBrush
Tú
Luego, usa ShaderBrush
como parámetro para los comandos de dibujo dentro de tu
Alcance de dibujo de 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)
}
.
Cómo usar RuntimeShader con RenderEffect
Puedes usar
RenderEffect
para aplicar un
RuntimeShader
a un elemento superior View
y todas las vistas secundarias. Esto es más costoso que dibujar un View
personalizado. pero
te permite crear fácilmente un efecto que incorpore lo que habría
originalmente se dibujó con
createRuntimeShaderEffect
Kotlin
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
Java
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"));
El segundo parámetro es el nombre de un uniforme de sombreador que puedes eval
con un
parámetro de coordenadas (como el que se pasó en fragCoord) para obtener el color original
de los
RenderNode
(la vista y su elemento secundario
vistas), lo que te permite realizar todo tipo de efectos.
uniform shader background; // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
.
Un efecto de cuadrícula sobre un botón, pero debajo de un botón de acción flotante
(ya que está en una jerarquía de View
diferente).