Utiliser AGSL dans votre application Android

Cette page présente les principes de base d'AGSL et les différentes façons de l'utiliser dans votre application Android l'application.

Un nuanceur AGSL simple

Le code du nuanceur est appelé pour chaque pixel dessiné et renvoie la couleur du pixel. à peindre. Un nuanceur extrêmement simple renvoie toujours une seule couleur ; cet exemple utilise le rouge. Le nuanceur est défini à l'intérieur d'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" +
   "}";

L'étape suivante consiste à créer un RuntimeShader initialisé avec votre chaîne de nuanceur. Le nuanceur est également compilé.

Kotlin

val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)

Java

RuntimeShader fixedColorShader = new RuntimeShader(COLOR_SHADER_SRC);

Votre RuntimeShader peut être utilisé partout où un nuanceur Android standard le peut. En tant que Par exemple, vous pouvez l'utiliser pour dessiner dans un View personnalisé à l'aide d'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
   }
}

Un View rouge est alors dessiné. Vous pouvez utiliser un uniform pour transmettre un paramètre de couleur à le nuanceur à dessiner. Tout d'abord, ajoutez la couleur uniform au nuanceur:

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

Appelez ensuite setColorUniform à partir de votre View personnalisé pour transmettre la couleur souhaitée. dans le nuanceur AGSL.

Kotlin

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

Java

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

Vous obtenez maintenant une View verte. la couleur View est contrôlée à l'aide d'un du code de votre View personnalisé au lieu d'être intégré dans le nuanceur.

À la place, vous pouvez créer un effet de dégradé de couleurs. Vous devez d'abord modifier le nuanceur d'accepter la résolution View comme entrée:

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

Dessiner le dégradé

Ce nuanceur fait quelque chose d'un peu sophistiqué. Pour chaque pixel, un élément float2 est créé. vecteur contenant les coordonnées x et y divisées par la résolution, ce qui crée une valeur comprise entre 0 et 1. Il utilise ensuite ce vecteur mis à l'échelle pour construire les composants rouge et vert de la couleur renvoyée.

Vous transmettez la résolution de View à un nuanceur AGSL uniform en appelant 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);
   }
}
<ph type="x-smartling-placeholder">
</ph> Dégradé rouge et vert
Dégradé rouge et vert
.

Animer le nuanceur

Vous pouvez utiliser une technique similaire pour animer le nuanceur en le modifiant pour recevoir des variables uniformes iTime et iDuration. Le nuanceur utilise ces valeurs pour créer une onde triangulaire pour les couleurs, ce qui les fait basculer entre leurs valeurs de dégradé.

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

À partir du code source de la vue personnalisée, ValueAnimator met à jour le Uniforme 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());
   }
});
<ph type="x-smartling-placeholder">
</ph> Dégradé animé rouge et vert
Dégradé animé rouge et vert
.

Peindre des objets complexes

Il n'est pas nécessaire de dessiner le nuanceur pour remplir l'arrière-plan. il peut s'agir utilisé dans tout endroit acceptant un objet Paint, tel que 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);
<ph type="x-smartling-placeholder">
</ph> Texte en dégradé animé rouge et vert
Texte en dégradé animé rouge et vert
.

Transformations Shading et Canvas

Vous pouvez appliquer des transformations Canvas supplémentaires à votre texte ombré, comme la rotation des clés. Dans ValueAnimator, vous pouvez mettre à jour une matrice pour les rotations 3D à l'aide de la fonction 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);

Puisque vous souhaitez faire pivoter le texte à partir de l'axe central plutôt que de l'angle, obtenir les limites du texte, puis utiliser preTranslate et postTranslate pour modifier la pour traduire le texte de sorte que 0,0 soit le centre de la rotation sans modifier la position du texte à l'écran.

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();
<ph type="x-smartling-placeholder">
</ph> Texte en dégradé animé rotatif rouge et vert
Texte en dégradé animé rotatif rouge et vert
.

Utiliser RuntimeShader avec Jetpack Compose

Il est encore plus facile d'utiliser RuntimeShader si vous affichez votre UI à l'aide de Jetpack Compose. En partant du même nuanceur de dégradé before:

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

Vous pouvez appliquer ce nuanceur ShaderBrush Toi Utilisez ensuite ShaderBrush comme paramètre des commandes de dessin dans votre Champ d'application de dessin 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)
}
<ph type="x-smartling-placeholder">
</ph> Cercle en dégradé AGSL Compose
Cercle en dégradé rouge et vert

Utiliser RuntimeShader avec RenderEffect

Vous pouvez utiliser RenderEffect pour appliquer un RuntimeShader à un parent View et toutes les vues enfants. Cette opération est plus coûteuse que de dessiner un View personnalisé. mais elle vous permet de créer facilement un effet qui intègre ce qui aurait été a été dessinée à l'origine à l'aide createRuntimeShaderEffect

Kotlin

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

Java

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

Le deuxième paramètre est le nom d'un nuanceur uniforme que vous pouvez eval avec une (comme le paramètre transmis dans fragCoord) pour obtenir la couleur d'origine. des RenderNode (la vue et son enfant des vues), ce qui vous permet d'effectuer toutes sortes d'effets.

uniform shader background;       // Root node of View tree to be altered
return mix(returnColor, background.eval(fragCoord), 0.5);
<ph type="x-smartling-placeholder">
</ph> Bouton &quot;Grille fusionnée&quot;
Bouton Grille AGSL fusionnée

Effet de grille mélangé sur un bouton, mais sous un bouton d'action flottant (puisqu'il se trouve dans une hiérarchie View différente).