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
.
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" +
"}";
L'étape suivante consiste à créer un RuntimeShader
initialisé avec votre chaîne de nuanceur. Le nuanceur est également compilé.
val fixedColorShader = RuntimeShader(COLOR_SHADER_SRC)
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
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
}
}
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:
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" +
"}";
Appelez ensuite setColorUniform
à partir de votre View
personnalisé pour transmettre la couleur souhaitée.
dans le nuanceur AGSL.
fixedColorShader.setColorUniform("iColor", Color.GREEN )
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:
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" +
"}";
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
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);
}
}

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é.
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"+
"}";
À partir du code source de la vue personnalisée,
ValueAnimator
met à jour le
Uniforme 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());
}
});

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
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);

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
.
// 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);
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.
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();

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)
}

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
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(myShader, "background"))
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);

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).